Go Down

Topic: Arduino Zero TCC Capture (Read 25417 times) previous topic - next topic

MartinL

#120
Mar 06, 2018, 11:01 pm Last Edit: Mar 06, 2018, 11:04 pm by MartinL
Hi Daniel,

You can do this by setting the TC4 (plus TC3) to match frquency (MFRQ) mode.

In MFRQ mode the timer period can be set by the CC0 (counter compare 0) register, instead of the timer's maximum value:

Code: [Select]
REG_TC4_COUNT32_CC0 = 1919;                     // Set the TC4 CC0 register as the TOP value in match frequency mode
while (TC4->COUNT32.STATUS.bit.SYNCBUSY);       // Wait for synchronization

The timer will return to 0 when it reaches the value in the CC0 register.

The timer is set to MFRQ mode in the CTRLA register:

Code: [Select]
REG_TC4_CTRLA |= TC_CTRLA_PRESCALER_DIV1 |      // Set prescaler to 1, 48MHz/1 = 48MHz
                 TC_CTRLA_WAVEGEN_MFRQ |        // Put the timer TC4 into match frequency (MFRQ) mode                   
                 TC_CTRLA_MODE_COUNT32 |        // Set the TC4 timer to 32-bit mode in conjuction with timer TC3
                 TC_CTRLA_ENABLE;               // Enable TC4
while (TC4->COUNT32.STATUS.bit.SYNCBUSY);       // Wait for synchronization

Kind regards,
Martin


parnal

Hii Martin,

This is my code for clock chaining in Due and I wish to do the same on Zero.

Code: [Select]

uint32_t *TC_CV = (uint32_t *)(0x40084010); //for timer 1 channel 0 (see datasheet p.888)
uint32_t startTime;
uint32_t wantedTime;
uint32_t endTime;
uint32_t addup = 0;


void setup() {

pmc_set_writeprotect(false);

/*************  Timer Counter 1 Channel 0 to generate PWM pulses thru MCK  ************/

PMC->PMC_PCER0 |= PMC_PCER0_PID30;                          //PID30 from the peripheral identifiers for Timer 1 channel 0, the command enables PWM
TC1->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
                                           
// Frequency VARIANT_MCK is 84MHz, counter clocked with MCK/2 = 42MHz:
TC1->TC_CHANNEL[0].TC_RC =  VARIANT_MCK / 1000000 / 2;      //  count to 42 --> 1MHz on TIOA0
TC1->TC_CHANNEL[0].TC_RA = VARIANT_MCK / 1000000 / 4;       //  half value 21 (50% duty cycle on TIOA0)


TC1->TC_CHANNEL[0].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN;    // Software trigger TC0 counter and enable CLOCK


/*************  Timer Counter 1 Channel 1 to generate PWM pulses thru TIOA0  ************/

PMC->PMC_PCER0 |= PMC_PCER0_PID31;                        //PID31 from peripheral identifier from Timer 1 channel 1
TC1->TC_BMR = TC_BMR_TC1XC1S_TIOA0;                      // Timer Counter 1  XC1 is internally clocked by TIOA0
TC1->TC_CHANNEL[1].TC_CMR = TC_CMR_TCCLKS_XC1            // External clock XC1 (i.e. TIOA0) selected
                              | TC_CMR_WAVE                // Waveform mode
                              | TC_CMR_WAVSEL_UP_RC;        // UP mode with automatic trigger on RC Compare

TC1->TC_CHANNEL[1].TC_CV = 0;
TC1->TC_CHANNEL[1].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN;   // Software trigger TC1 counter and enable CLOCK


Serial.begin(250000);

}

void loop() {
 
    while (true){
    startTime = TC1->TC_CHANNEL[1].TC_CV; //*TC_CV
    //while(TC1->TC_CHANNEL[1].TC_CV == startTime)addup+=1;
    for(int i=0; i<5; i++);
    endTime=TC1->TC_CHANNEL[1].TC_CV;
   
    Serial.println(startTime);
    Serial.println(endTime);
    Serial.println(endTime-startTime);
    Serial.println(TC1->TC_CHANNEL[0].TC_RC);
 
    Serial.println("--");
    delay(500);
    }
}


and I wish to do this  : "2) Do you wish the timers to free run and overflow at their maximum (2^32)?"


MartinL

Hi parnal,

Ok, so this is ard_newbie's Arduino Due timer chaining code from here: http://forum.arduino.cc/index.php?topic=531944.0.

If you tell me what this code does, I'll implement for you on the SAMD21.

Kind regards,
Martin

parnal

Hii Martin,

The program is:
1. Here I wanted to use Timer 1 which is having 3 channels (T1_CH0; T1_CH1; T1_CH2).
2. I initialised 2 channels channel 0 and 1.
3. I have set: channel 0 in waveform mode with  master clock (MCK/2 which is 96/2 for due)  with trigger on RC compare. After Timer 1  channel 0 is done counting it  generates a output TIOA0 and this is fed input to Timer 1 channel 1 .
4. Set Channel 1 with same parameters as channel 0 (waveform mode, rc compare ) then set the input as TIOA0 instead of master clock.
5. Then simply print the counter value. (check pg. 861 Clock chaining diagram from datasheet SAM3X / SAM3A Series )

MartinL

#124
Mar 07, 2018, 12:15 pm Last Edit: Mar 09, 2018, 11:49 am by MartinL
Hi parnal,

The following sets up the 16-bit TC3 timer at 48MHz and gets it to overflow every microsecond (1MHz). On each overflow the timer outputs an event that is received by the 32-bit TC4 (with TC5 as the slave). TC4 is set up to increment each time it receives an event, in other words it's clocked at 1MHz.

There appears to be an error in the SAMD21 datasheet. It says that TC4 is chained with TC3 for 32-bit mode, in reality however it's chained to TC5.

This code does the same as the Arduino Due TC timers:

Code: [Select]
// Setup TC3 to clock TC4/TC5 in 32-bit mode at 1MHz using the event system

uint32_t timeMicros;

void setup()
{
  SerialUSB.begin(115200);                  // Send data back on the Zero's native port
  while(!SerialUSB);                        // Wait for the SerialUSB port to be ready
 
  REG_PM_APBCMASK |= PM_APBCMASK_EVSYS;     // Switch on the event system peripheral
 
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |    // Divide the 48MHz system clock by 1 = 48MHz
                    GCLK_GENDIV_ID(5);      // Set division on Generic Clock Generator (GCLK) 5
  while (GCLK->STATUS.bit.SYNCBUSY);        // Wait for synchronization

  REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC |           // Set the duty cycle to 50/50 HIGH/LOW
                     GCLK_GENCTRL_GENEN |         // Enable GCLK 5
                     GCLK_GENCTRL_SRC_DFLL48M |   // Set the clock source to 48MHz
                     GCLK_GENCTRL_ID(5);          // Set clock source on GCLK 5
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization*/

  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable the generic clock...
                     GCLK_CLKCTRL_GEN_GCLK5 |     // ....on GCLK5
                     GCLK_CLKCTRL_ID_TCC2_TC3;    // Feed the GCLK5 to TCC2 and TC3
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable the generic clock...
                     GCLK_CLKCTRL_GEN_GCLK5 |     // ....on GCLK5
                     GCLK_CLKCTRL_ID_TC4_TC5;     // Feed the GCLK5 to TC4 and TC5
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  REG_EVSYS_USER = EVSYS_USER_CHANNEL(1) |                                // Attach the event user (receiver) to channel 0 (n + 1)
                   EVSYS_USER_USER(EVSYS_ID_USER_TC4_EVU);                // Set the event user (receiver) as timer TC4
 
  REG_EVSYS_CHANNEL = EVSYS_CHANNEL_EDGSEL_NO_EVT_OUTPUT |                // No event edge detection
                      EVSYS_CHANNEL_PATH_ASYNCHRONOUS |                   // Set event path as asynchronous
                      EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_TC3_OVF) |         // Set event generator (sender) as TC3 overflow
                      EVSYS_CHANNEL_CHANNEL(0);                           // Attach the generator (sender) to channel 0                               

  REG_TC4_EVCTRL |= TC_EVCTRL_TCEI |              // Enable asynchronous input events on the TC timer
                    TC_EVCTRL_EVACT_COUNT;        // Increment the TC timer each time an event is received

  REG_TC3_EVCTRL |= TC_EVCTRL_OVFEO;              // Enable an event output on overflow of the TC3 timer

  REG_TC3_COUNT16_CC0 = 47;                       // Set the TC3 CC0 register to overflow every 1us (1MHz)
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY);       // Wait for synchronization
 
  REG_TC4_CTRLA |= TC_CTRLA_PRESCALER_DIV1 |      // Set prescaler to 1, 48MHz/1 = 48MHz
                   TC_CTRLA_MODE_COUNT32 |        // Set the TC4 timer to 32-bit mode in conjuction with timer TC5
                   TC_CTRLA_ENABLE;               // Enable TC4
  while (TC4->COUNT32.STATUS.bit.SYNCBUSY);       // Wait for synchronization

  REG_TC4_READREQ = TC_READREQ_RCONT |            // Enable a continuous read request
                    TC_READREQ_ADDR(0x10);        // Offset of the 32-bit COUNT register
  while (TC4->COUNT32.STATUS.bit.SYNCBUSY);       // Wait for (read) synchronization

  REG_TC3_CTRLA |= TC_CTRLA_PRESCALER_DIV1 |      // Set prescaler to 1, 48MHz/1 = 48MHz
                   TC_CTRLA_WAVEGEN_MFRQ |        // Put the timer TC3 into match frequency (MFRQ) mode   
                   TC_CTRLA_ENABLE;               // Enable TC3
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY);       // Wait for synchronization

  // Test timer TC3
  //REG_TC3_READREQ = TC_READREQ_RCONT |            // Enable a continuous read request
  //                  TC_READREQ_ADDR(0x10);        // Offset of the 32-bit COUNT register
  //while (TC3->COUNT16.STATUS.bit.SYNCBUSY);       // Wait for (read) synchronization

  timeMicros = micros();
}

void loop()
{
  SerialUSB.print(REG_TC4_COUNT32_COUNT);       // Output the results
  SerialUSB.print(F("   "));
  SerialUSB.println(micros() - timeMicros);
 
  //SerialUSB.println(REG_TC3_COUNT32_COUNT);     // Test timer TC3
}

parnal

Hii Martin,

Works perfect. Thanks a lot  :)

DR49

Thanks Martin,

I understand your point using MFREQ.

But it would be simplest for me to stop / restart the TCCount when it is appropriate.

If it is more complex to do so, tell me how to calculate the elapsed time of the TC : For example what is the time for your 1919 example ?

Kind regards

Daniel

MartinL

#127
Mar 07, 2018, 04:50 pm Last Edit: Mar 07, 2018, 04:53 pm by MartinL
Hi Daniel

The TC timer is simply counting the number of input pulses. So the time elapsed depends on how fast you're clocking the input and if that input's regular or not. If there's no input, the counter will stop counting.

The match frequency mode just resets the TC timer back to 0 when it's COUNT register matches the value in the CC0 register.

Taking the 1919 value in the example with a regular 40kHz PWM input gives you:

Time elapsed = (1 / 40000) * (1919 + 1) = 48ms

Add 1 to the 1919 to take into account that we're counting from 0.

If you want to stop the timer in your code:

Code: [Select]
REG_TC4_CTRLBSET = TC_CTRLBCLR_CMD_STOP;
while (TC4->COUNT32.STATUS.bit.SYNCBUSY);

...and to restart...

Code: [Select]
REG_TC4_CTRLBSET = TC_CTRLBCLR_CMD_RETRIGGER;
while (TC4->COUNT32.STATUS.bit.SYNCBUSY);

Kind regards,
Martin

DR49

Great MartinL, in fact I realised this calculation afterwards !

The point is that I can get noisy frequencies and then TC elapsed time can be different from one bit to another.

So I prefer to control the start / stop.

I'll try this and give you my results back

Many thanks

Kind regards

Daniel

DR49

Hi MartinL

It's working PERFECTLY !!! (RTC, I2C, Native port, TCC, TC, millis etc...) !!

Using "restart timer" at the relevent place, it's OK !

Thank you very much again,

Kind Regards

Daniel


Go Up