Go Down

Topic: Timer Interrupt Basic Example (Read 373 times) previous topic - next topic

baqwas

Hello,

I couldn't port my UNO code to MKR1010 for a beginner Interrupt Service Routine declaration. The following statement fails compilation because presumably the corresponding header file(s) were not implicitly loaded at the start of the compilation. The error message is:

Code: [Select]
Arduino: 1.8.8 (Linux), Board: "Arduino MKR WiFi 1010"

Build options changed, rebuilding all
BlinkInterrupt:29:5: error: expected constructor, destructor, or type conversion before '(' token
 ISR(TIMER1_COMPA_vect)
     ^
exit status 1
expected constructor, destructor, or type conversion before '(' token


The ISR routine that worked under UNO is:

Code: [Select]
ISR(TIMER1_COMPA_vect)
{
  interrupted = true;       // volatile variable
}


How can I get timer interrupts to work in MKR1010? I need the most elementary example, please.

Thanks.



JarkkoL

Setting up timer interrupts on MKR is more difficult than AVR. I'm using the following code to set the interrupt to run at given frequency on MKR:

Code: [Select]

void setup_timer4(uint16_t clk_div_, uint8_t count_)
{
   // Set up the generic clock (GCLK4) used to clock timers
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 1: 48MHz/1=48MHz
                    GCLK_GENDIV_ID(4);            // Select Generic Clock (GCLK) 4
  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 GCLK4
                     GCLK_GENCTRL_SRC_DFLL48M |   // Set the 48MHz clock source
                     GCLK_GENCTRL_ID(4);          // Select GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Feed GCLK4 to TC4 and TC5
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TC4 and TC5
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_TC4_TC5;     // Feed the GCLK4 to TC4 and TC5
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  REG_TC4_CTRLA |= TC_CTRLA_MODE_COUNT8;          // Set the counter to 8-bit mode
  while (TC4->COUNT8.STATUS.bit.SYNCBUSY);        // Wait for synchronization

  REG_TC4_COUNT8_CC0 = count_;                      // Set the TC4 CC0 register to some arbitary value
  while (TC4->COUNT8.STATUS.bit.SYNCBUSY);        // Wait for synchronization

  NVIC_SetPriority(TC4_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority for TC4 to 0 (highest)
  NVIC_EnableIRQ(TC4_IRQn);         // Connect TC4 to Nested Vector Interrupt Controller (NVIC)

  REG_TC4_INTFLAG |= TC_INTFLAG_OVF;              // Clear the interrupt flags
  REG_TC4_INTENSET = TC_INTENSET_OVF;             // Enable TC4 interrupts

  uint16_t prescale=0;
  switch(clk_div_)
  {
    case 1:    prescale=TC_CTRLA_PRESCALER(0); break;
    case 2:    prescale=TC_CTRLA_PRESCALER(1); break;
    case 4:    prescale=TC_CTRLA_PRESCALER(2); break;
    case 8:    prescale=TC_CTRLA_PRESCALER(3); break;
    case 16:   prescale=TC_CTRLA_PRESCALER(4); break;
    case 64:   prescale=TC_CTRLA_PRESCALER(5); break;
    case 256:  prescale=TC_CTRLA_PRESCALER(6); break;
    case 1024: prescale=TC_CTRLA_PRESCALER(7); break;
  }
  REG_TC4_CTRLA |= prescale | TC_CTRLA_WAVEGEN_MFRQ | TC_CTRLA_ENABLE;    // Enable TC4
  while (TC4->COUNT8.STATUS.bit.SYNCBUSY);        // Wait for synchronization
}
//----

uint16_t next_pow2(uint16_t v_)
{
  // the next power-of-2 of the value (if v_ is pow-of-2 returns v_)
  --v_;
  v_|=v_>>1;
  v_|=v_>>2;
  v_|=v_>>4;
  v_|=v_>>8;
  return v_+1;
}
//----

uint16_t get_clk_div(uint32_t freq_)
{
  float ideal_clk_div=48000000.0f/(256.0f*float(freq_));
  uint16_t clk_div=next_pow2(uint16_t(ceil(ideal_clk_div)));
  switch(clk_div)
  {
    case 32: clk_div=64; break;
    case 128: clk_div=256; break;
    case 512: clk_div=1024; break;
  }
  return clk_div;
}
//----

void setup_timer4(uint32_t freq_)
{
  uint16_t clk_div=get_clk_div(freq_);
  uint8_t clk_cnt=(48000000/clk_div)/freq_;
  setup_timer4(clk_div, clk_cnt);
}
//---------------------------------------------------------------------------

void TC4_Handler()
{
  if (TC4->COUNT16.INTFLAG.bit.OVF && TC4->COUNT16.INTENSET.bit.OVF)             
  {
    /*write your interrupt code here*/
    REG_TC4_INTFLAG = TC_INTFLAG_OVF;
  }
}

So just pass your requested frequency to setup_timer4() and then TC4_Handler() function starts to get called at that frequency. E.g. setup_timer4(1000) makes TC4_Handler() to be called 1000 times per second.

shengping

Setting up timer interrupts on MKR is more difficult than AVR. I'm using the following code to set the interrupt to run at given frequency on MKR:

Code: [Select]

void setup_timer4(uint16_t clk_div_, uint8_t count_)
{
   // Set up the generic clock (GCLK4) used to clock timers
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 1: 48MHz/1=48MHz
                    GCLK_GENDIV_ID(4);            // Select Generic Clock (GCLK) 4
  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 GCLK4
                     GCLK_GENCTRL_SRC_DFLL48M |   // Set the 48MHz clock source
                     GCLK_GENCTRL_ID(4);          // Select GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Feed GCLK4 to TC4 and TC5
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TC4 and TC5
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_TC4_TC5;     // Feed the GCLK4 to TC4 and TC5
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  REG_TC4_CTRLA |= TC_CTRLA_MODE_COUNT8;          // Set the counter to 8-bit mode
  while (TC4->COUNT8.STATUS.bit.SYNCBUSY);        // Wait for synchronization

  REG_TC4_COUNT8_CC0 = count_;                      // Set the TC4 CC0 register to some arbitary value
  while (TC4->COUNT8.STATUS.bit.SYNCBUSY);        // Wait for synchronization

  NVIC_SetPriority(TC4_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority for TC4 to 0 (highest)
  NVIC_EnableIRQ(TC4_IRQn);         // Connect TC4 to Nested Vector Interrupt Controller (NVIC)

  REG_TC4_INTFLAG |= TC_INTFLAG_OVF;              // Clear the interrupt flags
  REG_TC4_INTENSET = TC_INTENSET_OVF;             // Enable TC4 interrupts

  uint16_t prescale=0;
  switch(clk_div_)
  {
    case 1:    prescale=TC_CTRLA_PRESCALER(0); break;
    case 2:    prescale=TC_CTRLA_PRESCALER(1); break;
    case 4:    prescale=TC_CTRLA_PRESCALER(2); break;
    case 8:    prescale=TC_CTRLA_PRESCALER(3); break;
    case 16:   prescale=TC_CTRLA_PRESCALER(4); break;
    case 64:   prescale=TC_CTRLA_PRESCALER(5); break;
    case 256:  prescale=TC_CTRLA_PRESCALER(6); break;
    case 1024: prescale=TC_CTRLA_PRESCALER(7); break;
  }
  REG_TC4_CTRLA |= prescale | TC_CTRLA_WAVEGEN_MFRQ | TC_CTRLA_ENABLE;    // Enable TC4
  while (TC4->COUNT8.STATUS.bit.SYNCBUSY);        // Wait for synchronization
}
//----

uint16_t next_pow2(uint16_t v_)
{
  // the next power-of-2 of the value (if v_ is pow-of-2 returns v_)
  --v_;
  v_|=v_>>1;
  v_|=v_>>2;
  v_|=v_>>4;
  v_|=v_>>8;
  return v_+1;
}
//----

uint16_t get_clk_div(uint32_t freq_)
{
  float ideal_clk_div=48000000.0f/(256.0f*float(freq_));
  uint16_t clk_div=next_pow2(uint16_t(ceil(ideal_clk_div)));
  switch(clk_div)
  {
    case 32: clk_div=64; break;
    case 128: clk_div=256; break;
    case 512: clk_div=1024; break;
  }
  return clk_div;
}
//----

void setup_timer4(uint32_t freq_)
{
  uint16_t clk_div=get_clk_div(freq_);
  uint8_t clk_cnt=(48000000/clk_div)/freq_;
  setup_timer4(clk_div, clk_cnt);
}
//---------------------------------------------------------------------------

void TC4_Handler()
{
  if (TC4->COUNT16.INTFLAG.bit.OVF && TC4->COUNT16.INTENSET.bit.OVF)             
  {
    /*write your interrupt code here*/
    REG_TC4_INTFLAG = TC_INTFLAG_OVF;
  }
}

So just pass your requested frequency to setup_timer4() and then TC4_Handler() function starts to get called at that frequency. E.g. setup_timer4(1000) makes TC4_Handler() to be called 1000 times per second.

Hi Jarkkol

Thank you providing the interrupt code. And I paste it in my IDE for sending sine wave in certain frequency.
but I notice that the frequency of generated sine wave is no stable as I expect. Could you tell how the interrupt works or how can I make it stable? or where can I find the resource so that I can change the code by myself. Thank you. I will appreciate you a lot if you can help me.

Go Up