DUE timer reset Problem

Hello Everyone,

I am trying to program the Arduino DUE timer modules. My operation is like this-

  1. Every 1 microsecond one timer channel is set to generate an interrupt.
  2. At every interrupt, the CPU reads a 32-bit port and store it.
  3. then Toggle an output and reset the timers internal counter register.

The problem is -

While the execution point moved to the ISR it's consuming some time to read port and toggle the output.
So my interrupt is not happening exactly after one microsecond but taking 1.14 us because it's taking that 0.14 microsecond to read and toggle ( as I was reseting the counter register value after the read and toggle) .

Now inside the ISR, the code is as below-

###########################################

void TC6_Handler()
{
TC_GetStatus(TC2, 0); //to get the status

//assign the input values to values[]
values[valueIndex]=PIOA->PIO_PDSR;//stroing the vales

//Toggling one output
PIOB->PIO_SODR|=(1<<12);
PIOB->PIO_CODR|=(1<<12);

//Updating value index
valueIndex++;
//Reset Counter 2 channel 0
TC2->TC_CHANNEL[0].TC_CCR|=TC_CCR_SWTRG;
}

######################################################

When I tried to move the reset line immediately after the "TC_GetStatus" function it's taking more time to finish executing the later codes, approx. 1.5us.

##Why this is happening?

I don't have any explanation for this. As my understanding goes the reset will cause the internal counter running while the port operation is occurring so the entire operation should happen within 1 us because at the start of ISR call the counter value is reset( throughout the read and toggle the timer is incrementing so it doesn't account for the time taken for read and toggle).

My timer initialization code is as below-

###########################################################
int TimerStart(Tc *tc, uint32_t channel, IRQn_Type irq, uint32_t freq)
{
pmc_set_writeprotect(false); //for 32 bit due process prior to configureing counter channel write protect bits has to be disabled
pmc_enable_periph_clk(irq); // enabling the ISR
TC_Configure(tc, channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC |
TC_CMR_TCCLKS_TIMER_CLOCK1); ///////Configuring the timer counter
uint32_t rc = VARIANT_MCK / 2 / freq; /////// calculating the overflow for rc or register c
TC_SetRA(tc, channel, 21000000); // 50% duty cycle square wave
TC_SetRC(tc, channel, rc);
//TC_Start(tc, channel);
tc->TC_CHANNEL[channel].TC_IER= TC_IER_CPCS | TC_IER_CPCS;
tc->TC_CHANNEL[channel].TC_IDR=~(TC_IER_CPCS | TC_IER_CPCS);
NVIC_EnableIRQ(irq);
}

################################################################

And timer start function -

"TimerStart(TC2, 0, TC6_IRQn, 1000000); /// Set timer(microsecond range )frequency here"

Does anybody have any idea about this?

Thank you for reading the long post:).

A few thoughts:

  • 1 us equals 84 clock cycles for a uc clocked at 84 MHz (the default DUE clock).
  • Entering inside an ISR takes ~ 8 clock cycles
  • Set and clear a pin takes ~ 4 clock cycles

If you program a timer couter to trigger an interrupt every 1 us to toggle a pin, the first time you toggle the pin, it will take 1 us + ~ 12 * 11.9 ns =~ 1.144 us. But the next time you toggle the pin, it will take only 1us !

The solution might be to select a smaller period for the first timestamp (~ (1 us - 144ns)) then inside the ISR, select 1 us period. AFAIk, the problem is that you are not supposed to update the timer frequency once started, unless you disable the clock, update frequency and enable the clock again. However, you can trigger interrupts as well with a PWM, and you can easily update the PWM frequency on the fly with PWM_CPRDUPD.

Here are 2 example sketches with a Timer Counter and a PWM:

void setup() {

  pinMode(LED_BUILTIN, OUTPUT);
  tc_setup();
}

void loop() {
}

void tc_setup() {

  PMC->PMC_PCER0 |= PMC_PCER0_PID29;                       // TC2 power ON : Timer Counter 0 channel 2 IS TC2
  TC0->TC_CHANNEL[2].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1   // MCK/2, clk on rising edge
                              | TC_CMR_WAVE                // Waveform mode
                              | TC_CMR_WAVSEL_UP_RC;       // UP mode with automatic trigger on RC Compare

  TC0->TC_CHANNEL[2].TC_RC = 42;                           // Frequency = (Mck/2)/TC_RC  Hz = 1 MHz
  TC0->TC_CHANNEL[2].TC_IER = TC_IER_CPCS;                 // Interrupt on RC compare match
  NVIC_EnableIRQ(TC2_IRQn);                                // Enable interrupts
  TC0->TC_CHANNEL[2].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger and enable

}

void TC2_Handler() {
  static uint32_t Count;
  
  TC0->TC_CHANNEL[2].TC_SR;         // Read and clear status register

  if (Count++ == 1000000) {
    Count = 0;
   
    PIOB->PIO_ODSR ^= PIO_ODSR_P27;
  }
}
void setup() {
  
  pinMode(LED_BUILTIN, OUTPUT);
  pwm_setup();
}

void loop() {
}

void pwm_setup() {
  PMC->PMC_PCER1 |= PMC_PCER1_PID36;                   // PWM power ON

  
  PWM->PWM_CLK = PWM_CLK_PREB(0) | PWM_CLK_DIVB(1);    // select Frequency : Mck = 84 MHz

  PWM->PWM_CH_NUM[0].PWM_CMR = PWM_CMR_CPRE_CLKB;      // The period is left aligned, clock source as CLKB on channel 0
  PWM->PWM_CH_NUM[0].PWM_CPRD = 84;                    // Set the PWM frequency 84 MHz/ CPRD = 1 MHz
 
  PWM->PWM_IER1 = PWM_IER1_CHID0;                      // Interrupt on counter event (CPRD compare match)
  NVIC_EnableIRQ(PWM_IRQn);                            // Enable interrupt

  PWM->PWM_ENA = PWM_ENA_CHID0;                        // Enable PWM channel 0
}
void PWM_Handler() {
  static uint32_t Count;
  
  PWM->PWM_ISR1;      // Read and Clear status register
  if (Count++ == 1000000) {
   
    PIOB->PIO_ODSR ^= PIO_ODSR_P27;
    Count = 0;
  }
  
  // update PWM frequency with  PWM->PWM_CH_NUM[0].PWM_CPRDUPD = ....;
}