GPT2 Timer Interrupt - Hanging Up

Dimitri

Well-known member
Hello All,

I am trying to generate a simple counter, using GPT2 timer and an interrupt. Currently, when GPT2_CNT reaches the Output Compare Value (GPT2_OCR1), the program stops executing.

Within my ISR, I only increment a counter value.

My Code for GPT2 Timer:
Code:
/* 
 *  Teensy 4.1 General Purpose Timers Testing
 *  March 2, 2022
 */
const byte pin_LED = 13;

boolean LED_st = 1;
const unsigned long LED_time_ms = 100;
unsigned int ISR_Cnt = 0;
unsigned long t1,t2;

///////////////////////////////////////////////////////////////////////////////////////////////
void setup() 
{ 
  Serial.begin(115200);
  pinMode(pin_LED,OUTPUT);
  digitalWrite(pin_LED,HIGH);
  
  Setup_GPT_Regs();
  
  t1 = millis();
  t2 = t1;
}
///////////////////////////////////////////////////////////////////////////////////////////////
void loop()
{
  t1 = millis();
  
  if(((t1-t2) >= LED_time_ms) || (t2 > t1))
  {
    LED_st = !LED_st;      
    digitalWrite(pin_LED,LED_st);
    
    Serial.print(t1/1000);
    Serial.print("\t");
    Serial.print(ISR_Cnt);
    Serial.print("\t");
    Serial.println(GPT2_CNT);

    t2 = t1;
  }
}
///////////////////////////////////////////////////////////////////////////////////////////////
void Setup_GPT2_Regs()
{ 
  /* Initialize GPT2 */
  CCM_CCGR0 |= CCM_CCGR0_GPT2_BUS(CCM_CCGR_ON) | CCM_CCGR0_GPT2_SERIAL(CCM_CCGR_ON);
  
  GPT2_SR = 0x3F;                 // Clear all prior status - Not sure exactly how this works
  GPT2_SR = 0x00;                 // Not sure if this is necessary
  GPT2_CR |=  GPT_CR_CLKSRC(1);   // Set clocksource to Peripheral Clock
  GPT2_CR |= GPT_CR_ENMOD;        // Set Reset Mode
  GPT2_IR |= GPT_IR_OF1IE;        // Enable Output Compare for OCR1
  GPT2_OCR1 = 0x10FFFF00;         // Set Output Compare Value
  GPT2_CR |= GPT_CR_EN;           // Enable GPT2

  /* Setup Interrupt */
  attachInterruptVector(IRQ_GPT2, &Test_ISR);
  //NVIC_SET_PRIORITY(IRQ_GPT2, 0);
  NVIC_ENABLE_IRQ(IRQ_GPT2);
}
///////////////////////////////////////////////////////////////////////////////////////////////
inline void Test_ISR()
{
  ISR_Cnt = ISR_Cnt + 1;
}


Also - Does anyone know how to define a ISR directly, instead of using attachInterruptVector(xx,yy) function? In AVR-world, I would use something like:

Code:
ISR(TIMER1_COMPA_vect)
{
  //Do ISR Stuff Here
}
 
Last question first - the nature of the ARM processor is a MCU specific vector array of the functions to handle each of the many interrupts, so code like this is required:
attachInterruptVector(IRQ_GPT2, &gpt2_isr);

Looking for similar use see : {local install}\hardware\teensy\avr\libraries\TeensyThreads\TeensyThreads.cpp
Code:
    case 2:
      CCM_CCGR1 |= CCM_CCGR1_GPT1_BUS(CCM_CCGR_ON) ;  // enable GPT1 module
      GPT2_CR = 0;                   // disable timer
      GPT2_PR = 23;                  // prescale: divide by 24 so 1 tick = 1 microsecond at 24MHz
      GPT2_OCR1 = microseconds - 1;  // compare value
      GPT2_SR = 0x3F;                // clear all prior status
      GPT2_IR = GPT_IR_OF1IE;        // use first timer
      GPT2_CR = GPT_CR_EN | GPT_CR_CLKSRC(1) ; // set to peripheral clock (24MHz)
      break;

And the _isr() there looks like - it may show keeping the timer running?:
Code:
static void __attribute((naked, noinline)) gpt2_isr() {
  GPT2_SR |= GPT_SR_OF1;  // clear set bit
  __asm volatile ("dsb"); // see github bug #20 by manitou48
//  __asm volatile("b context_switch");  // this is THREADS SPECIFIC
}

@manitou for the WIN! :) - the sample code looks the same ...
 
Slow Counter Mis-Incrementing

Hi Manitou,

Thank you for the response, I found right after I posted that when adjust my ISR as such:
Code:
inline void Test_ISR()
{
  GPT2_SR = 0x3F;
  ISR_Cnt = ISR_Cnt + 1;
}

Then I get the ISR to behave. I did not initially realize at first that I had to clear the Status Register.

One thing I find strange however - if you look at my original code, I use Serial.print to output the value of my variable ISR_Cnt (every 250ms). The ISR_Cnt value increments by a value of 4, each time. Below, I have modified my code such that the ISR will execute every 1000ms.



Code:
const byte pin_LED = 13;
boolean LED_st = 1;
const unsigned long LED_time_ms = 250;

unsigned int ISR_Cnt = 0;
unsigned long t1,t2;

///////////////////////////////////////////////////////////////////////////////////////////////
void setup() 
{ 
  Serial.begin(115200);
  pinMode(pin_LED,OUTPUT); 
  
  SetupStream();
  
  Setup_GPT_Regs();

  Serial.print("t(ms) \t\t");
  Serial.print("ISR_Cnt \t");
  Serial.println("GPT2_CNT");
  
  t1 = millis();
  t2 = t1;
}
///////////////////////////////////////////////////////////////////////////////////////////////
void loop()
{
  
  t1 = millis();

  if(((t1-t2) >= LED_time_ms) || (t2 > t1))
  {
    LED_st = !LED_st;    
    digitalWrite(pin_LED,LED_st);
    
    Serial.print(t1);
    Serial.print("\t\t");
    Serial.print(ISR_Cnt);
    Serial.print("\t\t");
    Serial.println(GPT2_CNT);

    t2 = t1;
  }  
}
///////////////////////////////////////////////////////////////////////////////////////////////
void Setup_GPT_Regs()
{
  /* Initialize GPT2 */
  CCM_CCGR0 |= CCM_CCGR0_GPT2_BUS(CCM_CCGR_ON) | CCM_CCGR0_GPT2_SERIAL(CCM_CCGR_ON);
  
  GPT2_SR = 0x3F;                 // clear all prior status
  GPT2_CR |=  GPT_CR_CLKSRC(1);   // Set clocksource to Peripheral Clock
  GPT2_CR |= GPT_CR_ENMOD;        // Set Reset Mode
  GPT2_IR |= GPT_IR_OF1IE;        // Enable Output Compare 
  GPT2_OCR1 = 0x016E3600;         // Set Output Compare Value
  GPT2_CR |= GPT_CR_EN;           // Enable GPT2

  /* Setup Interrupt */
  attachInterruptVector(IRQ_GPT2, &Dimitri_ISR);
  //NVIC_SET_PRIORITY(IRQ_GPT2, 0);
  NVIC_ENABLE_IRQ(IRQ_GPT2);
}
///////////////////////////////////////////////////////////////////////////////////////////////
void Dimitri_ISR()
{
  GPT2_SR = 0x3F;
  ISR_Cnt = ISR_Cnt + 1;  
}


Here's the output of my Serial monitor:
Code:
t(ms) 		ISR_Cnt 	GPT2_CNT
1168		0		5999876
1418		0		11999876
1668		0		17999877
1918		0		23999876
2168		4		5999876
2418		4		11999876
2668		4		17999875
2918		4		23999875
3168		8		5999875
3418		8		11999874
3668		8		17999875
3918		8		23999874
4168		12		5999874
4418		12		11999873
4668		12		17999873
4918		12		23999874
5168		16		5999872
5418		16		11999873
5668		16		17999872
5918		16		23999873
6168		20		5999871
6418		20		11999872
6668		20		17999871
6918		20		23999872
7168		24		5999870
7418		24		11999871
7668		24		17999870
7918		24		23999871
8168		28		5999869
8418		28		11999870
 
Update:

Surprisingly.... when I put a Serial.print() call within the ISR, the output incrementing of ISR_Cnt is correct. I have no clue why this is occuring, investigating now and will update ASAP.


This increments correctly:
Code:
void Dimitri_ISR()
{
  GPT2_SR = 0x3F;
  Serial.print("ISR \t");
  ISR_Cnt = ISR_Cnt + 1;
  Serial.println();
}

Serial Output:
Code:
t(ms) 		ISR_Cnt 	GPT2_CNT
2517		0		5999879
2767		0		11999879
3017		0		17999879
3267		0		23999879
ISR 	
3517		1		5999878
3767		1		11999878
4017		1		17999878
4267		1		23999877
ISR 	
4517		2		5999878
4767		2		11999877
5017		2		17999878
5267		2		23999877
ISR 	
5517		3		5999877
5767		3		11999877
6017		3		17999877
6267		3		23999876
ISR 	
6517		4		5999875
6767		4		11999876
7017		4		17999876
7267		4		23999875
ISR 	
7517		5		5999875
 
Perhaps replace the .print with above posted :: __asm volatile ("dsb"); // see github bug #20 by manitou48

The print may be slowing it down enough to prevent _isr() double tapping - like the "dsb" does.

That is also in the linked code @manitou provided
 
defragster,

I was wondering about __asm volatile ("dsb");

Adding that line of code to the end of the ISR works great!

Thank You!
 
you should declare any external variables that are getting updated in the ISR as volatile
volatile unsigned int ISR_Cnt = 0;
 
Hi Manitou,

I actually made my variable volatile before I added __asm volatile ("dsb");, and that alone did not solve my problem. I will try it again to be sure (I tried about 100 combinations of things) but as I recall, making the variable volatile does not by-itself prevent the double-tap (quadruple-tap in my case).

Thanks for all your help! Very much appreciated!
 
volatile just helps ensure data integrity in use - that compiler doesn't assume the variable is somehow static to the code at hand when it can be modified elsewhere when used in interrupt context. It forces repeat RAM reference loading as needed.

The "dsb" keeps the 'fast and slow' halves of the processor in sync. The fast part being the running core - versus the I/O bus running at a slower clock based on the pin processing speed. A short _isr() can enter and exit before the fast core notices the interrupt was serviced and without the "dsb" can re-trigger the _isr().
 
Back
Top