2

I am currently porting my DCF77 library (you may find the source code at GitHub) from Arduino (AVR based) to Arduino Due (ARM Cortex M3).

The library requires precise 1ms timing. An obvious candidate is the use of the systicks. Conveneniently the Arduino Due is already setup for systicks with 1 kHz.

However my (AVR) DCF77 library is capable to tune the timing once it locks to DCF77. This is done by manipulating the timer reload values like so

void isr_handler() {
    cumulated_phase_deviation += adjust_pp16m;
    // 1 / 250 / 64000 = 1 / 16 000 000
    if (cumulated_phase_deviation >= 64000) {
        cumulated_phase_deviation -= 64000;
        // cumulated drift exceeds 1 timer step (4 microseconds)
        // drop one timer step to realign
        OCR2A = 248;
    } else if (cumulated_phase_deviation <= -64000) {
        // cumulated drift exceeds 1 timer step (4 microseconds)
        // insert one timer step to realign
        cumulated_phase_deviation += 64000;
        OCR2A = 250;
    } else {
        // 249 + 1 == 250 == 250 000 / 1000 =  (16 000 000 / 64) / 1000
        OCR2A = 249;
    }

    DCF77_Clock_Controller::process_1_kHz_tick_data(the_input_provider());
}

I want to port this to the ARM processor. In the ARM information center I found the following documentation.

Configuring SysTick

...

To configure the SysTick you need to load the SysTick Reload Value register with the interval required between SysTick events. The timer interrupt or COUNTFLAG bit (in the SysTick Control and Status register) is activated on the transition from 1 to 0, therefore it activates every n+1 clock ticks. If a period of 100 is required 99 should be written to the SysTick Reload Value register. The SysTick Reload Value register supports values between 1 and 0x00FFFFFF.

If you want to use the SysTick to generate an event at a timed interval, for example 1ms, you can use the SysTick Calibration Value Register to scale your value for the Reload register. The SysTick Calibration Value Register is a read-only register that contains the number of pulses for a period of 10ms, in the TENMS field (bits 0 to 23). This register also has a SKEW bit (30) that is used to indicate that the calibration for 10ms in the TENMS section is not exactly 10ms due to small variations in clock frequency. Bit 31 is used to indicate if the reference clock is provided.

...

Unfortunately I did not find anything on how SysTick->LOAD and SysTick->CALIB are connected. That is: if I want to throttle or accelerate systicks, do I need to manipulate the LOAD or the CALIB value? And which values do I need to put into these registers?

Searching the internet did not bring up any better hints. Maybe I am searching at the wrong places. Is there anywhere a more detailed reference for these questions? Or maybe even some good examples?

Udo Klein
  • 6,784
  • 1
  • 36
  • 61

2 Answers2

2

Comparing the AtMega328 datasheet with the Cortex-M3 TRM, the standout point is that the timers work opposite ways round: on the AVR, you're loading a value into OCR2A and waiting for the timer in TCNT2 to count up to it, whereas on the M3 you load the delay value into SYST_RVR, then the system will count down from this value to 0 in SYST_CVR.

The big difference for calibration is going to be because the comparison value is fixed at 0 and you can only adjust the reload value, you might have more latency compared to adjusting the comparison value directly (assuming the counter reload happens at the same time the interrupt is generated).

The read-only value in SYST_CALIB (if indeed it even exists, being implementation-defined and optional), is merely for relating SYSTICK ticks to actual wallclock time - when first initialising the timer, you need to know the tick frequency in order to pick an appropriate reload value for your desired period, so having a register field that says "this many reference clock ticks happen in 10ms (possibly)" offers some possibility of calculating that at runtime in a portable fashion, rather than having to hard-code a value that might need changing for different devices.

In this case, however, not only does having an even-more-accurate external clock to synchronise against makes this less important, but crucially, the firmware has already configured the timer for you. Thus you can assume that whatever value is in SYST_RVR represents close-enough-to-1KHz, and work from there - in fact to simply fine-tune the 1KHz period you don't even need to know what the actual value is, just do SysTick->LOAD++ or SysTick->LOAD-- if the error gets too big in either direction.


Delving a bit deeper, the SAM3X datasheet shows that for the particular M3 implementation in that SoC, SYSTICK has a 10.5 MHz reference clock, therefore the SYST_CALIB register should give a value of 105000 ticks for 10ms. Except it doesn't, because apparently Atmel thought it would be really clever to make the unambiguously-named TENMS field give the tick count for 1ms, 10500, instead. Wonderful.

Notlikethat
  • 20,095
  • 3
  • 40
  • 77
  • Arduino sets the systicks such that they will run at 1 kHz. However my library needs better precision. Thus I need to tune it better. Hence I need to modify the values accordingly. Latency is not an issue. What keeps bogging me is the meaning of "The read-only value in SYST_CALIB (if indeed it even exists, being implementation-defined and optional), is merely for relating SYSTICK ticks to actual wallclock time -" How exactly does this mechanism work? Or should I just ignore the calibration value and directly adjust the reload value? But then what is the calibration value good for? – Udo Klein Jan 11 '15 at 15:15
  • So basically SYST_CALIB is not relevant for RELOAD. It is only relevant for other pieces of the software --> I may ignore it. Excellent. Your comment also explains why I did not get it. I noticed the same issue (10 ms vs 1 ms) in the datasheets but I thought it was my lack of understanding. – Udo Klein Jan 11 '15 at 17:14
  • According to what I found out now (see my answer below) SysTicks seems to have a 84 MHz clock. Accordingly I would expect that SYST_CALIB (which I ignore anyway) should be filled with 84000 or 8400 depending on how you interpret the documentation - correct? – Udo Klein Jan 13 '15 at 20:08
1

Just for the reason that others do not have to dig around like had to do - here is what I found out in addition.

In arduino-1.5.8/hardware/arduino/sam/system/CMSIS/CMSIS/Include/core_cm*.h there is code to manipulate SysTick. In particular in core_cm3.h there is a function

static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
  if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            /* Reload value impossible */

  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;      /* set reload register */
  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  /* set Priority for Cortex-M0 System Interrupts */
  SysTick->VAL   = 0;                                          /* Load the SysTick Counter Value */
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
                   SysTick_CTRL_TICKINT_Msk   |
                   SysTick_CTRL_ENABLE_Msk;                    /* Enable SysTick IRQ and SysTick Timer */
  return (0);                                                  /* Function successful */
}

Then in arduino-1.5.8/hardware/arduino/sam/variants/arduino_due_x/variant.cpp in function init there is

  // Set Systick to 1ms interval, common to all SAM3 variants
  if (SysTick_Config(SystemCoreClock / 1000))
  {
    // Capture error
    while (true);
  }

Since SystemCoreClock evaluates to 84000000 it follows that this compiles like SysTick_Config(84000). I verified against a DCF77 module that SysTick_Config(84001) will slow down SysTicks while SysTick_Config(83999) will speed it up.

Udo Klein
  • 6,784
  • 1
  • 36
  • 61
  • Good find - I had a bit of a look, but GitHub's awful search made me give up. Presumably `SysTick_CTRL_CLKSOURCE_Msk` is 0x04, which will set the CLKSOURCE bit to drive SysTick off the CPU clock rather than the reference clock (although in this case that's just CPU clock/8 anyway). Note that this function resets the current count, which could cause unnecessary jitter, and does a bunch of work that's really only for first-time initialisation - personally I'd just modify `SysTick->LOAD` directly where needed. – Notlikethat Jan 13 '15 at 22:57
  • Thanks for the hint. Although I had it directly before my eyes I missed this point. So I will then modify SysTick->Load directly. – Udo Klein Jan 14 '15 at 14:37