Code:
;***** STK500 Lernprogramm Nr.4e1
;*** Aufgabe: die Taste an PinD0 schaltet die zugeordnete LED auf dem STK500
;*** 1. Tastendruck: LEDs einschalten
;*** 2. Tastendruck: LEDs ausschalten
;*** eine Timerroutine liest den TastenStatus ein
;*** zum Entprellen - muss die TimerRoutine 3mal hintereinander den gleichen Wert,
;*** einlesen um einen Tastzustand zu erkennen
;*** die Flanken der Tastzustaende werden in rTastFlanken gesammelt und schalten die LEDs um
;*** Taste nicht gedrueckt >>> log 0
;*** Taste gedrueckt >>> log 1
;
;
.include "m8515def.inc"
.def Temp = r16 ; Temporary Register
.def rTastFlanken = r0 ; Zustandsvariable - beinhaltet eine 2Bit-Zahl zur Tastzustandserkennung
.def rLED_Update_SPRR = r21 ; Sperrt die Ausfuehrung von TastenTest wenn, keine neuer TIMER1_OVL voran gegangen
;
.equ Timerwert = 6249 ; Timerschritte bis zum Überlauf 0,05 sec
;
.equ LED_PORT = PORTB ; LEDs
.equ LED_DDR = PORTB-1 ; DataDirectory fuer LEDs
;
.equ TAST_PORT= PORTD ; Tasten
.equ TAST_DDR = PORTD-1 ; DataDirectory fuer TastenEingang
.equ TAST_PIN = PORTD-2 ; TastenEingang
;
; Pinzuordnung für div. Tasten
.equ TASTE0_PIN = 0
.equ TASTE1_PIN = 1
.equ TASTE2_PIN = 2 ; die Zuordnung der PINs muss ja nicht mit der TastenNr übereinstimmen!
.equ TASTE3_PIN = 3
.equ TASTE4_PIN = 4
.equ TASTE5_PIN = 5
.equ TASTE6_PIN = 6
.equ TASTE7_PIN = 7
;
; jetzt kommen die Register, die die Z-Werte der Tasten enthalten..
.def rTASTE1_0_ZST = r15 ; enthält Zustand Taste1 im oberen Nibble und 0 im unteren Nibble
.def rTASTE3_2_ZST = r14 ; enthält Zustand Taste3 im oberen Nibble und 2 im unteren Nibble
.def rTASTE5_4_ZST = r13 ; enthält Zustand Taste5 im oberen Nibble und 4 im unteren Nibble
.def rTASTE7_6_ZST = r12 ; enthält Zustand Taste7 im oberen Nibble und 6 im unteren Nibble
;
; Vektortabelle
;
rjmp RESET ; 1 - $000 RESET External Pin, Power-on Reset, Brown-out, Reset and Watchdog Reset
reti ; 2 - $001 INT0 External Interrupt Request 0
reti ; 3 - $002 INT1 External Interrupt Request 1
reti ; 4 - $003 TIMER1 CAPT Timer/Counter1 Capture Event
rjmp TIMER1_COMPA_ISR ; 5 - $004 TIMER1 COMPA Timer/Counter1 Compare Match A
reti ; 6 - $005 TIMER1 COMPB Timer/Counter1 Compare Match B
reti ; 7 - $006 TIMER1 OVF Timer/Counter1 Overflow
reti ; 8 - $007 TIMER0 OVF Timer/Counter0 Overflow
reti ; 9 - $008 SPI, STC Serial Transfer Complete
reti ; 10 - $009 USART, RXC USART, Rx Complete
reti ; 11 - $00A USART, UDRE USART Data Register Empty
reti ; 12 - $00B USART, TXC USART, Tx Complete
reti ; 13 - $00C ANA_COMP Analog Comparator
reti ; 14 - $00D INT2 External Interrupt Request 2
reti ; 15 - $00E TIMER0 COMP Timer/Counter0 Compare Match
reti ; 16 - $00F EE_RDY EEPROM Ready
reti ; 17 - $010 SPM_RDY Store Program memory Ready
;
TBL_UEBERGANG_01:
.dw 0x0008 ; Tabellenlänge
; T = 0 T = 1
.dw 0x0200 ; Z=0 -> Z=0,Flanke=0 / Z=2,Flanke=0
.dw 0x0308 ; Z=1 -> Z=0,Flanke=1 / Z=3,Flanke=0
.dw 0x0400 ; Z=2 -> Z=0,Flanke=0 / Z=4,Flanke=0
.dw 0x0501 ; Z=3 -> Z=1,Flanke=0 / Z=5,Flanke=0
.dw 0x0602 ; Z=4 -> Z=2,Flanke=0 / Z=6,Flanke=0
.dw 0x0703 ; Z=5 -> Z=3,Flanke=0 / Z=7,Flanke=0
.dw 0x0F04 ; Z=6 -> Z=4,Flanke=0 / Z=F,Flanke=1
.dw 0x0705 ; Z=7 -> Z=5,Flanke=0 / Z=7,Flanke=0
;
RESET:
;
ldi r16, LOW(RAMEND) ;Stack initialisieren
out SPL, r16
ldi r16, HIGH(RAMEND)
out SPH, r16
;
; ldi temp,(1<<CS10)|(1<<WGM12) ; Taktfrequenz Vorteiler 1 gewaehlt (fuer Simaulatortest)
ldi temp,(1<<CS10)|(1<<CS11)|(1<<WGM12) ; Taktfrequenz mit Vorteiler 64 gewaehlt
out TCCR1B,temp
;
ldi temp,HIGH(Timerwert)
out OCR1AH,temp
ldi temp,LOW(Timerwert)
out OCR1AL,temp
;
ldi temp,(0<<COM1A1)|(0<<COM1A0)
out TCCR1A,temp
ldi temp,(1<<OCIE1A) ; Timer1 Overflow aktivieren
out TIMSK,temp
;
clr Temp ;Temp mit 0b00000000 bzw. 0x00 laden
out TAST_DDR, Temp ;PORTD als Eingang
ser Temp ;Temp mit 0b11111111 bzw. 0xFF laden
out TAST_PIN, temp ;STK500 schaltet gegen GND - Taste gedreuckt (Pin==0)
out TAST_PORT, temp ;PullUp an PortD einschalten
out LED_DDR,Temp ;PORTB als Ausgang
out LED_PORT, temp ;PORTB (LEDs) aus
;
; Tastenzustände aller Tasten auf Null setzen
clr r16 ;
mov rTASTE1_0_ZST,r16 ; Taste0 und Taste1 zurücksetzen
mov rTASTE5_4_ZST,r16 ; Taste2 und Taste3 zurücksetzen
;
sei
;????
; hier soll das Ruecksichern aus dem EEPROM erfolgen - ; lds temp, ????
; und ; out LED_PORT, temp
;!!!!
MAIN:
sbrc rLED_Update_SPRR,0 ; kein LED_Update, wenn Bit0 ==0
rcall LED_Update
rjmp MAIN
;
LED_Update:
;
; LED_PORT auslesen - mit TastenZustand verarbeiten - LED_PORT ausgeben
push temp
in temp, LED_PORT ; LED_PORT einlesen
eor temp, rTastFlanken ; toggle das Bit im LED_PORT fuer das eine Flanke in rTastFlanken ansteht
out LED_PORT,temp ; LED_PORT augeben
;????
; hier soll noch ins EEPROM gesichert werden ; sts ????, temp
; LED_PORT sichern
;!!!!
cbr rLED_Update_SPRR,1 ; loesche Bit0, um LED_Update bis zur naechsten TIMER1_ISR zu sperren
clr rTastFlanken ; setze rTastFlanken zum erneuten Beschreiben auf 0x00
pop temp
ret
;
/*-------------------------------------
INTERRUPTDIENST TIMER1_COMPA_ISR
Die Interruptdienstprogramm TIMER1_COMPA_ISR wird vom Output-Match-Interrupt von
Timer1 ausgelöst. Es liest die aktuellen Messwerte der Tasten vom TAST_PIN ab und
berechnet für jede Taste den neuen Zustand (Z-Wert).
Eingansgrössen:
keine
Ausgangsgrössen:
keine
geänderte Register
keine
Anzahl Zyklen
*/
TIMER1_COMPA_ISR:
push r25
in r25,SREG
push r1 ; Register r1 - temporaeres Register fuer Unterprogramme
push r20 ; Register r20 - temp. Bit fuer rTastFlanken(uebergibt Unterprogramm den Tastursprung)
push r16
push r17
push r18
push r19 ; Register r19 - temporaeres Register fuer swap
push zl
push zh
;
ldi zl,LOW(TBL_UEBERGANG_01) ; zh:zl := Zeiger auf Übergangstabelle ...
ldi zh,HIGH(TBL_UEBERGANG_01) ; ...wird für NEXT0_TAST_ZST gebraucht
;
; Betätigungszustand der Tasten einlesen und der Reihe nach verarbeiten
in r18,TAST_PIN ; r18 := Messwerte aller Pins
com r18 ; invertier r18
;
; Taste1 und Taste0 verarbeiten und in Register TASTE1_0_ZST
;
; Taste1 verarbeiten (!!! Achtung oberes Nibble von rTASTE1_0_ZST 0b????xxxx)
rcall CLEAR_REGISTER
ldi r20, 2 ; tmp. Tastenursprung - fuer Bitbestimmung in rTastFlanken
bst r18,TASTE1_PIN ; überträgt den Messwert von TASTE1_PIN ins T-Flag von SREG
; lade TASTE1_0_ZST in R1 und loesche MSB (oberes Nibble)
mov r1, rTASTE1_0_ZST ; temp Register fuer SET_XXX_NIBBLE - uebernimmt rTastx_y_ZST
rcall GET_HIGH_NIBBLE ; hole HIGH_NIBBLE nach r16
rcall FLANKE_SICHERN ;
andi r16,0x07 ; loesche MSB (eigentlichliches LSB) und "Flankenbit"
mov r19,r16 ; r16 := Z-Wert Taste1
rcall NEXT0_TAST_ZST ; Folgezustand für Taste1 in r16 berechnen
rcall SET_HIGH_NIBBLE ; bereite HIGH_NIBBLE fuer Ruecksichern vor
mov rTASTE1_0_ZST,r16 ; neuen Z-Wert von Taste1 in TASTE1_0_ZST speichern
;
; Taste0 verarbeiten (!!! Achtung unteres Nibble von rTASTE1_0_ZST 0bxxxx????)
rcall CLEAR_REGISTER
ldi r20, 1 ; tmp. Tastenursprung
bst r18,TASTE0_PIN ; überträgt den Messwert von TASTE0_PIN ins T-Flag von SREG
; lade TASTE1_0_ZST in R1 und loesche MSB (oberes Nibble)
mov r1, rTASTE1_0_ZST ; temp Register fuer SET_XXX_NIBBLE
rcall GET_LOW_NIBBLE ; hole LOW_NIBBLE nach r16
rcall FLANKE_SICHERN ;
andi r16,0x07 ; "Flankenbit" und MSB löschen
rcall NEXT0_TAST_ZST ; Folgezustand für Taste0 in r16 berechnen
rcall SET_LOW_NIBBLE ; bereite LOW_NIBBLE fuer Ruecksichern vor
mov rTASTE1_0_ZST,r16 ; neuen Z-Wert von Taste0 in TASTE1_0_ZST speichern
;
sbr rLED_Update_SPRR,1 ; setze Status fuer LED_Update
;
pop zh
pop zl
pop r20
pop r19
pop r18
pop r17
pop r16
pop r1
out SREG,r25
pop r25
reti
;
; Vergleich ob im Flanke fuer gedrueckte Taste(0xF) erreicht wurde -
; bei Erreichen wird eine 1 ; bei nicht Erreichen eine 0 in rTASTFLANKEN mit Hilfe des CARY_FLAGs von links nach rechts rotiert
;
CLEAR_REGISTER:
clr r1 ; Register r1 - temporaeres Register fuer Unterprogramme
clr r16 ; Register r16
clr r19 ; Register r19 - temporaeres Register fuer swap
clr r20 ; Register r20 - temporaeres Register fuer Unterprogramme
ret
;
GET_HIGH_NIBBLE:
mov r16, r1
swap r16 ; tausche MSB mit LSB
ret
;
GET_LOW_NIBBLE:
mov r16,r1 ; r16 := Z-Wert Taste0
ret
;
; Flanke sichern
; hatte es mit dem links schieben versucht, aber mit der zusaetzlichen Variablen r20 bin ich flexibler in der Tastenwahl
; FRAGE???
; meine Loesung funktioniert zwar - aber scheint umstaendlich
; wie kann ich es in einem Unterprogramm realisieren?
;
; setze Tast_FLAG wenn rTastx.. oder ..y_ZST 0b00001111 erreicht
FLANKE_SICHERN:
cpi r16, 0x0F
breq SET_TAST_FLAG
ret
;
SET_TAST_FLAG:
or rTastFlanken, r20
ret
;
SET_HIGH_NIBBLE:
swap r16 ; tausche MSB mit LSB
; schiebe rTASTE1_0_ZST in R19 und loesche MSB fuer neues Z von Taste1
mov r19, r1 ; hole rTASTx_y_ZST in r19
andi r19,0x0F ; loesche altes Z (MSB)
or r16, r19 ; vereine MSB und LSB in rTASTE1_0_ZST
ret
;
SET_LOW_NIBBLE:
; schiebe rTASTEx_y_ZST in R19 und loesche LSB fuer neues Z von jeweiligen Taste
andi r16,0x0F ; loesche MSB fuer or
mov r19, r1 ; hole rTASTx_y_ZST in r19
andi r19,0xF0 ; loesche altes Z (LSB)
or r16, r19 ; vereine MSB und LSB in rTASTE1_0_ZST
ret
;
/*-------------------------------------
PROZEDUR NEXT0_TAST_ZST
Die Prozedur NEXT0_TAST_ZST liest den Folgezustand der Tastenentprellung aus einer
Übergangstabelle aus. Die Adresse der Tabelle wird in zh:zl übergeben. Der aktuelle
Zustand (Z-Wert) wird in r16 übergeben. Der aktuelle Tastenzustand (betätigt/nicht
betätigt) ist im T-Flag von SREG gespeichert. Das Ergebnis wird in r16 zurückgegeben.
Die Tabelle, darf maximal 127 Einträge enthalten. Der erste Eintrag der Tabelle ist
die Länge der Tabelle und dient der Bereichsüberprüfung.
D.h. der Zeiger auf die Übergangstabelle muss zh:zl geladen werden BEVOR diese
Prozedur aufgerufen wird!! In diesem Beispiel, bei dem nur eine einzige Übergangstabelle
im Spiel ist, könnte man zh:zl auch innerhalb von NEXT0_TAST_ZST laden. Um die
Prozedur auch in Programmen verwenden zu können, in denen mehrere Zustandsautomaten
nebeneinanderherlaufen, ist es sinnvoll, den Zeiger vorher zu laden. Dann kann je
nach Aufgabe eine andere Tabelle verwendet werden. Kommt in meinen Programmen öfters
vor ;-).
Eingansgrössen:
r16 : enthält aktuellen Z-Wert der Taste
zh:zl : enthält Zeiger auf die Übergangstabelle
SREG (T-Flag) : enthält den aktuellen Messwert der Taste
T = 0 : Taste nicht betätigt
T = 1 : Taste betätigt
Ausgangsgrössen:
r16 : enthält den neuen Z-Wert für die Taste
zh:zl unverändert
SREG unverändert
geänderte Register
r16
Anzahl Zyklen
*/
NEXT0_TAST_ZST:
push r17
in r17,SREG
push r17
push r18
push zl
push zh
; Zeiger auf Anfang der Übergangstabelle berechnen
add zl,zl ; zh:zl verdoppeln, weil einer Adresse im...
adc zh,zh ; .. Programmspeicher immer zwei Bytes entsprechen
;
;
; Information in Bit7 löschen; Tabelle darf nicht länger als 127 Einträge sein
andi r16,0x7F ;
; Tabellenlänge einlesen (0-ter Eintrag in der Tabelle)
lpm r17,z+ ; r17 := TBL_LNG einlesen
;
lpm r18,z+ ; zweites Byte überlesen, wird nicht gebraucht
; Bereichsprüfung ( r17 < TBL_LNG )
cp r16,r17 ; Tabellenindex mit TBL_LNG vergleichen
brlt NXT0ZST_00 ; Sprung, wenn Tabellenindex iO (wenn r16 kleiner r17)
; Fehler: Tabellenindex ist zu gross
clr r16 ; Zustand auf Null setzen
rjmp NXT0ZST_EXIT ; fertig
NXT0ZST_00:
; Tabellenindex im zulässigen Bereich
clr r17 ;
add zl,r16 ; Index zweimal zum Zeiger addieren, weil...
adc zh,r17 ; ... in der Tabelle...
add zl,r16 ; ... 2-Byte-Worte gespeichert sind.
adc zh,r17 ; zh:zl := Zeiger auf Tabelleneintrag
lpm r16,z+ ; r16 := Folgezustand für T=0
lpm r17,z ; r17 := Folgezustand für T=1
brtc NXT0ZST_EXIT ; fertig, wenn T = 0
; T = 1, d.h. Taste betätigt
mov r16,r17 ; zweiten Eintrag als Folgezustand verwenden
NXT0ZST_EXIT:
pop zh
pop zl
pop r18
pop r17
out SREG,r17
pop r17
ret
;
Lesezeichen