Arduino Due What is the MCK (master clock) precision ?

Hi,

I think about buying an Arduino Due for an inertial motorbike power bench. It's in fact a big roll (around 500 kg) that a bike accelerates... and measuring its acceleration, since we know its inertia, we can compute the power applied by the wheel of the motorbike.

I would need to know how precise is the Master Clock (MCK) of the Due, in ppm or % ?
I would need a precision of at least 0.1 % or better 0.01% (and to be sure of this precision)

I thank you a lot for your help,

Ulysse

I also have a second question, I wanted to be sure of the following thing:

  • with an Arduino Due it's not possible to use an external clock input for timer, isn't it ?

The new version of Arduino DUE has an external crystal, unlike the previous ones which have or not the external crystal. This external crystal is more precise than the internal RC oscillator, however even the external crystal is not as stable as a temperature compensated oscillator.

See section PMC of Sam3x datasheet for uc capability to use an external clock input.

Hi,

Thank's for your answer. It seems that when using peripheral clock for timers, this external clock has to have a 2.5 lower freqeuncy than the peripheral one.
Do I say something wrong by saying : epriphal clock can be set to main clock (Figure 28-2. page 527) with:

  • CSS = MAINCK
  • PRES = 1

So could it be correct to plug a TCX0 on XC0 pin to increment Timer0 with a 1/MCK period ? (in fact I would need a lower period, around 1/1MHz)

I thank you again for your precious help !

Sorry, I forgot the link (Figure 28-2. page 527 of this datasheet) :
http://www.atmel.com/Images/Atmel-11057-32-bit-Cortex-M3-Microcontroller-SAM3X-SAM3A_Datasheet.pdf

It seems that you would need a Timer Capture (See section 36.6.7 page 863 ).

You will find some examples of timer capture codes in the DUE sub-section of this forum.

Hey again,

I found the Due section but isn't it possible to make a research in this to find subjects speaking of Timer Capture ? I am quite new to the forum and surprised not to find this possibility in fact

Here is an example of a Timer Capture to detect frequency and duty cycle of a PWM signal:

// a jumper needs to be installed between pin 7 (PWM output) and pin A7 (TC0 channel 1 TIOA pin)

volatile uint32_t CaptureCountA, CaptureCountB, Period, Duty, TimerCount;
volatile boolean CaptureFlag;


void setup() {
  Serial.begin(250000);                                   // initilize serial port to 250000 baud

  analogWriteResolution(12); // From 0 to 2exp12  - 1 = 4095
  analogWrite(7, 1024);                                   // Duty cycle is 25% with 12 bits resolution

  PMC->PMC_PCER0 |= PMC_PCER0_PID28;                      // Timer Counter 0 channel 1 IS TC1

  TC0->TC_CHANNEL[1].TC_CCR = TC_CCR_CLKDIS ;            // disable internal clocking while setup regs

  TC0->TC_CHANNEL[1].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 // capture mode, MCK/2, clk on rising edge
                              | TC_CMR_ABETRG              // TIOA is used as the external trigger
                              | TC_CMR_LDRA_RISING         // load RA on rising edge of trigger input
                              | TC_CMR_LDRB_FALLING;       // load RB on falling edge of trigger input

  TC0->TC_CHANNEL[1].TC_IER |= TC_IER_LDRAS | TC_IER_LDRBS; // Trigger interruption on Load RA and load RB
  TC0->TC_CHANNEL[1].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger and enable

  //NVIC_DisableIRQ(TC1_IRQn);
  //NVIC_ClearPendingIRQ(TC1_IRQn);
  //NVIC_SetPriority(TC1_IRQn, 0);                      // Give TC1 interrupt the highest urgency
  NVIC_EnableIRQ(TC1_IRQn);                           // Enable TC1 interrupts

  Serial.println("Timer Capture begins");
}

void loop() {
  if (CaptureFlag) {
    CaptureFlag = 0;
 
  }

}

void TC1_Handler() {
 
  static uint32_t _CaptureCountA = 0;
  //Registers A and B (RA and RB) are used as capture registers. They are loaded with
  //the counter value TC_CV when a programmable event occurs on the signal TIOA.
  // TimerCount = (uint32_t) TC0->TC_CHANNEL[1].TC_CV;            // save the timer counter register, for testing

  uint32_t status = TC0->TC_CHANNEL[1].TC_SR;       // Read & Save satus register -->Clear status register

  // If TC_SR_LOVRSRA is set, RA or RB have been loaded at least twice without any read
  // of the corresponding register since the last read of the Status Register,
  // We are losing some values,trigger of TC_Handler is not fast enough !!
  //if (status & TC_SR_LOVRS) abort();

  if (status & TC_SR_LDRAS) {  // If ISR is fired by LDRAS then ....
    CaptureCountA = (uint32_t) TC0->TC_CHANNEL[1].TC_RA;        // get data from capture register A for TC0 channel 1
  }
  else { /*if ((status & TC_SR_LDRBS) == TC_SR_LDRBS)*/  // If ISR is fired by LDRBS then ....
    CaptureCountB = (uint32_t) TC0->TC_CHANNEL[1].TC_RB;         // get data from caputre register B for TC0 channel 1

    CaptureFlag = 1;                      // set flag indicating a new capture value is present
  }

  Period = CaptureCountA - _CaptureCountA;
  Duty = CaptureCountB - _CaptureCountA;
  _CaptureCountA = CaptureCountA;

}

So since my initial wish is to use an external frequence to get more precise time measurements, what I could do is to measure the number of rising edges (I could use TC_CV) of a "perfect" frequence coming from an external device (for example a TCX0) to measure the elapsed time between two instants, isn't it ?

If what I say is true, I just learnt the interest of the counters =)

P.S. In your example code, each rising edge what kind of value is retained by CaptureCountA (timer 0 value or the number of counts) ? If it was the number of counts, Period=CaptureCountA-CaptureCountA variable should always be 1 I think since it is calculated each rising edge. I think there is still something I nee to understand in this example.

After reading the datasheet for an external clock selection in the TC datasheet section:

In TC_BMR, set e.g. TC0XC0S to 0 if you are using Timer Counter 0 to select an external clock input (TCLK0 = pin 22 = PB26 ),

In in TC_CMR, set TCCLKS = 5 = TC_CMR_TCCLKS_XC0 to select XC0 as the clock selection.

Note that the external clock should be 2.5 lower than the TC peripheral clock (42 MHz)

In capture mode, you should time very precisely edges rising or falling if your input clock IS very precise.

All correct for me, thank you a lot =)

Bisiaux:
Hi,

I think about buying an Arduino Due for an inertial motorbike power bench. It's in fact a big roll (around 500 kg) that a bike accelerates... and measuring its acceleration, since we know its inertia, we can compute the power applied by the wheel of the motorbike.

I would need to know how precise is the Master Clock (MCK) of the Due, in ppm or % ?
I would need a precision of at least 0.1 % or better 0.01% (and to be sure of this precision)

I thank you a lot for your help,

Ulysse

The original Due derives its 84MHz system clock from a 12MHz quartz crystal multiplied up by a PLL,
so the accuracy depends on that crystal's specs. The phase noise will depend on the on-chip PLL,
should timing jitter matter to you that much.

Typical series resonant crystals used for logic clocks are 50, 30, 20 or 10ppm rated.

Hi again,

MarkT I understand your point of view but I always prefer to know exactly the quality of my hardware, for this reason the TCX0 corresponds to mu need.

ard_newbie, I coded the following, but I still have a problem with the registers parametrization, one line does not compile with the message: "'struct TcChannel' has no member named 'TC_BMR'"

Code:

volatile boolean CaptureFlag;
int counterXC0=0;

void setup() {
  Serial.begin(250000);                           

  PMC->PMC_PCER0 |= PMC_PCER0_PID28;                    // Timer Counter TC0 channel 1 IS TC1

  TC0->TC_CHANNEL[1].TC_CCR = TC_CCR_CLKDIS;            // counter clock disabled    
  TC0->TC_CHANNEL[1].TC_CMR = TC_CMR_TCCLKS_XC0         // XC0 clock selected 
                            | TC_CMR_ETRGEDG_RISING;    // external trigger interrupts on rising edge
  TC0->TC_CHANNEL[1].TC_IER = TC_IER_ETRGS;             // external trigger interrupts enabled
  TC0->TC_CHANNEL[1].TC_BMR = TC_BMR_TC0XC0S_TCLK0;   // signal connected to XC0 (THIS LINE DOES NOT COMPILE)
  TC0->TC_CHANNEL[1].TC_CCR = TC_CCR_CLKEN;             // counter clock enabled    
  NVIC_EnableIRQ(TC1_IRQn);                             // TC1 interrupts enabled
}

void loop() {
  if (CaptureFlag) {
    Serial.println(counterXC0);
    CaptureFlag = 0;
  }
}

void TC1_Handler() {
  counterXC0++;
  CaptureFlag=1;
}

:slight_smile: Thank you a lot for your answers !!

TC_BMR is for all Timer Counter x channels, i.e.:

TC0->TC_BMR = TC_BMR_TC0XC0S_TCLK0; // Should compile !!

Here is the most basic code for a counter implementation on an Arduino Due SAM3X8E, I think it could help =)

Thank you all !!

void setup() {
  Serial.begin(250000);                            
  PMC->PMC_PCER0 = PMC_PCER0_PID27;                // interrupt line TC0 selected                
  TC0->TC_CHANNEL[0].TC_CCR = TC_CCR_CLKDIS ;      // counter clock disabled for initialization
  TC0->TC_CHANNEL[0].TC_CMR = TC_CMR_TCCLKS_XC0;   // counter clock selected : XC0
  TC0->TC_BMR = TC_BMR_TC0XC0S_TCLK0;              // signal connected to XC0: TCLK0=PB26=Arduino_Due_Digital_Pin_22 https://www.arduino.cc/en/Hacking/PinMappingSAM3X
  TC0->TC_CHANNEL[0].TC_CCR = TC_CCR_SWTRG         // sowftware trigger: reset counter, start clock
                            | TC_CCR_CLKEN;        // counter clock enabled
}

void loop() {
  delay(500);
  Serial.println(TC0->TC_CHANNEL[0].TC_CV);
}

Hey guys,

I still have an important question. At this time, I am using 2 external interrupts on my Due. The required treament time is less than 5us for sure, so it is not a big problem if the second interrupt occurs when the first ISR is already running, it can wait 5us.
BUT what happened if one of my external interrupts happens during Serial.print("blabla") ? According to the attached table, I deduce that interrupts on external pins are prioritory, isn't it ?

Thank you,

 1  Reset
 2  External Interrupt Request 0  (pin D2)          (INT0_vect)
 3  External Interrupt Request 1  (pin D3)          (INT1_vect)
 4  Pin Change Interrupt Request 0 (pins D8 to D13) (PCINT0_vect)
 5  Pin Change Interrupt Request 1 (pins A0 to A5)  (PCINT1_vect)
 6  Pin Change Interrupt Request 2 (pins D0 to D7)  (PCINT2_vect)
 7  Watchdog Time-out Interrupt                     (WDT_vect)
 8  Timer/Counter2 Compare Match A                  (TIMER2_COMPA_vect)
 9  Timer/Counter2 Compare Match B                  (TIMER2_COMPB_vect)
10  Timer/Counter2 Overflow                         (TIMER2_OVF_vect)
11  Timer/Counter1 Capture Event                    (TIMER1_CAPT_vect)
12  Timer/Counter1 Compare Match A                  (TIMER1_COMPA_vect)
13  Timer/Counter1 Compare Match B                  (TIMER1_COMPB_vect)
14  Timer/Counter1 Overflow                         (TIMER1_OVF_vect)
15  Timer/Counter0 Compare Match A                  (TIMER0_COMPA_vect)
16  Timer/Counter0 Compare Match B                  (TIMER0_COMPB_vect)
17  Timer/Counter0 Overflow                         (TIMER0_OVF_vect)
18  SPI Serial Transfer Complete                    (SPI_STC_vect)
19  USART Rx Complete                               (USART_RX_vect)
20  USART, Data Register Empty                      (USART_UDRE_vect)
21  USART, Tx Complete                              (USART_TX_vect)
22  ADC Conversion Complete                         (ADC_vect)
23  EEPROM Ready                                    (EE_READY_vect)
24  Analog Comparator                               (ANALOG_COMP_vect)
25  2-wire Serial Interface  (I2C)                  (TWI_vect)
26  Store Program Memory Ready                      (SPM_READY_vect)