Home ==> Mikrobeginner ==> 7. LED mit Taster-Interrupt
ATtiny13

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

  1. Einführung in Tasten und die INT0-Programmierung
  2. Aufgabenstellung
  3. Hardware, Bauteile, Aufbau
  4. Programm

7.1 Einführung in Tasten und die INT0-Programmierung

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.

Schalter 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. Pull-Up 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

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

ISC-Bits Der INT0-Interrupt überwacht den entsprechenden INT0-Eingang (beim ATtiny13 Pin6) auf auftretende Pegel und Pegelflanken und verweigt zum INT0-Interrupt-Vektor, wenn der entsprechende Interrupttyp einschaltet ist. Die mit ISC01 und ISC00 auswählbaren Pegel und Flanken sind folgende:
ISC01ISC00Interruptauslösung
00Niedriger Pegel löst Interrupt aus
01Jeder Wechsel des Pegels löst Interrupt aus
10Fallender Pegel löst Interrupt aus
11Steigender 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.

INT0 im GIMSK Und so kriegt man den INT0 dazu, Interrupts anzufordern: Eins-setzen des INT0-Bits im Port MCUCR. 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.

Home Top Einführung Aufgabe Hardware Programm


7.2 Aufgabenstellung

Das hier ist die komplexe Aufgabe:

Signalfolge

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.

Home Top Einführung Aufgabe Hardware Programm


7.3 Hardware, Bauteile und Aufbau

7.3.1 Hardware

Schaltbild 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
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
Taster von unten Taster von oben Das ist ein Taster. Jeweils zwei seiner vier Anschlussstifte sind verbunden, Drücken der Taste schließt die Anschlüsse.


Home Top Einführung Aufgabe Hardware Programm


7.4 Programm

7.4.1 Ablauf

Um die Aufgabe zu lösen ist ein klarer Ablauf aufzuzeichnen. In diesem Fall ist er offenkundig so:

Zähler 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

INT0 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.

CTC-Int 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.

Ablauf 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 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 Tatendruck.

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
;
; --------- 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, 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
	set ; setze T-Flagge
	reti ; zurueckkehren
;
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
;
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
;
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:

Home Top Einführung Aufgabe Hardware Programm


©2016 by http://www.gsc-elektronic.net