Pfad:
Home ==>
Mikrobeginner ==> 7. LED mit Taster-Interrupt
This page in English (external):
Lektion 7: Eine LED blinkt mit dem Tasten-Interrupt
Mit dieser Lektion schließen wir eine Taste an den Prozessor an und verwenden den
INT0-Interrupt, um nicht per Schleifen auf Drücke auf die Taste warten zu müssen.
7.0 Übersicht
- Einführung in Tasten und die INT0-Programmierung
- Aufgabenstellung
- Hardware, Bauteile, Aufbau
- Programm
7.1.1 Tasten an Inputports
Schaltet man bei einem Portpin den Ausgangstreiber mit DDRp/portbit=0 aus, dann kann man den
Zustand des Pins im PORT PINp einlesen und abhängig von diesem Zustand Programmteile
ausführen.
Um eine LED am Portpin PB0 einzuschalten, solange ein Schalter am Portpin PB1 einschaltet
(mit der Betriebsspannung verbunden ist), wäre für die nebenstehende Schaltung das
folgende Programm nötig:
sbi DDRB,PB0 ; LED-Ausgangstreiber an PB0 einschalten
Schleife:
sbis PINB,PINB1 ; ueberspringe Instruktion, wenn Inputpin Eins ist
sbi PORTB,PORTB0 ; LED ausschalten
sbic PINB,PINB1 ; ueberspringe Instruktion, wenn Inputpin Null ist
cbi PORTB,PORTB0 ; LED einschalten
rjmp Schleife
Was geschieht, wenn der Schalter gar nicht angeschlossen oder kaputt ist? Der Eingang PB1 ist
dann offen. Er reagiert wegen seines extrem hohen Eingangswiderstandes auf alles, was sich in
seiner Umgebung signalmäßig so abspielt, z. B. elektrische Felder des
230 V-Netzes, die statische Ladung von Fingern in der Nähe oder Pegelwechsel am
Nachbarpin. Ergebnis ist Zappeln am Eingangspin und hektisches Flackern der LED.
Dagegen ist ein elektronisches Kraut gewachsen: der Pull-Up-Widerstand. Er wird gegen die
positive Betriebsspannung geschaltet, setzt den Eingangswiderstand auf 47 kOhm herab und
sorgt dafür, dass der Pin auf einem definierten Potential liegt. Der Strombedarf bei
geschlossenem Schalter ist nicht allzu hoch (0,1 mA), auch wenn mit einem
Mäuseklavier acht Eingänge gleichzeitig auf Null gezogen werden.
Da die Aufgabe von Pull-Up-Widerständen sehr oft vorkommt, gibt es die Möglichkeit,
diese prozessorintern zuzuschalten. Sie werden eingeschaltet, indem das
Datenrichtungsregister-Bit auf Null und das Ausgabeport-Bit auf Eins gesetzt wird. Z. B.
cbi DDRB,DDB1 ; Richtung = Input
sbi PORTB,PORTB1 ; Pull-Up einschalten
Warum Pull-Up und nicht Pull-Down dürfte historische Gründe haben.
Dasselbe gilt, wenn statt eines Schalters ein Taster an den Eingang angeschlossen ist. Dann
ist der Pull-Up umso wichtiger, weil der im losgelassenen Zustand ja tatsächlich
elektrisch in der Luft hängt.
7.1.2 Tasten und Schalter prellen
Eine unangenehme Eigenschaft ist, dass mechanische Tasten und Schalter immer prellen. Das
heißt, sie schließen und öffnen nicht mit einem Mal sondern mehrmals. Das
sorgt im Zeitbereich bis 10 oder 20 ms für einen echten Signalschwarm.
Programmtechnisch bedeutet das, dass die Software auf solche Schwärme vorbereitet werden
muss, damit sie von nachfolgenden Signalen nicht irritiert ist.
Das Prellen spielt erst bei der nächsten Lektion eine Rolle.
7.1.3 Der INT0-Interrupt
Der INT0-Interrupt überwacht den entsprechenden INT0-Eingang (beim ATtiny13 Pin6) auf
auftretende Pegel und Pegelflanken und verzweigt zum INT0-Interrupt-Vektor, wenn der
entsprechende Interrupttyp einschaltet ist. Die mit ISC01 und ISC00 auswählbaren Pegel
und Flanken sind folgende:
ISC01 | ISC00 | Interruptauslösung |
0 | 0 | Niedriger Pegel löst Interrupt aus |
0 | 1 | Jeder Wechsel des Pegels löst Interrupt aus |
1 | 0 | Fallender Pegel löst Interrupt aus |
1 | 1 | Steigender Pegel löst Interrupt aus |
Das ist ziemlich fatal, weil der Defaultwert ist, dass Nullpegel Interrupts auslösen. Das
bedeutet, dass versehentliches Aktivieren des Interrupts in diesem Modus zu einer Dauerblockade
des Prozessors führen: der Interrupt schlägt sofort wieder zu, wenn der letzte fertig
bearbeitet ist, solange der Eingang Null ist. Da dieser INT0-Interrupt auch noch die höchste
Priorität hat, kommt kein anderer mehr durch. Damit kriegt man einen AVR zur Totalblockade
mit nachhaltiger Arbeitsverweigerung.
Die ISC-Bits liegen im gleichen Port wie das SE-Bit zum Schlafen. Die Pegelauswahl erfolgt
daher immer zusammen mit dem SE-Bit.
Und so kriegt man den INT0 dazu, Interrupts anzufordern: Eins-setzen des INT0-Bits im Port
GIMSK. Die Instruktion dafür ist:
ldi R16,1<<INT0 ; INT0-Bit setzen
out GIMSK,R16 ; und in General Interrupt Mask schreiben
Im Bereich der Interrupt-Vektoren steht der INT0-Interrupt an oberster Stelle hinter dem
Reset-Vektor, mit höchster Priorität. Im ATtiny13:
.CSEG ; Assemblieren in den Flashspeicher (Code Segment)
.ORG 0 ; Adresse auf Null (Reset- und Interruptvektoren beginnen bei Null)
rjmp Start ; Reset Vektor, Sprung zur Initiierung
rjmp Int0_Isr ; INT0-Int, aktiv
reti ; PCINT-Int, nicht aktiv
reti ; TIM0_OVF, nicht aktiv
reti ; EE_RDY-Int, nicht aktiv
reti ; ANA_COMP-Int, nicht aktiv
reti ; TIM0_COMPA-Int, nicht aktiv
reti ; TIM0_COMPB-Int, nicht aktiv
reti ; WDT-Int, nicht aktiv
reti ; ADC-Int, nicht aktiv
;
; Interrupt-Service-Routinen
;
Int0_Isr: ; INT0-ISR
in R15,Sreg ; SREG retten
[...] ; weitere Aktionen
out SREG,R15 ; SREG wieder herstellen
reti ; zurueckkehren
;
; Programmstart beim Reset
;
Start:
; [Hier beginnt das Programm]
Das war es schon und es kann an die Aufgabe gehen.
Das hier ist die komplexe Aufgabe:

Ein Tastersignal startet eine Signalfolge der LED, die zwei kurze und ein drittes längeres
Signal mit dem dargestellten Timing umfasst. Die Lösung soll prell-unempfindlich sein.
7.3.1 Hardware
Die Duo-LED ist an den Portbits PB2 (Anode rot) und PB0 (Anode grün) angeschlossen. Die
Ausgangssignale an beiden Portpins zeigt die Tabelle.
Die Taste ist mit einem Anschluss an den INT0-Eingang (PB1) angeschlossen, der zweite Anschluss
liegt auf Masse (Minus).
7.3.2 Bauteile
Duo-LED
Die Duo-LED enthält zwei LED, eine rote und eine grüne. Der längere
Anschlussdraht ist die Anode der roten LED und die Kathode der grünen.
Taster
Das ist ein Taster. Jeweils zwei seiner vier Anschlussstifte sind verbunden, Drücken der
Taste schließt die Anschlüsse.
7.4.1 Ablauf
Um die Aufgabe zu lösen ist ein klarer Ablauf aufzuzeichnen. In diesem Fall ist er
offenkundig so:
Noch ein Wort darüber, warum Assemblerprogrammierer Zähler immer rückwärts
laufen lassen: erreicht er beim Dekrementieren die Null, wird die Z-Flagge gesetzt. Und die
lässt sich mit "BREQ" und "BRNE" bequem zum Verzweigen benutzen.
Liefe der Zähler aufwärts, müsste mit "cpi rCntDwn,7" und dann erst
könnte mit "breq Label" verzweigt werden. Abwärts spart eine Instruktion.
Um die Farben der LED zu den richtigen Zeiten zu wechseln, verwenden wir einen
Abwärtszähler, rCntDwn. Der Zähler beginnt bei sechs und muss alle
0,25 Sekunden abwärts zählen. Bei den Zählerständen 5 und 3 muss auf
Grün umgeschaltet werden. Ist der Zähler Null, dann endet der Zyklus, die LED werden
abgeschaltet und die T-Flagge auf Null gesetzt.
7.4.2 Ablaufdiagramme
Die T-Flagge ist ein frei verfügbares Bit 6 im Statusregister (siehe Lektion 6).
Es lässt sich mit "SET" auf Eins setzen und mit
"CLT" löschen. Mit den bedingten Sprungbefehlen
"BRTS" und "BRTC" lassen sich
Verzweigungen realisieren, wenn das T-Bit geSetzt oder geCleared ist. Eine Besonderheit tritt
auf, wenn die T-Flagge in einer Interrupt-Service-Routine geändert werden soll: mit dem
Sichern und Wiederherstellen des Statusregisters würde das zwischendurch gesetzte
T-Flag wieder überschrieben. In unserem Fall macht das nix, weil der INT0-Interrupt
nichts weiter tun muss als nur die T-Flagge setzen. Da die anderen Bits im SREG in der Routine
nicht verändert werden, gibt es diesmal keinen Konflikt.
Um das Timing exakt hinzukriegen, wählen wir einen Vorteiler von 8, einen CTC-Teiler von
150 und einen Software-Zähler bis 250. Das ergibt genau 0,25 Sekunden. Da wir noch
ein paar Takte Verwaltung zu tun haben, ist die Genauigkeit nur scheinbar, aber die
Abweichungen sind hinnehmbar. Und besser und eleganter als elendig lange
Verzögerungsschleifen allemal.
Das ist der gesamte Ablauf ohne die beiden Interrupt-Service-Routinen. Diese beiden laufen
separat ab und kommunizieren ihre erreichten Zustände (die Taste wurde gedückt,
der Timer hat 0,25 s erreicht) über zwei Flaggen (T, bTo) an das Hauptprogramm, das
schlafend auf diese beiden Ereignisse wartet.
Das Programm startet mit den Reset- und Interruptvektoren. Im Hauptprogramm folgt das
Initiieren der Portpins und das Ermöglichen des INT0-Interrupts. Danach tritt der
schlafende Wartezustand ein. Tritt ein Tasteninterrupt ein, dann wird das nach dem Aufwachen
dadurch bemerkt, indem das T-Flag gesetzt ist. Damit das Setzen der T-Flagge nicht erneut zu
einem Neustart einer schon ablaufenden Blinksequenz führt, muss zuerst geprüft werden,
ob eine solche Sequenz bereits gestartet ist und gerade abläuft. Das wird am Registerinhalt
des Countdown-Zählers "rCntDwn" erkannt. Ist er nicht Null, wird die folgende
Prüfung auf eine gesetzte T-Flagge übersprungen. Wenn nicht, wird T geprüft. Ist
T gesetzt, wird nun eine Blinksequenz gestartet. Dazu wird beim Timer
- der CTC-Wert in das Vergleichsregister geschrieben,
- der CTC-Modus in das Kontrollregister A geschrieben,
- mit einem Vorteiler von 8 im Kontrollregister B gestartet, und
- der Vergleicher-A-Interrupt ermöglicht.
Die LED wird auf rot geschaltet und der Countdown-Zähler mit 6 gestartet.
Sind die vorgewählte Anzahl Timer-Interrupts eingetreten, setzt der Timer die bTo-Flagge.
Ist bTo gesetzt, wird in der Hauptprogrammschleife die nächste Phase des Ablaufs
bearbeitet. Hat der Ablauf rCntDwn Null erreicht, wird der Timer und die LED abgeschaltet.
In diesem Zustand verharrt das Programm schlafend bis zum nächsten Tastendruck.
7.4.3 Das Programm
Das hier ist das Programm (den Quellcode gibt es hier):
;
; *************************************
; * Taster mit INT0-Interrupt *
; * (C)2016 by www.gsc-elektronic.net *
; *************************************
;
.NOLIST
.INCLUDE "tn13def.inc"
.LIST
;
; ------ Programmablauf ---------------
;
; Das Programm wartet auf negative Flanken
; am INT0-Eingang. Tritt eine solche ein,
; wird mittels der T-Flagge der folgende
; Ablauf gestartet:
; - Ein Abwaertzaehler wird auf 6 gesetzt.
; - Die LED wird auf Rot geschaltet.
; - Der Timer wird im CTC-Modus mit einem
; Teiler von 150 im Compare-A-Register
; gestartet.
; Beim Compare-A-Interrupt wird ein
; Softwarezaehler von 250 auf Null ab-
; waerts gezaehlt. Wird Null erreicht,
; wird der Zaehler mit 250 neu gestartet
; und die bTO-Flagge gesetzt.
; Ist die bTO-Flagge gesetzt, wird sie
; wieder rueckgesetzt und der Abwaerts-
; zaehler um Eins vermindert. Bei den
; folgenden Zaehlerstaenden werden diese
; Operationen vorgenommen:
; 5, 3: LED auf gruen
; 4, 2: LED auf rot
; 0: Ablauf beenden, Zaehler abschalten,
; T-Flagge ruecksetzen
;
; --------- Register ------------------
; frei R0 .. R14
.def rSreg = R15 ; Sichern Statusregister
.def rmp = R16 ; Vielzweckregister
.def rimp = R17 ; Vielzweckregister Interrupts
.def rFlag = R18 ; Flaggenregister
.equ bTo = 0 ; Timeout-Flagge Timer
.def rCntDwn = R19 ; Count Down
.def rCtcCnt = R20 ; CTC-Count-Down
; frei R21 .. R31
;
; --------- Ports ---------------------
.equ pOut = PORTB ; Output-Port
.equ pDir = DDRB ; Direction-Port
.equ pIn = PINB ; Input-Port
.equ bARO = PORTB2 ; Anode LED rot Ausgabe
.equ bKRO = PORTB0 ; Kathode LED rot Ausgabe
.equ bPuO = PORTB1 ; Pull-Up Taster Ausgabe
.equ bARD = DDB2 ; Anode LED rot Richtung
.equ bKRD = DDB0 ; Kathode LED rot Richtung
;
; --------- Timing --------------------
; Clock = 1200000 Hz
; Prescaler = 8
; CTC-Teiler = 150
; Counter = 250
; ----------------------------
; Signaldauer = 0,250 Sekunden
;
; --------- Konstanten ----------------
.equ cCtcCmp = 149 ; CTC-Teiler - 1
.equ cCtcInt = 250 ; Int-Zaehler
;
; --------- Reset- und Int-Vektoren ---
.CSEG ; Assemblieren in den Flashspeicher (Code Segment)
.ORG 0 ; Adresse auf Null (Reset- und Interruptvektoren beginnen bei Null)
rjmp Start ; Reset Vektor, Sprung zur Initiierung
rjmp Int0_Isr ; INT0-Int, aktiv
reti ; PCINT-Int, nicht aktiv
reti ; TIM0_OVF, nicht aktiv
reti ; EE_RDY-Int, nicht aktiv
reti ; ANA_COMP-Int, nicht aktiv
rjmp Tc0CmpA ; TIM0_COMPA-Int, aktiv
reti ; TIM0_COMPB-Int, nicht aktiv
reti ; WDT-Int, nicht aktiv
reti ; ADC-Int, nicht aktiv
;
; Interrupt-Service-Routinen
;
; INT0 wird von fallenden Flanken am Tasten-
; eingang ausgeloest. Das T-Flag wird gesetzt.
;
Int0_Isr: ; INT0-ISR
set ; setze T-Flagge
reti ; zurueckkehren
;
; TC0-Compare-A wird bei Ueberschreitung des Compare-A-
; Wertes ausgeloest. Der Zaehler rCtcCnt wird abwaerts
; gezaehlt. Erreicht er Null, wird er neu gestartet
; und die bTO-Flagge gesetzt.
;
Tc0CmpA: ; TC0 Compare A ISR
in rSreg,SREG ; sichere SREG
dec rCtcCnt ; CTC-Zaehler abwaerts
brne Tc0CmpA1 ; noch nicht Null
sbr rFlag,1<<bTo ; Timeout-Flagge setzen
ldi rCtcCnt,cCtcInt ; Neustart Zaehler
Tc0CmpA1:
out SREG,rSreg ; SREG wieder herstellen
reti
;
; Programmstart beim Reset
;
Start:
; Stapel-Init
ldi rmp,LOW(RAMEND)
out SPL,rmp;
; LED-Ausgaenge initiieren
ldi rmp,(1<<bARD)|(1<<bKRD) ; Ausgaenge
out pDir,rmp ; an Richtungsport
; Pull-Up-Widerstand einschalten
sbi pOut,bPuO ; Pull-up an
; Anfangszustand setzen
clr rCntDwn ; Countdown-Zaehler aus
clt ; Busy-Flagge aus
; Schlafen ermoeglichen, Ext. Int
ldi rmp,(1<<SE)|(1<<ISC01) ; Schlafen, fallende Flanke
out MCUCR,rmp
; INT0 ermoeglichen
ldi rmp,1<<INT0 ; INT0-Interrupts an
out GIMSK,rmp ; in General Interrupt Maske
; Interrupts einschalten
sei ; Interrupt-Flagge setzen
Schleife:
sleep
nop
tst rCntDwn ; Countdown-Zaehler = Null?
brne Schleife1 ; nein, nicht starten
brtc Schleife1 ; springe bei T = 0
rcall Starten ; Sequenz starten
Schleife1:
sbrc rFlag,bTo ; Springe, wenn CTC-Flagge aus
rcall Countdown ; abwaerts bearbeiten
rjmp Schleife
;
; Sequenz starten
; - Startwert Abwaertszaehler setzen
; - LED auf rot
; - bTO-Flagge loeschen
; - Timer 0 im CTC-Modus starten
;
Starten:
ldi rCntDwn,6 ; Startwert erster Zyklus
ldi rmp,(1<<bPuO)|(1<<bARO) ; rote LED und Pullup
out pOut,rmp ; an Ausgangsport
ldi rCtcCnt,cCtcInt ; Neustart CTC-Int-Zaehler
cbr rFlag,1<<bTo ; Timeout-Flagge Null
ldi rmp,cCtcCmp ; CTC-Teilerwert
out OCR0A,rmp ; in Vergleichsregister A
ldi rmp,1<<WGM01 ; TC0 als CTC mit Compare A
out TCCR0A,rmp ; in Kontrollregister A
ldi rmp,1<<CS01 ; Prescaler = 8
out TCCR0B,rmp ; in Kontrollregister B
ldi rmp,1<<OCIE0A ; Compare-A-Interrupt
out TIMSK0,rmp ; in TC0-Int-Maske
ret ; Fertig
;
; 250 ms vorbei, naechste Stufe
; - bTO-Flagge loeschen
; - rCntDwn vermindern
; - bei 0: LED ausschalten, Timer aus
; - bei 5 und 3: LED gruen schalten
; - bei 4 und 2: LED rot schalten
;
Countdown:
cbr rFlag,1<<bTo ; Flagge ruecksetzen
dec rCntDwn ; naechste Stufe abwaerts
breq CountdownAus ; beendet, ausschalten
cpi rCntDwn,5 ; Zyklus = 5?
breq CountDownGreen ; Lampe auf Gruen
cpi rCntDwn,3 ; Zyklus = 3?
breq CountDownGreen
; LED auf rot schalten
ldi rmp,(1<<bPuO)|(1<<bARO) ; Rot und Pullup
out pOut,rmp ; an Ausgabeport
ret
CountDownGreen: ; LED auf Gruen
ldi rmp,(1<<bPuO)|(1<<bKRO) ; Gruen und Pullup
out pOut,rmp ; an Ausgabeport
ret
CountdownAus: ; Beendet, alles ausschalten
clr rmp ; Zaehler ausschalten
out TCCR0B,rmp ; in Kontrollregister B
out TIMSK0,rmp ; Timer-Int aus
ldi rmp,1<<bPuO ; alles aus ausser Pullup
out pOut,rmp ; LED aus
clt ; T-Flagge aus
ret
;
; Ende Quellcode
;
Zwei Instruktionen sind neu:
7.4.4 Simulation der Vorgänge
Um diese Abläufe zu simulieren füttern wir den Quellcode in
avr_sim
und steppen uns durch die einzelnen Sequenzen des Quellcodes.
Das ist der Zustand des Port B nach dem Init:
- Die zwei In-/Output-Pins (I/O-Pin) PB0 und PB2 sind als
Ausgänge geschaltet, ihre Richtungsbits sind gesetzt
ihr Treiber ist eingeschaltet und die Ausgänge sind
Low. Die Duo-LED zwischen PB0 und PB2 ist daher abgeschaltet.
- An PB1, an dem der Taster angeschlossen ist, ist das
Richtungsbit Null und der Portausgang PORTB1 Eins. Das
schaltet den internen Pullup-Widerstand an. Beim Lesen
des Portregisters PINB käme daher bei diesem Bit
eine Eins heraus, solange die Taste nicht gedrückt
wird.
- An PB1 ist auch der INT0-Eingang lokalisiert. Die
Init-Software hat für fallende Flanken (das sind
Übergänge von 1 auf 0) den INT0-Interrupt
ermöglicht. Er wird ausgeführt, wenn der
Taster geschlossen wird.
Nun geht der Herr Professor in den Idle-Schlafmodus und Port B
wartet auf Signale an PB1.
Durch Klicken auf das Portbit INT0 lösen wir eine
INT0-Interruptanforderung aus. Sofern kein anderer Interrupt
gerade bearbeitet wird, wird die Anforderung mit der nächsten
Instruktion ausgeführt. Selbst wenn nun noch andere
Interruptanforderungen vorlägen, käme INT0 trotzdem
dran, denn er hat die höchste Priorität (er steht in
der Vektorliste ganz oben).
Der Controller wacht damit auf, legt die derzeitige
Ausführungsadresse auf dem Stapel ab und verzweigt zur
INT0-Vektoradresse.
Die einzige Tätigkeit in der INT0-Serviceroutine ist das
Setzen der T-Flagge im Statusregister SREG und startet damit
den Ablauf.
Nach dem Aufwachen bemerkt der Herr Professor, dass die
T-Flagge gesetzt ist und er ruft mit RCALL StartSeq
die Unterroutine auf, die den Ablauf startet. Durch den
RCALL wird der gerade durch die Rückkehr vom
INT0-Interrupt wieder erhöhte Stapelzeiger wieder um
zwei Positionen niedriger, weil die Rückkehradresse
auf dem Stapel abgelegt wird.
Die Unterroutine StartSeq hat zuerst geprüft, ob der
Abwärtszähler auf Null steht. Wenn nicht, wird
wieder schlafen gelegt. Ist er auf Null, wird der
Abwärtszähler in R19 auf 6 gesetzt, um eine
Sequenz zu beginnen. Der CTC-Zähler in R20 wurde auf
250 gesetzt, damit 250 CTC-Sequenzen ausgeführt
werden. Man beachte, dass die T-Flagge gesetzt bleibt.
Sie wird erst gelöscht, wenn die Zählsequenz auf
Null läuft. Weitere INT0-Interrupts tun daher nichts,
weil der Abwärtszähler ab jetzt nicht mehr Null
ist.
Die Routine StartSeq hat den Timer TC0 in den CTC-Modus
gebracht. Sein TOP-Wert ist 149, daher startet der Timer
nach 150 Zählimpulsen neu. Der Prozessortakt wird
mit dem Prescaler durch 8 geteilt, daher dauert der CTC-Zyklus
8 * 150 / 1.200.000 = 1,00 ms.
Nachdem TC0 150 erreicht hat (und neu gestartet wurde)
löst er einen Compare Match A aus und verzweigt zum
CMP0A-Interrupt-Vektor.
Wie vorherberechnet erfolgt der erste Compare Match Interrupt
nach 1.0025 ms. Die "Überzeit" von
2,5 µs ist dadurch verursacht, dass die
Rücksprungadresse auf dem Stapel abgelegt und das I-Flag
im SREG gelöscht werden muss, bevor die Vektoradresse
angesprungen werden konnte. Misst man von Sprung bis Sprung
immer bei der gleichen Instruktion, verschwindet diese
Extrazeit und die Millisekunde stimmt genau. Für das
menschliche Auge ist aber der kleine Unterschied beim
allerersten Zyklus ohnehin nicht sichtbar.
Hier sind 250 CTC-Zyklen abgelaufen. Die bTO-Timeout-Flagge
wird nun gesetzt um das Time-Out zu signalisieren. Die
abgelaufene Zeit seit dem Timer-Start is ziemlich genau.
Nun, da die bTO-Flagge gesetzt ist, ist es Zeit die Farbe
der Duo-LED von rot nach grün umzukehren. Hier wechseln
die Bits PB0 und PB2 und die machen das schon.
Das wiederholt sich fünf mal, davon einmal ohne
Farbwechsel.
Nach 1,5 Sekunden ist der ganze Zyklus vorbei, die T-Flagge
wird gelöscht und es kann mit Tastendruck von vorne
beginnen.
©2016 by http://www.gsc-elektronic.net