Die Softwarestruktur eines uC-Programms sollte so aussehen, dass alle "üblichen" Arbeiten in einer Hauptschleife ausgeführt werden. Interrupt-Rotuinen setzen "nur" Flags oder verwalten FIFOs.
Zeiten können elegant über eine Struktur (ti) verwaltet werden, die eine Systemzeit (ti.Akt) und andere Aktionszeiten bzw. Triggerzeiten enthält.
#include <avr/io.h> #include <avr/interrupt.h> #include <stdint.h> //... volatile uint32_t gtiAkt; typedef { uint32_t Akt; // Systemzeit in ms uint32_t Toggle; uint32_t Start; uint32_t Stop; uint32_t Rise; uint32_t Duration; // usw... } tiStruct; tiStruct ti; // Timer-Compare Interrupt ISR, wird z.B. alle 10ms ausgefuehrt ISR(TIMER1_COMPA_vect) { gtiAkt+=10; } int main(void) { uint_32t *tiptr; while (1) { // die HAUPTSCHLEIFE
cli(); // Interupts (eigentlich nur Timerinterrupt) deaktivieren ti.Akt = gtiAkt; // Zeit atomar abholen if(ti.Akt&0xC0000000) { // Zeitstempel laufen an die Obergrenze gtiAkt-=0x80000000; // aktuelle Zeit und tiptr = (uint_32t*)(ti); // jeden Zeitstempel nach unten korrigieren sei(); // Timerinterrupt kann hier schon wieder aktiviert werden
for(char i=0; i<sizeof(ti)/sizeof(uint32_t); tiptr++) { if(*tiptr>0x80000000) *tiptr-=0x80000000;
else *tiptr=0;
} } sei(); // Wenn nichts zu korrigieren gibt: hier Ints wieder aktivieren
// LED blinken if(ti.Akt>ti.Toggle) { LedPin = !LedPin; ti.Toggle = ti.Toggle+500; // Blinkfrequenz 1 Hz = 500ms an, 500ms aus } // Start-Zeitpunkt erreicht if(ti.Akt>ti.Start) { run = 1; ti.Start = ti.Akt + 100000; ti.Stop = ti.Akt + 1000; } // Stop-Zeitpunkt erreicht if(ti.Akt>ti.Stop) { run = 0; ti.Start = ti.Akt + 2000; ti.Stop = ti.Akt + 100000; } // Beispiel: Zeit ermitteln für Port-Pin high PortPinAkt = Portpin_Einlesen(): if (PortPinAkt!=PortPinAlt) { // Flanke am Portpin? if (PortPinAkt) // steigende Flanke ti.Rise = ti.Akt; // --> Zeit merken else // fallende Flanke ti.Duration = ti.Akt-ti.Rise; // --> Betätigungsdauer ausrechnen } PortPinAlt = PortPinAkt; // Pegel merken für Flankenerkennung } // Ende HAUPTSCHLEIFE }
Hier wird in einer Interruptroutine ein Millisekunden-Zähler hochgezählt und als Zeitbasis auf eine globale Struktur abgebildet. Auf diese systemweit einmalige Zeit werden dann alle lokalen Zeiten bezogen. Bei einem drohenden Überlauf wird von allen Zeiten in der ti-Struktur ein Offset abgezogen.