Arduino DUE Timer Counter

Hi everyone!

I hope someone can help me.

I'm trying to do a simple pulse counter with an Arduino DUE, and because I need to be sure to have precise readings,I want to do it with one of the Timer Counter of the SAM.

I had a look to the datasheet, and after some readings, I've tried to do it in this way.

First of all, I've configured the PB26 pin on the B line, so:

REG_PIOB_WPMR = 0x50494F00;    //disable the protection on the registers
REG_PIOB_ABSR |= 0x04000000;
REG_PIOB_WPMR = 0x50494F00; // enable again the write-protection

Then, I've tried to configure the TC0 in this way:

REG_TC0_WPMR = 0x54494D00;
REG_TC0_CMR0 = 0x0405;
REG_TC0_CCR0 = 0x0001;
REG_TC0_BMR = 0x0;
REG_TC0_WPMR = 0x54494D01;

Then I've tried to input a 10Hz 3Vpp square wave on TCLK0, but REG_TC0_CV0 remains 0.
I can't understand where I'm wrong. It's the first time I'm trying to write directly the SAM3X8E registers.
I've read that I've to enable the clock in the PMC, but reading the datasheet I don't understand how (and also, I think it's needed for internal clocks). Also I've read that the PID27 interrupt must be enabled, maybe the problem is this?
Or maybe my approach is simply wrong?
Thanks for the replies.
Regards

You have to set a timer in Timer Capture Mode to receive the PWM inputs, count rising and falling edges, do the math to calculate frequency and duty cycle.

Depending on the frequency you are measuring, you can use an Timer Interrupt (correct well below 1 MHz), or by polling TC_SR of the timer that receives the PWM (up to 20 Mhz).

Here is an example sketch with an interrupt. One timer counter generates a PWM signal while the other Timer Counter, actually TC1, measure frequency and duty cycle of the PWM:

/*************************************************************************************************/
/*  a jumper needs to be installed between pin 2 (TIOA0) and pin A7 (TC0 channel 1 TIOA pin)     */
/*************************************************************************************************/

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

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

   /*************  Timer Counter 0 Channel 0 to generate PWM pulses thru TIOA0  ************/
  PMC->PMC_PCER0 |= PMC_PCER0_PID27;                      // Timer Counter 0 channel 0 IS TC0
  PIOB->PIO_PDR |= PIO_PDR_P25 | PIO_PDR_P27;
  PIOB->PIO_ABSR |= PIO_ABSR_P25 | PIO_ABSR_P27;

  TC0->TC_CHANNEL[0].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
                              | TC_CMR_ACPA_CLEAR          // Clear TIOA0 on RA compare match
                              | TC_CMR_ACPC_SET;           // Set TIOA0 on RC compare match
                             
                              
  TC0->TC_CHANNEL[0].TC_RC = 100;                          // Frequency of PWM pulses = MCK/2/TC_RC
  TC0->TC_CHANNEL[0].TC_RA = 50;                           // Duty cycle of PWM pulses = (TC_RA/TC_RC) * 100 %
  
  TC0->TC_CHANNEL[0].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger and enable

  /*************  Timer Counter 0 Channel 1 to capture PWM pulses thru TIOA1  ************/
  PMC->PMC_PCER0 |= PMC_PCER0_PID28;                       // Timer Counter 0 channel 1 IS TC1

  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_SetPriority(SysTick_IRQn, 15);                 // SysTick interrupt will not interrupt TC1 interrupt
  NVIC_EnableIRQ(TC1_IRQn);                           // Enable TC1 interrupts

}

void loop() {
  const uint32_t _F_CPU = F_CPU/2;
  static uint32_t counter;
  float Frequency = 1.00;
  float _Duty;

  if (counter > 10000) {

    if ( CaptureFlag == 1) {

      Frequency = _F_CPU / Period  ; //  (Mck/2 is TC1 clock) F in Hz
      _Duty = (Duty * 100.00) / Period;

      Serial.print(" F = "); Serial.print((uint32_t)Frequency); Serial.print(" Hz ");
      Serial.print (" ,  Duty = "); Serial.print((uint32_t)_Duty); Serial.println(" %");
      counter = 0;
      CaptureFlag = 0;

    }

  }
  counter++;
}

void TC1_Handler() {
 
  static uint32_t _CaptureCountA;

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


  //if (status & TC_SR_LOVRS) abort();  // We are loosing some edges

  if (status & TC_SR_LDRAS) {  // If ISR is triggered by LDRAS then ....
    CaptureCountA = (uint32_t) TC0->TC_CHANNEL[1].TC_RA;        // get data from capture register A for TC0 channel 1
    Period = CaptureCountA - _CaptureCountA;
    _CaptureCountA = CaptureCountA;
  }
  else { /*if ((status & TC_SR_LDRBS) == TC_SR_LDRBS)*/  // If ISR is triggered by LDRBS then ....
    CaptureCountB = (uint32_t) TC0->TC_CHANNEL[1].TC_RB;         // get data from caputre register B for TC0 channel 1
    Duty = CaptureCountB - _CaptureCountA;
    CaptureFlag = 1;                      // set flag indicating a new capture value is present
  }

}