How this timer even works ?

This guy creates a sine wave(Zero) using the timers: (you basically input the resolution then it outputs sine from the DAC)

      //This sketch generates a sine wave on the Arduino Zero DAC based on user entered sample count and sample rate
      //It was used in a tutorial video on the ForceTronics YouTube Channel. This code can be used and modified freely
      //at the users own risk
      volatile int sIndex; //Tracks sinewave points in array
      int sampleCount = 100; // Number of samples to read in block
      int *wavSamples; //array to store sinewave points
      uint32_t sampleRate = 1000; //sample rate of the sine wave
      void setup() {
        analogWriteResolution(10); //set the Arduino DAC for 10 bits of resolution (max)
        analogReadResolution(12); //set the ADC resolution to 12 bits, default is 10
        getSinParameters(); //get sinewave parameters from user on serial monitor
        /*Allocate the buffer where the samples are stored*/
        wavSamples = (int *) malloc(sampleCount * sizeof(int));
        genSin(sampleCount); //function generates sine wave

      void loop() {
        sIndex = 0;   //Set to zero to start from beginning of waveform
        tcConfigure(sampleRate); //setup the timer counter based off of the user entered sample rate
        //loop until all the sine wave points have been played
        while (sIndex<sampleCount)
       //start timer, once timer is done interrupt will occur and DAC value will be updated
           float voltage = analogRead(A1) * 3.3 / 4096.0;
        //disable and reset timer counter

      //This function generates a sine wave and stores it in the wavSamples array
      //The input argument is the number of points the sine wave is made up of
      void genSin(int sCount) {
       const float pi2 = 6.28; //2 x pi
       float in; 
       for(int i=0; i<sCount;i++) { //loop to build sine wave based on sample count
        in = pi2*(1/(float)sCount)*(float)i; //calculate value in radians for sin()
        wavSamples[i] = ((int)(sin(in)*511.5 + 511.5)); //Calculate sine wave value and offset based on DAC resolution 511.5 = 1023/2

      //This function handles getting and setting the sine wave parameters from 
      //the serial monitor. It is important to use the Serial.end() function
      //to ensure it doesn't mess up the Timer counter interrupts later
      void getSinParameters() {
        Serial.println("Enter number of points in sine wave (range 10 to 1000)");
        sampleCount = readParameter();
        if (sampleCount < 10 || sampleCount > 1000) sampleCount = 100;
        Serial.print("Sample count set to ");
        Serial.println("Enter sample rate or samples per second for DAC (range 1 to 100k)");
        sampleRate = readParameter();
        if (sampleRate < 1 || sampleRate > 100000) sampleRate = 10000;
        Serial.print("Sample rate set to ");
        Serial.println("Generating sine wave........");
      //waits for serial data and reads it in. This function reads in the parameters
      // that are entered into the serial terminal
      int readParameter() {
       return Serial.parseInt(); //get int that was entered on Serial monitor

      // Configures the TC to generate output events at the sample frequency.
      //Configures the TC in Frequency Generation mode, with an event output once
      //each time the audio sample frequency period expires.
       void tcConfigure(int sampleRate)
       // Enable GCLK for TCC2 and TC5 (timer counter input clock)
       while (GCLK->STATUS.bit.SYNCBUSY);
        tcReset(); //reset TC5
        // Set Timer counter Mode to 16 bits
       // Set TC5 mode as match frequency
       //set prescaler and enable TC5
       //set TC5 timer counter based off of the system clock and the user defined sample rate or waveform
       TC5->COUNT16.CC[0].reg = (uint16_t) (SystemCoreClock / sampleRate - 1);
       while (tcIsSyncing());
       // Configure interrupt request
       NVIC_SetPriority(TC5_IRQn, 0);
        // Enable the TC5 interrupt request
       TC5->COUNT16.INTENSET.bit.MC0 = 1;
       while (tcIsSyncing()); //wait until TC5 is done syncing 

      //Function that is used to check if TC5 is done syncing
      //returns true when it is done syncing
      bool tcIsSyncing()
        return TC5->COUNT16.STATUS.reg & TC_STATUS_SYNCBUSY;

      //This function enables TC5 and waits for it to be ready
      void tcStartCounter()
        TC5->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE; //set the CTRLA register
        while (tcIsSyncing()); //wait until snyc'd

      //Reset TC5 
      void tcReset()
        TC5->COUNT16.CTRLA.reg = TC_CTRLA_SWRST;
        while (tcIsSyncing());
        while (TC5->COUNT16.CTRLA.bit.SWRST);

      //disable TC5
      void tcDisable()
        TC5->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE;
        while (tcIsSyncing());

      void TC5_Handler (void)
        analogWrite(A0, wavSamples[sIndex]);
        TC5->COUNT16.INTFLAG.bit.MC0 = 1;

There is something very strange about this code, although it works.
In the main loop, there is a while loop that is basically fires up timers in the processor speed, without even waiting for the previous interrupt to finish, so

  while (sIndex<sampleCount)
       //start timer, once timer is done interrupt will occur and DAC value will be updated

means he starts timers again and again in the clock speed, before the previous one is done. ( the while loop speed is 48MHz, while timers are slower)

What am i missing here ? how is it working ?

There's only one timer in the code, that's TC5. The function:


...should really be placed just before the while loop.

In the while loop, it's constantly setting the timer's enable bit, but that bit's already been set the first time around (the while loop), so calling it each time has no further effect.

The while loop is waiting for the interrupt hander to be called "sampleCount" number of times before continuing.

Thank Martin, I see, so setting this bit again and again has no effect, this is why it works. So if I take this line out of the while loop, the code should work exactly the same.

Can you also explain please how this :

       TC5->COUNT16.CC[0].reg = (uint16_t) (SystemCoreClock / sampleRate - 1);

works ? if the percale is 1, TC ticks at 48MHz, that its being divided by a sample rate, so if the sample rate is 1000 the register will get 48MHz/(1000-1) =48KHz ?

What this number means? How can i just set a number to count to in HEX ? I know this formula :

 T(time to count) =  counterTopValue *  ( 1/ (clk/prescale)  )

How is that fits here ?

Hi BenStlr,

So if I take this line out of the while loop, the code should work exactly the same.


if the percale is 1, TC ticks at 48MHz, that its being divided by a sample rate, so if the sample rate is 1000 the register will get 48MHz/(1000-1) =48KHz ?

That's right, but remove the brackets to divide first, 48MHz/1000 - 1. The timer's being put into match frequency (MFRQ) mode. In this mode the CC0 (counter compare 0) register becomes the TOP value. Each time the timer's counter matches the CC0 value, the interrupt handler is called and the counter is reset. As the timer's being clocked at 48MHz and the value in CC0 is 47999, the sample rate is 1kHz, (48MHz/(47999 + 1)).

How is that fits here ?

In this instance the prescaler is 1, therefore the time taken for the timer to count from 0 to CC0 (timer period) is:

Timer period = (CC0 + 1) / timer clock frequency = (47999 + 1) / 48MHz = 0.001s or 1ms (millisecond)

Martin thanks very much ! you really helped me.