This is an old revision of the document!
Necessary knowledge: [HW] Controller module, [AVR] Counters/Timers, [LIB] Pins, [LIB] Delay, [LIB] Timers, [PRT] Software delay
Die Softwareverzögerung ist nicht die einzige Methode Pausen zu erzeugen. Das gleiche kann mit Timern gemacht werden. Timer sind Hardware welche mit einer bestimmten Frequenz rauf oder runter zählen. Die Taktfrequenz des Timers kann über die Frequenz des Microcontrollers oder einem externen Taktgeber erzeugt werden. Normalerweise kann die Taktfrequenz mit einem Multiplikator dividiert werden um eine geringere Frequenz zu erreichen - das wird mit einem Prescaler gemacht. Wichtig ist jedoch, dass die Taktfreqzenz des Timers linear mit der Zeit ist. Die Zeit kann berechnet werden, in dem die Periode der Taktfrequenz mit dem Wert des Timers multipliziert.
AVR counter können eingestllt werden um ein Signal bei einem Overflow machen oder einen bestimmten Vergleichstreffer. Ein Overflow passiert, wenn der Counter seine maximalen Wert erreicht hat und er wieder von 0 anfängt. Mit einem vorgegebenen Wert vergleicht der Counter den Wert des Timers bei jedem anstieg mit dem gegeben Wert des Nutzers. Falls einer der Fälle eintritt werden die Bits des Statusindexes des AVR automatisch High gesetzt.
Um eine Verzögerung mit einem Timer zu generieren, muss man nur den Timer setzen und darauf warten, dass das Statusbit High geht. Anders als bei der Softwareverzögerung ist die Arbeit des Timers nicht vom Compiler abhängig, und macht ihn so zuverlässiger. Zur gleichen Zeit wird die Diversität (bzw. Komplexität) des Setups des AVR Counters recht mühsam. Je nach Taktsignal des Microcontroller kann es sein, dass es nicht exakt mit der gewünschten Verzögerungsperiode dividiert und die Verzögerung ungenau wird.
Der Programmcode unterhalb ist eine Verzögerungsfunktion die auf einen Timer basiert und etwas vereinfacht wurde. Das Prinzip des Zählens ist das gleiche wie bei einer Softwareverzögerungsfunktion - eine gewünschte 1ms Verzögerung wird generiert. Die Verzögerung wird mit einem 8-Bit ATmega128 Counter 0 erzeugt. Die Taktfrequenz, wie zuvor schon berechnet, ist 14,7456 MHz und muss mindestens 64mal dividiert werden, so dass der Counter seinen Overflow nicht in einer Millisekunde erreicht. Der Wert den der Zähler haben muss, so dass der Overflow nach 1ms passiert wird mit einer variablen timer_start dargestellt. F_CPU welche einer konstante in der Macrosprache ist, gibt die Taktfrequenz in Hz an. Die Taktfrequenz sollte 25,6 sein, aber da Kommazahlen nicht genutzt werden können wird der Startwert auf 26 eingestellt. Leider entsteht hier eine gewisse Ungenauigkeit, welche aber sehr gering ist (- 1,7µs).
In dem Zyklus wird der Counter initialisiert und die Overflow Flagge genullt (in dem man eine 1 reinschreibt). Dann wartet man bis der Counter vom Startwert bis 256 gezählt hat, also zum Overflow. Wenn der Overflow stattfindet wird die Flagge hochgesetzt und eine Verzögerung von einer 1ms hat stattgefunden. Am Ende der Funktion wird der Timer gestoppt.
// // Hardware delay in milliseconds. // void hw_delay_ms(unsigned short count) { // Calculating the initial value of the timer. register unsigned char timer_start = 256 - F_CPU / 1000 / 64; // Starting the timer. timer0_init_normal(TIMER0_PRESCALE_64); // Counting the variable of the delay to the 0. while (count-- > 0) { // Initializing the timer. timer0_set_value(timer_start); // Zeroing the overflow flag. timer0_overflow_flag_clear(); // Waiting for overflow. while (!timer0_overflow_flag_is_set()) { asm volatile ("nop"); } } // Zeroing the overflow flag. timer0_overflow_flag_clear(); // Stoping the timer. timer0_stop(); }
Das folgende Programm ist ähnlich wie das im Beispiel für die Softwareverzögerung, In der kürzeren 100ms halb-Periode wird die LED angeschaltet und in der längeren 900ms halb-Periode wird die LED ausgeschaltet. Das Resultat: die LED blinkt jede Sekunde. Leider ist in diesem Beispiel die Periode ebenfalls nicht exakt eine Sekunde, da das Ausführen andere Funktionen des Programms ebenfalls Zeit verbraucht. Um ein exaktes Timing zu nutzen, muss ein 16-Bit Timer mit Interrupts genutzt werden.
// // Demonstration program of hardware delay of the HomeLab. // The Program blinks LED for a moment after every ~1 second. // #include <homelab/pin.h> #include <homelab/delay.h> // // Determining the pin of the Test LED. // pin debug_led = PIN(B, 7); // // Main program. // int main(void) { // Setting the pin of the LED as output. pin_setup_output(debug_led); // Endless loop. while (true) { // Lighting the LED. pin_clear(debug_led); // Hardware delay for 100 milliseconds. hw_delay_ms(100); // Switch off of the LED. pin_set(debug_led); // Hardware delay for 900 milliseconds. hw_delay_ms(900); } }