Go Down

Topic: Arduino ZERO timer interrupts (Read 20375 times) previous topic - next topic

RRLAN

i have red the ATSAMD21 datasheet and some example codes for the arduino DUE. do you have any idea what is wrong with my approach?

/*
 * Blink__Interrupt_.c
 *
 * Created: 24-6-2015 13:15:52
 *  Author: Dell Pc
 */

#include "sam.h"

/**
 * \brief Application entry point.
 *
 * \return Unused (ANSI-C compatibility).
 */

int main(void)
{
    /* Initialize the SAM system */
    SystemInit();
   
   REG_PORT_DIRSET0 |= (1 << (17%32));//Pin 13 is now an output
   
   REG_PORT_OUTSET0 |=  (1 << (17%32));//Pin 13 == High
   
   /*setup timer:*/
   
   REG_PM_APBCMASK |= (1 << (8%32));//Enable CLK_TCC0_APB
   
   REG_TCC0_CTRLA |= (1 << (3%32));//set MODE[1:0] to 0x2 for 32 bit counting (TOP = 468741, log2(468741) = 18.8384)
   
   REG_TCC0_CTRLA |= (1 << (5%32));//set WAVEGEN[1:0] to 0x1 for matching the frequency operation
   
   REG_TCC0_CTRLA |= (1 << (8%32))|(1 << (9%32))|(1 << (10%32));//set PRESCALER[2:0] to 0x7, prescaler == 1024
   
   REG_TCC0_CTRLA |= (1 << (12%32));//set PRESYNC[1:0] to 0x1 to Reload or reset the counter on next prescaler clock (PRESC).
   
   REG_TCC0_CC0 = 468741;//Reload or reset the counter on next prescaler clock thus TOP(1Hz) = ((48*10^6)/(1*1024))-1 = 468741 (TOP=f_clock/(f_PWM×Prescaler)-1).
      
   REG_TCC0_INTENSET |= (1 << (0x00));//Enable Overflow/Underflow interrupt by writing bit 0 (OVF) to 1
   
   NVIC_EnableIRQ(TCC0_IRQn);//Enable TCC0 Interrupt   
   
   REG_TCC0_CTRLA |= (1 << (1%32));//Enable and start the timer

    while (1)
    {
      /*wacht op een interrupt*/
      //REG_PORT_OUTTGL0 |=  (1 << (17%32));//Toggle Pin 13
    }
}

void TCC0_Handler()
{
   REG_PORT_OUTTGL0 |=  (1 << (17%32));//Toggle Pin 13   
}

cmaglie

Hi RRLAN,

SAMD21 and SAM3X belongs to different families: even if both are CPU based on a Cortex-M core, the devices (PORT, TIMERS, SPI, UARTS, ecc.) are different and low-level code cannot be transported as is from SAM3X to SAMD21.

For example in your sketch:


   REG_TCC0_CTRLA |= (1 << (3%32));//set MODE[1:0] to 0x2 for 32 bit counting (TOP = 468741, log2(468741) = 18.8384)
   REG_TCC0_CTRLA |= (1 << (5%32));//set WAVEGEN[1:0] to 0x1 for matching the frequency operation


if you read carefully the datasheet of the SAMD21 (chapter 30.8.1) you'll never find the MODE[1:0] bits or the WAVEGEN[1:0] bits in the TCC0.CTRLA register.

That is the reason why it is always preferred to use the Arduino API for this kind of operation if you want to make portable code (or any other specialized library if the functions provided by the Arduino Core are not powerful enough) instead of writing directly on the registers of the CPU.
C.

RRLAN

thanks a lot, i was looking at chapter 29 instead of 30.

moresun

Hello cmaglie

I would like to see how it looks like with the Arduino API.
Would it be possible for you to post a working piece of code?

Greetings
Markus

NT7S

I'm curious about this as well. I'm versed in avr-gcc, and how to use avr-gcc code to set timers in Arduino for the boards based on the AVR platform, but I'm not certain what cmaglie is referring to if he does not mean using the CMSIS library. Thanks.

Hello cmaglie

I would like to see how it looks like with the Arduino API.
Would it be possible for you to post a working piece of code?

Greetings
Markus


moresun

Hi RRLAN

At least would it be possible to get your final working example?

Greetings

OMac

Hi RRLAN,

I'm also trying to use a timer interrupt on the zero.

I have copied your code and when uploaded to the Zero the function SystemInit() causes the arduino to hang.
When I comment out SystemInit() the arduino doesn't hang, but ISR is never called.

Any chance you or anyone else could give us an example of a working Arduino Zero timer interrupt configuration?





q2dg

Any chance you or anyone else could give us an example of a working Arduino Zero timer interrupt configuration?
Oooh, another very interesting and reasonable question without answer. Come on, Arduino guys...your documentation sucks

moresun

#8
Aug 13, 2015, 02:03 pm Last Edit: Aug 13, 2015, 05:12 pm by moresun
HI

After a vacation and some work I got it  :) .
I have two versions one for the TC timer chapter 29 and an other one for the TCC chapter 30 of the SAMD21 handbook.

Markus

29. TC - Timer/Counter
Code: [Select]

/**
 * @author Markus Bader
 * @brief this program shows how to use the TC timer with interrupts on an Arduino Zero board
 * @email markus.bader@tuwien.ac.at
 */
 
int pin_ovf_led = 13;  // debug pin for overflow led
int pin_mc0_led = 5;  // debug pin for compare led
unsigned int loop_count = 0;
unsigned int irq_ovf_count = 0;

void setup() {

  pinMode(pin_ovf_led, OUTPUT);   // for debug leds
  digitalWrite(pin_ovf_led, LOW); // for debug leds
  pinMode(pin_mc0_led, OUTPUT);   // for debug leds
  digitalWrite(pin_mc0_led, LOW); // for debug leds

 
  // Enable clock for TC
  REG_GCLK_CLKCTRL = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID_TCC2_TC3) ;
  while ( GCLK->STATUS.bit.SYNCBUSY == 1 ); // wait for sync

  // The type cast must fit with the selected timer mode
  TcCount16* TC = (TcCount16*) TC3; // get timer struct

  TC->CTRLA.reg &= ~TC_CTRLA_ENABLE;   // Disable TC
  while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync

  TC->CTRLA.reg |= TC_CTRLA_MODE_COUNT16;  // Set Timer counter Mode to 16 bits
  while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync
  TC->CTRLA.reg |= TC_CTRLA_WAVEGEN_NFRQ; // Set TC as normal Normal Frq
  while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync

  TC->CTRLA.reg |= TC_CTRLA_PRESCALER_DIV256;   // Set perscaler
  while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync
 
  // TC->PER.reg = 0xFF;   // Set counter Top using the PER register but the 16/32 bit timer counts allway to max 
  // while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync

  TC->CC[0].reg = 0xFFF;
  while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync
 
  // Interrupts
  TC->INTENSET.reg = 0;              // disable all interrupts
  TC->INTENSET.bit.OVF = 1;          // enable overfollow
  TC->INTENSET.bit.MC0 = 1;          // enable compare match to CC0

  // Enable InterruptVector
  NVIC_EnableIRQ(TC3_IRQn);

  // Enable TC
  TC->CTRLA.reg |= TC_CTRLA_ENABLE;
  while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync

}

void loop() {
  // dummy
  delay(250);
}

void TC3_Handler()
{
  TcCount16* TC = (TcCount16*) TC3; // get timer struct
  if (TC->INTFLAG.bit.OVF == 1) {  // A overflow caused the interrupt
    digitalWrite(pin_ovf_led, irq_ovf_count % 2); // for debug leds
    digitalWrite(pin_mc0_led, HIGH); // for debug leds
    TC->INTFLAG.bit.OVF = 1;    // writing a one clears the flag ovf flag
    irq_ovf_count++;                 // for debug leds
  }
 
  if (TC->INTFLAG.bit.MC0 == 1) {  // A compare to cc0 caused the interrupt
    digitalWrite(pin_mc0_led, LOW);  // for debug leds
    TC->INTFLAG.bit.MC0 = 1;    // writing a one clears the flag ovf flag
  }
}


30. TCC - Timer/Counter for Control Applications
Code: [Select]

/**
 * @author Markus Bader
 * @brief this program shows how to use the TCC timer with interrupts on an Arduino Zero board
 * @email markus.bader@tuwien.ac.at
 */
 
int pin_ovf_led = 13; // debug pin for overflow led
int pin_mc0_led = 5;  // debug pin for compare led
unsigned int loop_count = 0;
unsigned int irq_ovf_count = 0;

void setup() {

  pinMode(pin_ovf_led, OUTPUT);   // for debug leds
  digitalWrite(pin_ovf_led, LOW); // for debug leds
  pinMode(pin_mc0_led, OUTPUT);   // for debug leds
  digitalWrite(pin_mc0_led, LOW); // for debug leds

 
  // Enable clock for TC
  REG_GCLK_CLKCTRL = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID_TCC0_TCC1) ;
  while ( GCLK->STATUS.bit.SYNCBUSY == 1 ); // wait for sync


  // The type cast must fit with the selected timer
  Tcc* TC = (Tcc*) TCC0; // get timer struct
 
  TC->CTRLA.reg &= ~TCC_CTRLA_ENABLE;   // Disable TC
  while (TC->SYNCBUSY.bit.ENABLE == 1); // wait for sync


  TC->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV256;   // Set perscaler


  TC->WAVE.reg |= TCC_WAVE_WAVEGEN_NFRQ;   // Set wave form configuration
  while (TC->SYNCBUSY.bit.WAVE == 1); // wait for sync

  TC->PER.reg = 0xFFFF;              // Set counter Top using the PER register 
  while (TC->SYNCBUSY.bit.PER == 1); // wait for sync

  TC->CC[0].reg = 0xFFF;
  while (TC->SYNCBUSY.bit.CC0 == 1); // wait for sync
 
  // Interrupts
  TC->INTENSET.reg = 0;                 // disable all interrupts
  TC->INTENSET.bit.OVF = 1;          // enable overfollow
  TC->INTENSET.bit.MC0 = 1;          // enable compare match to CC0

  // Enable InterruptVector
  NVIC_EnableIRQ(TCC0_IRQn);

  // Enable TC
  TC->CTRLA.reg |= TCC_CTRLA_ENABLE ;
  while (TC->SYNCBUSY.bit.ENABLE == 1); // wait for sync

}

void loop() {
  // dummy
  delay(250);
}

void TCC0_Handler()
{
  Tcc* TC = (Tcc*) TCC0;       // get timer struct
  if (TC->INTFLAG.bit.OVF == 1) {  // A overflow caused the interrupt
    digitalWrite(pin_ovf_led, irq_ovf_count % 2); // for debug leds
    digitalWrite(pin_mc0_led, HIGH); // for debug leds
    TC->INTFLAG.bit.OVF = 1;    // writing a one clears the flag ovf flag
    irq_ovf_count++;                 // for debug leds
  }
 
  if (TC->INTFLAG.bit.MC0 == 1) {  // A compare to cc0 caused the interrupt
    digitalWrite(pin_mc0_led, LOW);  // for debug leds
    TC->INTFLAG.bit.MC0 = 1;    // writing a one clears the flag ovf flag
  }
}

q2dg

Great!
Could this code be "packaged" as a library? (à la https://github.com/ivanseidel/DueTimer for Due)
This would be fantastic.
Thanks!

moresun

Hi

There is no interface, it is just a demo

moresun


pharaohamps

Hi

I placed some demo programs on github:
https://github.com/maxbader/arduino_tools.git
Your demo programs are great - any advice on how to set the timer frequency?  I can change it manually by adjusting TC->PER.reg and TC->CC.reg but I'd love to know how to set the interval to a specific time duration like I can with the TimerOne library.

whitlow

Hi,

I don't know if this is what you need, but here is an example sketch that shows how the clock frequency for the timers can be set for an M0 Pro.  (Note only TC3 and TC5 are available because TC4 is used internally for the micros() routine in the M0 Pro - I am not sure about the Zero). If you want to set the top-count and generate interrupts when this is reached you might be better of with using the TCC. This is because they are designed to be used in this mode for PWM control and H-bridge motor drivers.

For my part, I have been trying to read the TCC counter values for use as a 24-bit live-time clock. This will reduce latency due to interrupt servicing compared to 16 bit timer counters. Sadly, despite a months of evenings testing things and reading the manual, I did not manage to get this to work.  It seems it only works using the event system.

I wish you success!

Harry J. Whitlow

Code: [Select]



/////////////////////////////////////////////////////////////////////////
//
//
//   Real time clock on TC3
//
//
//
/////////////////////////////////////////////////////////////////////////

volatile uint32_t realTcount = 0x0 ;   // Counter for superticks (overflow interrupts)


void setup() {
Serial.begin(9600);
setTC3clock();
}


/*
This is test code that counts to 10 s then resets and waits 3s to test
reset, start and stop functions on TC3.
*/
void loop() {
 
  delay(250);
 
    Serial.print(realTime(), 7);
    Serial.println();
    double val = realTime();
   
    if( int(val) >= 10)
    {
  resetStartTC3();  // reset so never gets above 10 s
      stopTC3();
    delay(3000); //wait 3 sec
    startTC3();
    }
   
}





void TC3_Handler()  // Interrupt on overflow
{
  TcCount16* TC = (TcCount16*) TC3; // get timer struct
    realTcount++;                    // Increment the supertick register
    TC->INTFLAG.bit.OVF = 1;    // writing a one clears the ovf flag
//  }
}

/*
Get the real time in seconds.
*/
double realTime()
{   
double  realTime =  (realTcount * 1.2288E-2) + (REG_TC3_COUNT16_COUNT * 1.875E-7) ;;
return realTime;
}


/*
  Setup the Generic clock register
*/


void setTC3clock()
{
  GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID( GCM_TCC2_TC3 ));
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization
  TcCount16* TC = (TcCount16*) TC3; // get timer struct
  TC->CTRLA.reg &= ~TC_CTRLA_ENABLE;   // Disable TC
  while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync
  TC->CTRLA.reg |= TC_CTRLA_MODE_COUNT16;  // Set Timer counter Mode to 16 bits
  while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync
  TC->CTRLA.reg |= TC_CTRLA_WAVEGEN_NFRQ; // Set TC as normal Normal Frq
  while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync
  TC->CTRLA.reg |= TC_CTRLA_PRESCALER_DIV8;   // Set perscaler
  while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync
  // Interrupts
  TC->INTENSET.reg = 0;              // disable all interrupts
  TC->INTENSET.bit.OVF = 1;          // enable overfollow interrup
  // Enable InterruptVector
  NVIC_EnableIRQ(TC3_IRQn);
  // Enable TC
  TC->CTRLA.reg |= TC_CTRLA_ENABLE;
  while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync
}



void resetStartTC3()
{
   TcCount16* TC = (TcCount16*) TC3; // get timer struct
   realTcount = 0x00;                // Zero superclicks
    TC->CTRLBSET.reg |= TC_CTRLBCLR_CMD_RETRIGGER;   // restart
}

void stopTC3()
{
  TcCount16* TC = (TcCount16*) TC3; // get timer struct
    TC->CTRLBSET.reg |= TC_CTRLBSET_CMD_STOP;   // Stop counter
}


void startTC3()
{
  TcCount16* TC = (TcCount16*) TC3; // get timer struct
    TC->CTRLBSET.reg |= TC_CTRLBSET_CMD_RETRIGGER;   //  Start
}



pharaohamps

OK, I think I have this figured out.  I'm using the TCC0 example from moresun's site.  In order to change the timer count in the middle of the program you have to invoke Tcc* TC = (Tcc*) TCC0;

Here's my function for changing the counter:

Code: [Select]
void setTimer(long period) {

  // The type cast must fit with the selected timer

 // must do this or you can't touch the timer

  Tcc* TC = (Tcc*) TCC0; // get timer struct



  TC->CTRLA.reg &= ~TCC_CTRLA_ENABLE;   // Disable TC
  while (TC->SYNCBUSY.bit.ENABLE == 1); // wait for sync

TC->PER.reg = period;              // Set counter Top using the PER register 
  while (TC->SYNCBUSY.bit.PER == 1); // wait for sync

    // Enable TC
  TC->CTRLA.reg |= TCC_CTRLA_ENABLE ;
  while (TC->SYNCBUSY.bit.ENABLE == 1); // wait for sync
 
}


Changing the prescaler in the middle of the program crashes my application, so I need to figure out what's up with that.  There's also a weird spot in the timer top count: if I set the timer with a period of less than 4185 the speed goes DOWN instead of up, and over a certain point I have to feed it HALF the period to get the right output speed.  I've got a hack in my application now to do this but it's odd.

 But thanks to moresun for the examples, very helpful.

Go Up