Quad Timer Interrupt Setup

Dimitri

Well-known member
Hello Everyone,

In my project, I use Quad Timer (TMR4_CH2) to count up to a value (TMR4_COMP12) and then trigger an interrupt. In the interrupt, I create a PWM signal. I am successful in doing so, however I still have questions regarding some of the registers. My (working) code is below, it outputs a 23.42Hz signal at 80% duty.

In the code I have an LED blinking for a heartbeat, within the loop() function.

Code:
const byte pin_LED = 13;
const byte pinOut_OutputSig = 11;

boolean LED_st = 1;
const unsigned long LED_time_ms = 500;
boolean OutputSig_st = 1;
int i = 0;
unsigned long t1,t2;

///////////////////////////////////////////////////////////////////////////////////////////////
void setup() 
{  
  Serial.begin(115200);
  pinMode(pin_LED,OUTPUT);
  pinMode(pinOut_OutputSig,OUTPUT);
  digitalWrite(pin_LED,HIGH);
  digitalWrite(pinOut_OutputSig,OutputSig_St);
  
  delay(25);
  Setup_TMR4_CH2();
  
  t1 = millis();
  t2 = t1;
}
///////////////////////////////////////////////////////////////////////////////////////////////
void loop()
{
  t1 = millis();

  if(((t1-t2) >= LED_time_ms) || (t2 > t1))
  {
    // Heartbeat function
    LED_st = !LED_st;
    digitalWrite(pin_LED,LED_st);    
    Serial.print(t1/1000);
    Serial.println();
    t2 = t1;
  }
}
///////////////////////////////////////////////////////////////////////////////////////////////
void Dimitri_ISR()
{
  //if((TMR4_SCTRL2 & TMR_SCTRL_TCF) != 0)
  //if((TMR4_SCTRL2 & TMR_SCTRL_TOF) != 0)
    
    OutputSig_St = !OutputSig_St;
    digitalWrite(pinOut_OutputSig,OutputSig_St);
    
    if(OutputSig_St == 1)
    {
      TMR4_COMP12 = 40000;
    }
    else
    {
      TMR4_COMP12 = 10000;
    }
    TMR4_CSCTRL2 &= ~(TMR_CSCTRL_TCF1);  // clear
  __asm volatile ("dsb");  

}
///////////////////////////////////////////////////////////////////////////////////////////////
void Setup_TMR4_CH2()
{
  TMR4_CTRL2 = 0; // stop
  TMR4_LOAD2 = 0;  // start val after compare
  TMR4_COMP12 = 0xF000;  // count up to this val, interrupt,  and start again
  TMR4_CMPLD12 = 0xF000;
  TMR4_CTRL2 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8 + 7) | TMR_CTRL_LENGTH ;  // prescale
  attachInterruptVector(IRQ_QTIMER4, Dimitri_ISR);
  TMR4_CSCTRL2 &= ~(TMR_CSCTRL_TCF1);  // clear
  TMR4_CSCTRL2 |= TMR_CSCTRL_TCF1EN;  // enable interrupt
  //TMR4_SCTRL2 |= TMR_SCTRL_TCFIE;
  NVIC_ENABLE_IRQ(IRQ_QTIMER4);
}

The question is... when do I enable interrupts using TMR4_CSCTRL2 vs TMR4_SCTRL2? Within the function Setup_TMR4_CH2(), I have the line with TMR4_SCTRL2 commented out in the working software, because if I do include this line, then the system will hang up, the PWM signals is whacky, and the heartbeat function within loop() does not work.

Does anyone know why this is? Why is it that within TMR4_CSCTRL2 I have to enable TCF1EN (Timer Compare 1 Interrupt Enable) but I am not supposed to enable TCFIE (Timer Compare Flag Interrupt Enable) within TMR4_SCTRL2?

Also, just FYI, within the ISR, I must clear the TCF flag of TMR4_SCTRL2, otherwise the system will jam up.



Also - I have tried manipulating TMR4_CMPLD12, thinking that when the ISR is invoked, this value would override TMR4_COMP12, however this is not the case. This is why I directly change TMR4_COMP12 within my ISR. Does anyone know why this is such?

Thank You!
 
Why not just use Teensy hardware PWM and avoid the ISR overhead all together ?? Here is sketch that generates 23 hz on pin 11 at 80% duty
Code:
void setup() {
  analogWriteFrequency(11, 23.44);
  analogWrite(11, 205);  // 80% duty
}

void loop() {

}
The teensy core code that manages PWM on T4 is in hardware/teensy/avr/cores/teensy4/pwm.c (pin 11 PWM uses quad timer 1 ch 2)

There are some quad timer test sketches (qtrmr*.ino) at https://github.com/manitou48/teensy4
and see Ch 54 in T4 reference manual and Teensy PWM
 
Hi Manitou,

Firstly - I based my code directly on your examples, so thank you for that!

Second - I need to adjust the high and low times, for each cycle. I am creating a specific digital signal, not just a PWM with a given frequency & Duty-Cycle. Otherwise I'd do exactly what you've posted.


Thanks Again!
 
The question is... when do I enable interrupts using TMR4_CSCTRL2 vs TMR4_SCTRL2? Within the function Setup_TMR4_CH2(), I have the line with TMR4_SCTRL2 commented out in the working software, because if I do include this line, then the system will hang up, the PWM signals is whacky, and the heartbeat function within loop() does not work.

Does anyone know why this is? Why is it that within TMR4_CSCTRL2 I have to enable TCF1EN (Timer Compare 1 Interrupt Enable) but I am not supposed to enable TCFIE (Timer Compare Flag Interrupt Enable) within TMR4_SCTRL2?

Also, just FYI, within the ISR, I must clear the TCF flag of TMR4_SCTRL2, otherwise the system will jam up.



Also - I have tried manipulating TMR4_CMPLD12, thinking that when the ISR is invoked, this value would override TMR4_COMP12, however this is not the case. This is why I directly change TMR4_COMP12 within my ISR. Does anyone know why this is such?

It is common practice to use TCF1EN. if you choose to use TCFIE, then in the ISR you need clear the TCF bit
TMR4_SCTRL2 &= ~(TMR_SCTRL_TCF);.
If you don't clear the TCF (or TCF1) bit in the ISR, the interrupt will be called again immediately on exit of the ISR, resulting in infinite loop (e.g., hung Teensy 4).

To use CMPLD12, read about CL1 field in CSCTRL4, see 54.4.5.14 Variable-Frequency PWM Mode. Though your sketch seems to be working loading TMR4_COMP12 in the ISR.

here is a variable PWM sketch
 
Last edited:
Back
Top