Notwendiges Wissen: [HW] Controller module, [HW] User Interface Module, [AVR] Interrupts, [AVR] Counters/Timers, [LIB] Pins, [LIB] Delay, [LIB] Timers, [PRT] Software delay
Ziel dieses praktischen Beispiels ist es, die Verwendung von Interrupts an Beispiel von Timern darzustellen. Interrupts sind Programmteile welche auf Ereignisse, die in einem Mikrocontroller stattfinden, reagieren. Normalerweise dienen sie dazu, schnell auf ein Event zu reagieren. Sie können jedoch auch dazu genutzt werden, um mehrere parallel ablaufende Vorgänge oder zeitlich abgestimmte Aktionen auszuführen, oder zur Energieeinsparung. Es ist beispielsweise möglich eine LED durch Interrupts blinken zu lassen, so dass die Blinkfrequenz nicht von einem Programm abhängig ist.
Das folgende Programm zeigt wie Timer eingestellt werden, um einen Interrupt auszulösen. Das Programm nutzt zwei LEDs des digitalen I/O Moduls. Der Status der roten LED wird mit einer Softwareverzögerung periodisch geändert, der Status der grünen LED wird geändert wenn ein Interrupt auftritt. Es gibt ein separates Beispiel für blinkende LEDs durch Softwareverzögerung, daher wird es an dieser Stelle nicht näher erläutert. Ziel ist es, die Bibliothek für Counter und Interrupts zu verwenden.
Zu Beginn des Programms wird der 16-Bit Timer 1 mit der Funktion timer1_init_ctc eingestellt. Dadurch wird der Timer in den CTC clear timer on compare match Modus gesetzt, wodurch sein maximaler Wert nicht 216 - 1 ist, sondern vom Benutzer eingestellt werden kann. In diesem Fall wird der maximale Wert so gesetzt dass er gleich dem Wert des ICR1 Indexes ist. Der Teiler des Timers ist 1024 und der Wert des ICR1 beträgt 14400. Bei einer Taktfrequenz von 14,7456MHz umfasst eine Periode somit genau eine Sekunde. Dieses kann mit folgender Formel berechnet werden:
f = 14745600 Hz / 1024 / 14400 = 1
Nachdem zugelassen wurde, dass ein Interrupt den maximalen Wert von Counter 1 erreicht, muss ein Interrupt auch global, also im Mikrocontroller, zugelassen werden. Dazu gibt es die Funktion sei, um sie zu untersagen die Funktion cli. Um den Programmteil für diese Funktionen und Interrupts festzulegen muss die Headerdatei avr/interrupt.h eingefügt werden. Ein Interrupt wird im Programm durch die Makrofunktion ISR definiert, deren Parameter der Name des Interruptvektors ist. Im folgenden Beispiel beträgt der Interruptvektor des maximal erreichbaren Wertes von Counter 1 TIMER1_CAPT_vect.
// // HomeLab Beispiel für blinkende LED aufgrund von Counter-Interrupts. // Zum Vergleich blinkender LEDs aufgrund von Interrupts, // gibt es parallel eine durch Softwareverzögerung blinkende LED. // #include <homelab/pin.h> #include <homelab/delay.h> #include <homelab/timer.h> #include <avr/interrupt.h> // // Festlegung der Pins der LEDs. // pin led_red = PIN(C, 5); pin led_green = PIN(C, 3); // // Interrupt // ISR(TIMER1_CAPT_vect) { // Status der grünen LED ändern. pin_toggle(led_green); } // // Hauptprogramm. // int main(void) { // LED Pins als Output setzen. pin_setup_output(led_red); pin_setup_output(led_green); // Timer in den CTC Modus setzen. timer1_init_ctc( TIMER1_PRESCALE_1024, TIMER1_CTC_TOP_ICR); // Maximaler Wert des Timers ist 14400, wodurch // die Periode 1 s lang ist. // Formel: 14,7456Mhz / 1024 = 14400 timer1_set_input_capture_value(14400); // Dem Interrupt erlauben, den Wert zu erreichen. timer1_input_capture_interrupt_enable(true); // Globalen Interrupt zulassen. sei(); // Endlosschleife. while (true) { // Softwareverzögerung 1000 ms. sw_delay_ms(1000); // Status der roten LED ändern. pin_toggle(led_red); } }
Zu Beginn des Programms sieht man, dass unabhängig davon was der Mikrocontroller gerade im Hauptprogramm macht, Interrupts stattfinden und die grüne LED blinkt.
Läuft das Programm ein paar Minuten wird ein entscheidender Aspekt deutlich, der im Rahmen der Softwareverzögerung nicht so einfach zu erkennen ist. Auch wenn die Verzögerung der blinkenden roten LED 1000 ms beträgt, ist die tatsächlich benötigte Zeit um einen Zyklus zu beenden länger. Das liegt daran, dass die Änderung vom LED-Status', die Angabe der Verzögerungsfunktion und das Beenden vom Zyklus einige Taktzyklen des Prozessors dauern. Daher sieht es so aus, als blinke die rote LED immer nach der grünen. Aus diesem Grund sollten Taktgeber oder andere präzise Vorgänge nicht mit Verzögerungen, sondern mit Timer-Interrupts ausgeführt werden.