Code:
;***** STK500 Lernprogramm Nr.4e
;*** 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, Zst_Var 0x03 bzw. 0x00
;*** Taste nicht gedrueckt >>> log 0 in Tast_Stat(r17)
;*** Taste gedrueckt >>> log 1 in Tast_Stat(r17)
;
;
.include "m8515def.inc"
.def Temp = r16 ; Temporary Register
.def Tast_Stat = r17 ; Tasten Status
.def Tast_Stat0 = r18
.def LED_Stat = r19 ; LED Status
.def Zst_Var = r20 ; Zustandsvariable - beinhaltet eine 2Bit-Zahl zur Tastzustandserkennung
.def LED_Update_SPRR = r21 ; Sperrt die Ausfuehrung von TastenTest wenn, keine neuer TIMER1_OVL voran gegangen
;
.equ Timerwert = 255 ;-1562 ; Timerschritte bis zum Überlauf
.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
; Bit-Definitionen für Z-Wert
.equ bZWERT_FLANKE = 3 ; Bit3 speichert das "Flankenbit"
; 0 = "keine Flanke"
; 1 = "Flanke steht an"
.equ bZWERT_BETAETIGT = 0 ; Bit0 speichert den Tastenzustand
; 0 = "nicht betätigt"
; 1 = "betätigt"
; Pinzuordnung für div. Tasten
.equ TASTE0_PIN = 0
.equ TASTE1_PIN = 1
.equ TASTE2_PIN = 3 ; die Zuordnung der PINs muss ja nicht mit der TastenNr übereinstimmen!
.equ TASTE3_PIN = 2
; jetzt kommen die Register, die die Z-Werte der Tasten enthalten. Im Prinzip
; braucht man nur jeweils nur ein Nibble pro Taste. Um die Sache erstmal einfach
; zu halten wird für jede Taste ein ganzes Byte verbraucht.
.def rTASTE0_ZST = r15 ; enthält Zustand Taste0
.def rTASTE1_ZST = r14 ; enthält Zustand Taste1
.def rTASTE2_ZST = r13 ; enthält Zustand Taste2
.def rTASTE3_ZST = r12 ; enthält Zustand Taste3
;
; 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=7,Flanke=1
.dw 0x0705 ; Z=7 -> Z=5,Flanke=0 / Z=7,Flanke=0
; verweist auf den Programmspeicher und zeigt dort im ersten Wort auf den ersten
; definierten Wert(Tabellenlaenge),
; anschliessende Worte beinhalten die festgelegten Werte fortlaufend
; bedeutet in meinem Fall Verweis auf 0x0011 mit 08 00 (.dw 0x0008)
; 0x0012 mit 02 00 (.dw 0x0200)
; 0x0013 mit 00 00 (.dw 0x0000)
; 0x0014 mit 04 00 (.dw 0x0400)
; 0x0015 mit 08 50 (.dw 0x0580)
; 0x0016 mit 02 87 (.dw 0x8702)
; 0x0017 mit 03 07 (.dw 0x0703)
; 0x0018 mit 00 00 (.dw 0x0000)
; 0x0019 mit 05 07 (.dw 0x0705)
; ->>>
; Der Wert(Tabellenlaenge) ist auch nur ein festgelegter Wert und fuer Programmoperationen wichtig.
;
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<<CS12)|(1<<WGM12) ; Taktfrequenz mit Vorteiler 1024 gewaehlt
out TCCR1B,temp
;
ldi temp,HIGH(Timerwert)
out OCR1AH,temp
ldi temp,LOW(Timerwert)
out OCR1AL,temp
;
ldi temp,(1<<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 rTASTE0_ZST,r16 ; Taste0 und Taste1 zurücksetzen
mov rTASTE2_ZST,r16 ; Taste2 und Taste3 zurücksetzen
clr Zst_Var
;
sei
;
;
MAIN:
sbrc LED_Update_SPRR,0 ; kein LED_Update, wenn Bit0 ==0
rcall LED_Update
rjmp MAIN
;
LED_Update:
cbr LED_Update_SPRR,0 ; loesche Bit0, um LED_Update bis zur naechsten TIMER1_ISR zu sperren
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 r16
push r17
push r18
push zl
push zh
;/*
;Das hier hattest Du übersehen:
;Der Zeiger auf die Übergangstabelle muss VOR (!!) dem Aufruf von NEXT0_TAST_ZST
;in die Register zh:zl geladen werden !!
;*/
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
; Taste0 verarbeiten
bst r18,TASTE0_PIN ; überträgt den Messwert von TASTE0_PIN ins T-Flag von SREG
mov r16,rTASTE0_ZST ; r16 := Z-Wert Taste0
andi r16,0x07 ; "Flankenbit" löschen
rcall NEXT0_TAST_ZST ; Folgezustand für Taste0 in r16 berechnen
mov rTASTE0_ZST,r16 ; neuen Z-Wert in TASTE0_ZST speichern
; Taste1 verarbeiten
bst r18,TASTE1_PIN ; überträgt den Messwert von TASTE1_PIN ins T-Flag von SREG
mov r16,rTASTE1_ZST ; r16 := Z-Wert Taste1
andi r16,0x07 ; "Flankenbit" löschen
rcall NEXT0_TAST_ZST ; Folgezustand für Taste1 in r16 berechnen
mov rTASTE1_ZST,r16 ; neuen Z-Wert in TASTE1_ZST speichern
; usw...
sbr LED_Update_SPRR,0 ; Status fuer LED_Update 1 - LED_Update / 0 - kein LED_Update
pop zh
pop zl
pop r18
pop r17
pop r16
out SREG,r25
pop r25
reti
;
;/*-------------------------------------
;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
; FRAGE: Wo wird die Information abgelegt -> im Flashspeicher
;
;
; 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
; 1.Durchgang
; es wird in R16 der Wert 0x19 eingetragen - ??? Die letzte Stelle in der Tabelle (0x0019)
; und in R30(ZL) die 0x01
;
; 2.Durchgang
; es wird in R16 der Wert 0x19 eingetragen - ??? Die letzte Stelle in der Tabelle (0x0019)
; und in R30(ZL) die 0x01
;
; KLAR; Du hattest den Zeiger nicht gesetzt!
;
;
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
; 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:
; sbr LED_Update_SPRR,0 ; Status fuer LED_Update 1 - LED_Update / 0 - kein LED_Update
; diese Zeile gehört in den Interruptdienst!
pop zh
pop zl
pop r18
pop r17
out SREG,r17
pop r17
ret
;
Lesezeichen