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.