How to use the general purpose timer (GPT) output conpare interrupt

LukasFun

Member
I am trying to create multiple interrupts that use the gpt.
The project should look something like this:

Code:
// compare values that will trigger the interrupts
uint32_t ocvalue1 = 1000;
uint32_t ocvalue2 = 1500;

void setup() {
  // Whatever has to be done to get this working
}

// Stuff that should happen on the first interrupt (triggered by ocvalue1)
void interrupt1(void) {
  // My code
}

// Stuff that should happen on the second interrupt (triggered by ocvalue2)
void interrupt2(void) {
  // My code
}

void loop() {
  // Other code that runs in the meantime
}

What do I have to do in the setup() to have this program do what I described?
 
Thanks manitou for the inspiration. I've been working on the project for some time now and was able to get it working the way I wanted it to (I'd still like the structure to be a bit better, but interrupts demand quick response times). I wrote two libraries (one for general use of projects like this and one customizable for each unique project) and one sketch to show it off. Here is the stuff (warning: the comments are in German):

The sketch:
Code:
/*
 * Dieses Programm stellt eine Umgebung zum Testen der Timerbasierten Interrupts mithilfe der Bibliothek L.h bereit.
 * Geschrieben von Lukas Freudenberg, 30. Oktober 2019
 * Dieses Programm ist gemeinfrei (public domain).
 */

#include <L.h>
#include <Interrupt.h>

uint32_t ocvalue1 = 1000;
uint32_t ocvalue2 = 500;
int outPinArr[] = {3, 6, 13};

void setup() {
  L::sBg(4000000);
  L::pinSetup(outPinArr, sizeof(outPinArr)/sizeof(int));
  L::interSetup(0, ocvalue1);
  L::interSetup(1, ocvalue2);
  L::sync();
}

void loop() {
  digitalWriteFast(outPinArr[2], HIGH);
  delay(200);
  digitalWriteFast(outPinArr[2], LOW);
  delay(200);
}

The L.h file:

Code:
/*
 * L.h - Bibliothek, die einem das Leben erleichtert. Speziell für die Programmierung des Teensy 4.0
 * Geschrieben von Lukas Freudenberg, 30. Oktober 2019
 * Diese Bibliothek ist gemeinfrei (public domain).
*/
#ifndef L_h
#define L_h

#include "Arduino.h"

class L {
  public:
    static void pinSetup(int outPins[], int size);  // Setzt die Outputpins
    static void interSetup(int timer, int compare); // Initialisiert einen Timer-Counter mit Interruptfunktion
    static void sync();				    // Synchronisiert GPT1 und GPT2
  private:
    static void interrupt1();	// Was beim Interrupt 1 geschieht
    static void interrupt2();	// Was beim Interrupt 2 geschieht
    static int* pins;		// speichhert die Outputpins
};

#endif

The L.cpp file:

Code:
#include "Arduino.h"
#include "L.h"
#include "Interrupt.h"

int* L::pins;

// Setzt die Outputpins
void L::pinSetup(int outPins[], int size) {
  pins = outPins;
  for(int i = 0; i < size; i++) {
    pinMode(outPins[i], OUTPUT);
  }
  CCM_CSCMR1 = 0x00000000;  // Clock wird durch 1 dividiert (maximale Geschwindigkeit = 150 MHz)
}

// Synchronisiert GPT1 und GPT2
void L::sync() {
  GPT2_CR &= 0xFFFFFFFE;    	// Counter GPT2 deaktivieren (wird beim nächsten Interrupt von GPT1 wieder aktiviert)
}

// Was beim Interrupt 1 geschieht
void L::interrupt1() {
  GPT1_SR = 0x00000001;         // Interrupt im Statusregister zurücksetzen
  while (GPT1_SR == 0x0000001); // Auf Zurücksetzen des Registers warten
  Interrupt::in1(pins[0]);      // Interruptfunktion ausführen
  if (!(GPT2_CR & GPT_CR_EN)){GPT2_CR |= 0x00000001;} // Counter GPT2 aktivieren (aufgrund einer möglichen Synchronisation)
}

// Was beim Interrupt 2 geschieht
void L::interrupt2() {
  GPT2_SR = 0x00000001;         // Interrupt im Statusregister zurücksetzen
  while (GPT2_SR == 0x0000001); // Auf Zurücksetzen des Registers warten
  Interrupt::in2(pins[1]);      // Interruptfunktion ausführen
}

// Initialisiert einen Timer-Counter mit Interruptfunktion
void L::interSetup(int timer, int compare) {
  switch(timer) {
    case 0: CCM_CCGR1 |= 0x03000000;  // Clock für GPT1 Modul aktivieren
            
            // GPT1 konfigurieren
            GPT1_CR = 0;              // Kontrollregister zurücksetzen
            GPT1_PR = 0;              // Vorskalierung = 1:1
            GPT1_CR |= 0x00000040;    // Periphere Clock auswählen für maximale Geschwindigkeit (150 MHz)
            GPT1_OCR1 = compare - 1;  // Wert des Counters für das Auslösen des Interrupts setzen
            GPT1_IR = 0x00000001;     // Output Compare 1 aktivieren
            GPT1_CR |= 0x00000001;    // Counter GPT1 aktivieren
            
            attachInterruptVector(IRQ_GPT1, interrupt1);  // Interruptfunktion festlegen
            NVIC_ENABLE_IRQ(IRQ_GPT1);			  // Interrupt in der Interruptcontroller Tabelle aktivieren
            break;
    
    case 1: CCM_CCGR0 |= 0x03000000;  // Clock für GPT2 Modul aktivieren
            
            // GPT2 konfigurieren
            GPT2_CR = 0;              // Kontrollregister zurücksetzen
            GPT2_PR = 0;              // Vorskalierung = 1:1
            GPT2_CR |= 0x00000040;    // Periphere Clock auswählen für maximale Geschwindigkeit (150 MHz)
            GPT2_OCR1 = compare - 1;  // Wert des Counters für das Auslösen des Interrupts setzen
            GPT2_IR = 0x00000001;     // Output Compare 1 aktivieren
            GPT2_CR |= 0x00000001;    // Counter GPT2 aktivieren
            
            attachInterruptVector(IRQ_GPT2, interrupt2);  // Interrupt festlegen
            NVIC_ENABLE_IRQ(IRQ_GPT2);			  // Interrupt in der Interruptcontroller Tabelle aktivieren
            break;
  }
}

The Interrupt.h file:

Code:
/*
 * Interrupt.h - Bibliothek, die benutzerdefinierte Interrupts mithilfe der Bibliothek L.h auf dem Teensy 4.0 bereitstellt
 * Geschrieben von Lukas Freudenberg, 30. Oktober 2019
 * Diese Bibliothek ist gemeinfrei (public domain).
*/
#ifndef Interrupt_h
#define Interrupt_h

class Interrupt {
  public:
    static void in1(int data);
    static void in2(int data);
};

#endif

And finally the Interrupt.cpp file (this is the one that you would put into the folder of your sketch and edit the functions in it to whatever you want to happen at an interrupt):

Code:
#include "Arduino.h"
#include "Interrupt.h"

void Interrupt::in1(int data){
  digitalWriteFast(data, !digitalReadFast(data));
}

void Interrupt::in2(int data){
  digitalWriteFast(data, HIGH);
  digitalWriteFast(data, LOW);
}

Just as a side note: When a program with interrupts is already running on the Teensy, one might occur during the upload. This will cause it to fail, but don't worry: If you push the on board button to upload the sketch, you're fine.

Any thoughts on how to further improve this?

In any case, I release this code to the public domain.
 
OK, so this works if you want to use only one interrupt per timer (which is two in total). Is there a way to attach three different routines to the three output-compare events of one timer?
 
OK, so this works if you want to use only one interrupt per timer (which is two in total). Is there a way to attach three different routines to the three output-compare events of one timer?

I think in your ISR, you would need to look at GPTx_SR, and bits OF1 OF2 OF3 would indicate which compare-event fired, and then you could call your associated routine for that event. (and clear the OFx bit)
 
I think in your ISR, you would need to look at GPTx_SR, and bits OF1 OF2 OF3 would indicate which compare-event fired, and then you could call your associated routine for that event. (and clear the OFx bit)
Sorry for replying to an old thread, but do you by chance have any example code where this is done?
 
Sorry for replying to an old thread, but do you by chance have any example code where this is done?
It turned out to be very straightforward. In case it's useful to anyone else, here's a simple example adapted from some code by Kuba0040 elsewhere on the forum.

C++:
#include <Arduino.h>

void initialize_gpt1(uint8_t gpt_prescaler, uint32_t OCR1, uint32_t OCR2, uint32_t OCR3, void (*isr_function)()); // The last little thing is the pointer to the ISR function
void gpt1_ISR();

void setup()
{
  Serial.begin(115200);

  // Start timer GTP1 with a prescaler of 15,
  // and compare interrupt values of 10000000, 2000000 and 4000000 for Channels 1, 2 and 3
  initialize_gpt1(15, 10000000, 2000000, 4000000, gpt1_ISR);
}

void gpt1_ISR()
{

  // flags for Output Compare are the three least significant bits of GPT1_SR
  int OutputFlag = GPT1_SR & 0b111;

  if (OutputFlag == 0b001)
    Serial.println("Output Compare Flag 1");
  else if (OutputFlag == 0b010)
    Serial.println("Output Compare Flag 2");
  else
    Serial.println("Output Compare Flag 3");

  // reset output flags
  GPT1_SR |= 0b111;

  asm volatile("dsb");
}

void loop()
{
}

void initialize_gpt1(uint8_t gpt_prescaler, uint32_t OCR1, uint32_t OCR2, uint32_t OCR3, void (*isr_function)()) // The last little thing is the pointer to the ISR function
{
  GPT1_CR = 0;                                      // Clear the GPT control register
  GPT1_PR = (gpt_prescaler - 1) << 11;              // Set the pre-scaler, we have to subtract 1 from it as the timer logic adds it back. Also shift it 11 spaces to the Crystal Prescaler bits
  GPT1_OCR1 = OCR1 - 1;                             // Set Compare Channel 1 value
  GPT1_OCR2 = OCR2 - 1;                             // Set Compare Channel 2 value
  GPT1_OCR3 = OCR3 - 1;                             // Set Compare Channel 3 value
  GPT1_SR = 0;                                      // Clear GPT status register
  GPT1_IR = GPT_IR_OF1IE;                           // Enable interrupt on Compare Channel 1.
  GPT1_IR |= GPT_IR_OF2IE;                          // Enable interrupt on Compare Channel 2.
  GPT1_IR |= GPT_IR_OF3IE;                          // Enable interrupt on Compare Channel 3.
  GPT1_CR |= GPT_CR_EN_24M;                         // Enable the 24Mhz Crystal Clock
  GPT1_CR |= GPT_CR_EN | GPT_CR_CLKSRC(5);          // Set the clock source to the 24MHz Crystal Oscillator and set the enable bit high, starting the timer
  attachInterruptVector(IRQ_GPT1, (*isr_function)); // Attach our ISR function to the GPT interrupt
  NVIC_ENABLE_IRQ(IRQ_GPT1);                        // Enable interrupts comming from GPT1
}
 
Back
Top