Die Applikation verfügt durch die Basisklasse AppKernel bereits über einen Timer. Für den Applikations-Timer wird in der Regel der einfachste Timer also Timer0 verwendet. Dieser wird durch den AppKernel so initialisiert, dass er das System möglichst genau mit 10 Millisekunden triggert. Das Triggersignal, welches der Timer-Interrupt auslöst wird an die Applikation und alle Applikationsmodule als onTimer-Nachricht verteilt. Vergleichen Sie dazu die folgende Darstellung.
Es ist deutlich zu sehen, dass die Nachrichten onTimer10ms, onTimer100ms und onTimer1s aus der Interruptverarbeitung heraus gesendet werden. Das bedeutet für den Programmierer, der diese Nachrichten verarbeitet, dass er immer nur kurze Anweisungsfolgen und niemals Warteschleifen oder Unendlichschleifen bzw. Polling in die onTimer-Nachrichten programmieren darf. Als Programmierer der Applikation kann man sich durch überschreiben der Operationen an diese Nachrichten „ranhängen“. Das geschieht, indem man die zu überschreibende Operation exakt so notiert wie diese in der Basisklasse formuliert wurde. Das sind für die Timer-Nachrichten die Operationen:
Damit ist es sehr einfach möglich, zyklische Aufgaben in genauen Zeitabständen durchzuführen. Das Blinken einer LED ist solch eine zyklische Aufgabe. Diese wurde zuvor dadurch gelöst, dass innerhalb der Operation onWork die Zeitabstände für das Umschalten der LED durch Wartefunktionen realisiert wurden. Das ist zwar einfach zu Programmieren und auch einfach zu verstehen, aber gleicht einer Autofahrt mit angezogener Handbremse. Denn Wartefunktionen sind Schleifen, die solange nichts tun, bis die geforderte Zeit um ist. Es wird also Prozessorzeit (Leistung) vergeudet. Der Timer läuft absolut autonom. Während dieser vor sich hin zählt, kann der Controller jede Menge anderer Aufgaben ohne Leistungsverlust ausführen. Wir sprechen hier bei 10 Millisekunden und 3,6864 Mhz von immerhin über 3600 Prozessortakten. Also gut und gerne 3000 Assemblerbefehlen, die in der Zeit vom AVR ausgeführt werden. Das sind dann im Übrigen vor allem die Anweisungen in der Operation onWork. Für die folgende Übung verbinden Sie Port B Bit 0 mit der roten LED.
Legen Sie jeweils immer ein neues kleines Programm an. Überprüfen Sie die Spracheinstellungen für AVR C++ und die Controllereinstellungen für den ATmega8. Laden Sie die Grundstruktur einer AVR C++ Anwendung. Es ist die Methode onTimer1s zu überschreiben und in dieser das Umschalten der LED zu programmieren.
Das Konzept:
///////////////////////////////////////////////////////////////////// // Blinklicht 2 ENTWURF ///////////////////////////////////////////////////////////////////// class Application : public Controller { public: void onStart() { // Port B Bit 0 = Ausgang } public: void onWork() { // leer } public: void onTimer1s() // überschriebene Operation { // LED umschalten } } app; /////////////////////////////////////////////////////////////////////
Die Realisierung:
///////////////////////////////////////////////////////////////////// // Blinklicht 2 ///////////////////////////////////////////////////////////////////// class Application : public Controller { public: void onStart() { ddrB.bit0=1; // Port B Bit 0 = Ausgang } public: void onWork() { // leer } public: void onTimer1s() // überschriebene Operation { portB.bit0 ^= 1; // LED umschalten } } app; /////////////////////////////////////////////////////////////////////
Kompilieren, linken und übertragen Sie die Anwendung auf den Controller. Die LED blinkt jetzt sehr langsam, übrigens mit 0,5 Hz ( 1s = 1 Halbzyklus, 2s = voller Zyklus). Damit diese schneller blinkt, bietet es sich an die Operation onTimer100ms zu überschreiben. Das ist dann 10 mal so schnell, also 5 Herz.
Die onTimer-Nachrichten stehen mit 10, 100 und 1000 Millisekunden nur in einem recht beschränktem Raster zur Verfügung. Dem ist mit recht einfachen Mitteln Abhilfe zu schaffen. Durch das Anlegen einer eigenen Zählvariable können wir auf der Basis der drei Timer-Nachrichten fast jeden beliebige Abstufung realisieren. Die nächste Übung soll die LED mit genau 1 Herz blinken lassen. Dazu ist die Operation onTimer1s nicht geeignet, deshalb benutzen wir die Nachricht onTimer100ms und zählen einfach bis fünf ( 5*100 ms = 0,5 s Halbzyklus = 1 s der ganze Zyklus).
Ein neues Programm anlegen und das Konzept als Kommentare eintragen.
///////////////////////////////////////////////////////////////////// // Blinklicht 4 ENTWURF ///////////////////////////////////////////////////////////////////// class Application : public Controller { // Attribute ................................................ // Zähler anlegen // Operationen .............................................. public: void onStart() { // Port B Bit 0 = Ausgang // Zähler initialisieren } public: void onWork() { // leer } public: void onTimer100ms() // überschriebene Operation { // Zähler + 1 // Wenn Zähler >= 5 // dann LED umschalten // und den Zähler zurücksetzen } } app; /////////////////////////////////////////////////////////////////////
Danach die entsprechenden Befehle eingeben.
///////////////////////////////////////////////////////////////////// // Blinklicht 4 ///////////////////////////////////////////////////////////////////// class Application : public Controller { // Attribute ................................................ protected: uint8_t counter; // Zähler anlegen // Operationen .............................................. public: void onStart() { ddrB.bit0 = 1; // Port B Bit 0 = Ausgang counter = 0; // Zähler initialisieren } public: void onWork() { // leer } public: void onTimer100ms() // überschriebene Operation { counter++; // Zähler + 1 if( counter >= 5 ) // Wenn Zähler >= 5 { portB.bit0 ^= 1; // dann LED umschalten counter=0; // und Zähler zurücksetzen } } } app; /////////////////////////////////////////////////////////////////////
Kompilieren, linken und übertragen Sie die Anwendung auf den Controller.