![]() |
AVR-Anwendungen Stoppuhr mit ATmega8 in Assembler Assembler Quellcode |
;
; **********************************
; * Vierkanal-Stoppuhr mit ATmega8 *
; * Version 1.0 Juli 2018 *
; * (C)2018 avr-asm-tutorial.net *
; **********************************
;
.nolist
.include "m8def.inc" ; Fuer ATmega8
.list
;
; *****************************
; D E B U G G I N G C O D E
; *****************************
;
; Alle debug-Schalter aus (0) fuer Endversion
.equ Eep = 0 ; 1 schreibt alle Daten ins EEPROM
;
; **********************************
; H A R D W A R E
; **********************************
;
; Device: ATmega8, Package: 28-pin-PDIP
; _________
; 1 / |28
; RESET o--|RES PC5|--o Taste Ch 4
; LCD-D0 o--|PD0 PC4|--o Taste Ch 3
; LCD-D1 o--|PD1 PC3|--o Taste Ch 2
; LCD-D2 o--|PD2 PC2|--o Taste Ch 1
; LCD-D3 o--|PD3 PC1|--o Taste Start/Stop
; LCD-D4 o--|PD4 PC0|--o Taste Reset
; VCC o--|VCC GND|--o GND
; GND o--|GND AREF|--o NC
; Quarz o--|XTAL1 AVCC|--o NC
; 8 MHz o--|XTAL2 PB5|--o SCK/LCD-RW
; LCD-D5 o--|PD5 PB4|--o MISO/LCD-RS
; LCD-D6 o--|PD6 PB3|--o MOSI
; LCD-D7 o--|PD7 PB2|--o LED-Rot Kathode
; LCD-E o--|PB0 PB1|--o Lautsprecher
; |__________|
;
; **********************************
; P O R T S U N D P I N S
; **********************************
;
; Rote Led
.equ pLedRO = PORTB ; Rot Led Output-Port
.equ pLedRD = DDRB ; Rote Led Richtungsport
.equ bLedRO = PORTB2 ; Rote Led Output-Portpin
.equ bLedRD = DDB2 ; Rote Led Richtungs-Portpin
;
; Lautsprecher
.equ pSpkO = PORTB ; Lautsprecher Output-Port
.equ pSpkD = DDRB ; Lautsprecher-Richtungsport
.equ bSpkO = PORTB1 ; Lautsprecher Output-Portpin
.equ bSpkD = DDB1 ; Lautsprecher Richtungs-Portpin
;
; Tasten
.equ pKeyO = PORTC ; Tasten Output-Port
.equ pKeyD = DDRC ; Tasten Richtungs-Port
.equ pKeyI = PINC ; Tasten Eingabe-Port
;
; Taktkonfiguration
; Moegliche Takte: 2,000 oder 8,000 MHz oder 2,048 MHz
; Wenn 2,048 MHz: TC2 Normaler Zaehler, Ueberlauf-Int
; Wenn 2/8 MHz: TC2 als CTC, Compare-Match-Int
.equ clock = 2048000 ; Taktfrequenz Controller in Hz
;
; LCD-Konfiguration fuer lcd.inc
; LCD-Groesse:
.equ LcdLines = 4 ; Anzahl Zeilen (1, 2, 4)
.equ LcdCols = 20 ; Anzahl Zeichen pro Zeile (8..24)
; LCD-Ansteuerung
.equ LcdBits = 8 ; Busbreite (4 oder 8)
; Wenn 4-Bit-Ansteuerung:
;.equ Lcd4High = 1 ; Busnibble (1=Oberes, 0=Unteres)
.equ LcdWait = 0 ; Ansteuerung (0 mit Busy, 1 mit Warteschleifen)
; LCD-Datenports
.equ pLcdDO = PORTD ; Daten-Ausgabe-Port
.equ pLcdDD = DDRD ; Daten-Richtungs-Port
; LCD-Kontrollports und -pins
.equ pLcdCEO = PORTB ; Control E Ausgabe-Port
.equ bLcdCEO = PORTB0 ; Controll E Ausgabe-Portpin
.equ pLcdCED = DDRB ; Control E Richtungs-Port
.equ bLcdCED = DDB0 ; Control E Richtungs-Portpin
.equ pLcdCRSO = PORTB ; Control RS Ausgabe-Port
.equ bLcdCRSO = PORTB4 ; Controll RS Ausgabe-Portpin
.equ pLcdCRSD = DDRB ; Control RS Richtungs-Port
.equ bLcdCRSD = DDB4 ; Control RS Richtungs-Portpin
; Wenn LcdWait = 0:
.equ pLcdDI = PIND ; Daten-Input-Port
.equ pLcdCRWO = PORTB ; Control RW Ausgabe-Port
.equ bLcdCRWO = PORTB5 ; Control RW Ausgabe-Portpin
.equ pLcdCRWD = DDRB ; Control RW Richtungs-Port
.equ bLcdCRWD = DDB5 ; Control RW Richtungs-Portpin
; Dezimalausgabe einbinden
.equ LcdDecimal = 1 ; Dezimalwandlung an
; Hexadezimalausgabe einbinden
;.equ LcdHex = 1 ; Hexadezimalwandlung aus
; Wenn nur Simulation im SRAM:
;.equ avr_sim = 1 ; 1=Simulieren, 0=Nicht simulieren
;
; ********************************************
; J U S T I E R B A R E K O N S T A N T E N
; ********************************************
;
; Englisch oder Deutsche Version
.equ LangEn = 0 ; Englisch = 1, Deutsch = 0
;
.equ cToggle=60 ; Prellunterdrueckung fuer n ms
;
; Tonfrequenzen
.equ cToneInit = 3136 ; Ton bei Init, G7 (g4), in Hz
.equ cToneSec = 2794 ; Ton be Sekunden, F7 (f4), in Hz
.equ cToneMin = 2637 ; Ton bei Minuten, E7 (e4), in Hz
.equ cToneHour = 2349 ; Ton bei Stunden, D7 (d4), in Hz
.equ cToneReset = 1319 ; Ton bei Reset, E6 (e3), in Hz
.equ cToneStart = 1175 ; Ton bei Start, D6 (d3), in Hz
.equ cToneStop = 1047 ; Ton bei Stop, C6 (c3), in Hz
.equ cToneCh1 = 440 ; Ton bei Kanal 1, A4 (a1), in Hz
.equ cToneCh2 = 392 ; Ton bei Kanal 2, G4 (g1), in Hz
.equ cToneCh3 = 349 ; Ton bei Kanal 3, F4 (f1), in Hz
.equ cToneCh4 = 330 ; Ton bei Kanal 4, E4 (e1), in Hz
;
; Lautsprechertoene Dauer
.equ cToneSecDur = 100 ; Tondauer bei Sekunden, in ms
.equ cToneMinDur = 500 ; Tondauer bei Minuten, in ms
.equ cToneHourDur = 750 ; Tondauer bei Stunden, in ms
.equ cToneResetDur = 500 ; Tondauer bei Reset, in ms
.equ cToneStartDur = 400 ; Tondauer bei Start, in ms
.equ cToneStopDur = 300 ; Tondauer bei Stop, in ms
.equ cToneCh1Dur = 200 ; Tondauer bei Kanal 1, in ms
.equ cToneCh2Dur = 200 ; Tondauer bei Kanal 2, in ms
.equ cToneCh3Dur = 200 ; Tondauer bei Kanal 3, in ms
.equ cToneCh4Dur = 200 ; Tondauer bei Kanal 4, in ms
;
; *************************************************
; F E S T E & A B G E L E I T E T E K O N S T
; *************************************************
;
.if clock == 8000000
.equ cPresc2 = 64 ; Prescaler TC2 = 64
.else
.equ cPresc2 = 8 ; Prescaler TC2 = 8
.endif
;
.if clock != 2048000
.equ cCtcDiv2 = clock / cPresc2 / 1000 ; CTC-Teiler
.equ cCmp2A = cCtcDiv2 - 1 ; Compare-Wert TC2
.endif
;
; Lautsprechertoene in Compare-Werte
.equ cPresc1 = 8 ; Prescaler TC1
.equ cCmpInit = clock / cToneInit / cPresc1 / 2 - 1 ; Compare Init
.equ cCmpSec = clock / cToneSec / cPresc1 / 2 - 1 ; Compare Sekunde
.equ cCmpMin = clock / cToneMin / cPresc1 / 2 - 1 ; Compare Minute
.equ cCmpHour = clock / cToneHour / cPresc1 / 2 - 1 ; Compare Stunde
.equ cCmpReset = clock / cToneReset / cPresc1 / 2 - 1 ; Compare Reset
.equ cCmpStart = clock / cToneStart / cPresc1 / 2 - 1 ; Compare Start
.equ cCmpStop = clock / cToneStop / cPresc1 / 2 - 1 ; Compare Stop
.equ cCmpCh1 = clock / cToneCh1 / cPresc1 / 2 - 1 ; Compare Kanal 1
.equ cCmpCh2 = clock / cToneCh2 / cPresc1 / 2 - 1 ; Compare Kanal 2
.equ cCmpCh3 = clock / cToneCh3 / cPresc1 / 2 - 1 ; Compare Kanal 3
.equ cCmpCh4 = clock / cToneCh4 / cPresc1 / 2 - 1 ; Compare Kanal 4
;
; Tondauer in Zaehlerwerte
.equ cCtrInit = 1 ; Zaehle bis zur ersten Interrupt-Ausfuehrung
.equ cCtrSec = cToneSec * 2 * cToneSecDur / 1000 ; Dauer Sekunde
.equ cCtrMin = cToneMin * 2 * cToneMinDur / 1000 ; Dauer Minute
.equ cCtrHour = cToneHour * 2 * cToneHourDur / 1000 ; Dauer Stunde
.equ cCtrReset = cToneReset * 2 * cToneResetDur / 1000 ; Dauer Reset
.equ cCtrStart = cToneStart * 2 * cToneStartDur / 1000 ; Dauer Start
.equ cCtrStop = cToneStop * 2 * cToneStopDur / 1000 ; Dauer Stop
.equ cCtrCh1 = cToneCh1 * 2 * cToneCh1Dur / 1000 ; Dauer Kanal 1
.equ cCtrCh2 = cToneCh2 * 2 * cToneCh2Dur / 1000 ; Dauer Kanal 2
.equ cCtrCh3 = cToneCh3 * 2 * cToneCh3Dur / 1000 ; Dauer Kanal 3
.equ cCtrCh4 = cToneCh4 * 2 * cToneCh4Dur / 1000 ; Dauer Kanal 4
;
; **********************************
; R E G I S T E R
; **********************************
;
; frei: R0 bis R8
.def rmsBuf = R9 ; Ablage der Millisekunden
.def rLedCtr = R10 ; LED-Zaehlregister
.def rLed = R11 ; LED-Anzeigeregister
.def rTgl = R12 ; Prellzaehler
.def rKeyCmp = R13 ; Vergleichswert Tasten
.def rKey = R14 ; Tasteneingabe-Wert
.def rSreg = R15 ; Sichern/Wiederherstellung Status-Port
.def rmp = R16 ; Vielzweckregister
.def rimp = R17 ; Vielzweck innerhalb Ints
.def rFlag = R18 ; Flaggenregister
.equ bMs = 0 ; Millisekundenflagge
.equ bRun = 1 ; Run/Stop-Flagge
.equ bCh1 = 2 ; Kanal 1 Flagge
.equ bCh2 = 3 ; Kanal 2 Flagge
.equ bCh3 = 4 ; Kanal 3 Flagge
.equ bCh4 = 5 ; Kanal 4 Flagge
.equ bdS = 6 ; Dezisekundenflagge
.equ bSort = 7 ; Sortierflagge
.def rHour = R19 ; Stunden
.def rMin = R20 ; Minuten
.def rSec = R21 ; Sekunden
.def rdSec = R22 ; Dezisekunden
.def rmSec = R23 ; Millisekunden
.def rCtrL = R24 ; Zaehler fuer Tondauer, LSB
.def rCtrH = R25 ; dto., MSB
; Benutzt: XH:XL=R27:R26 als Zeiger auf sData
; Benutzt: YH:YL=R29:R28 als Zeiger fuer Stopreihenfolge
; Benutzt: ZH:ZL=R31:R30 fuer diverse Zwecke
;
; **********************************
; S R A M
; **********************************
;
.dseg
.org SRAM_START
sData:
.byte 20 ; Reserviere 4*5 Bytes fuer Zeitinfo
sChannelRow:
.byte 4 ; Reihenfolgetabelle
sDataEnd:
;
; **********************************
; C O D E S E G M E N T
; **********************************
;
.cseg
.org 000000
;
; **************************************
; R E S E T & I N T - V E K T O R E N
; **************************************
rjmp Main ; Reset-Vector
reti ; INT0, nicht benutzt
reti ; INT1, nicht benutzt
.if clock == 2048000
reti ; TC2 Compare A
rjmp MilliSecIsr ; OVF2
.else
rjmp MilliSecIsr
reti ; OVF2
.endif
reti ; ICP1, nicht benutzt
rjmp OC1CmpIsr ; OC1A
reti ; OC1B, nicht benutzt
reti ; OVF1, nicht benutzt
reti ; OVF0, nicht benutzt
reti ; SPI, nicht benutzt
reti ; URXC, nicht benutzt
reti ; UDRE, nicht benutzt
reti ; UTXC, nicht benutzt
reti ; ADCC, nicht benutzt
reti ; ERDY, nicht benutzt
reti ; ACI, nicht benutzt
reti ; TWI, nicht benutzt
reti ; SPMR, nicht benutzt
;
; *****************************************
; I N T - S E R V I C E R O U T I N E N
; *****************************************
;
; TC2 Millisekunden-Interrupt
; wird in jeder Millisekunde ausgefuehrt
; Liest die Tasteneingaenge und setzt die ms-Flagge
; Wenn bRun eineschaltet: erhoehe Millisekunden,
; wenn 100: Neustart und Setzen der ds-Flagge
MillisecIsr:
in rSreg,SREG ; Sichere SREG
in rKey,pKeyI ; Lese Tasteneingang
sbr rFlag,1<<bmS ; Setze Millisekundenflagge
sbrs rFlag,bRun ; Run-Flagge gesetzt?
rjmp MillisecIsr1 ; Nein, nicht erhoehen
inc rmSec ; Erhoehe Millisekunden
cpi rmSec,100 ; 100 ms erreicht?
brcs MilliSecIsr1 ; Nein
clr rmSec ; Neustart Millisekunden
sbr rFlag,1<<bdS ; Setze Dezisekundenflagge
MillisecIsr1:
out SREG,rSreg ; SREG wieder herstellen
reti
;
; TC1 Compare-Match-Interrupt
; wird bei Compare-Match A ausgefuehrt
; Ein halber Tonzyklus is beendet
; Zaehlt den Dauerzaehler abwaerts, wenn
; Null: Lautsprecher-Ausgang auf Clear
; mit dem Ende des naechsten Zyklus
OC1CmpIsr:
in rSreg,SREG ; Sichern SREG
sbiw rCtrL,1 ; Zaehler abwaerts
brne OC1CmpIsr1 ; Nicht Null
ldi rimp,1<<COM1A1 ; Lautsprecherausgang Clear
out TCCR1A,rimp
OC1CmpIsr1:
out SREG,rSreg ; SREG wieder herstellen
reti
;
; ***********************************
; H A U P T P R O G R A M I N I T
; ***********************************
;
Main:
.ifdef SPH
ldi rmp,High(RAMEND) ; MSB auf RAMEND
out SPH,rmp ; Init MSB Stapelzeiger
.endif
ldi rmp,Low(RAMEND) ; LSB auf RAMEND
out SPL,rmp ; Init LSB Stapelzeiger
; Initiierung Tastereingaenge
ldi rmp,0x3F ; Alle sechs Output-Pins High
out pKeyO,rmp ; Einschalten der Pull-Ups
clr rmp ; Richtung auf Low
out pKeyD,rmp ; im Richtungsregister
; Initiieren der roten Led
sbi pLedRD,bLedRD ; Richtung Portpin = Output
cbi pLedRO,bLedRO ; Ausgang Low, Led an
ldi rmp,0b11000011 ; LED-Register Startwert
mov rLed,rmp ; in LED-Register
ldi rmp,8 ; LED-Zaehler-Register auf acht
mov rLedCtr,rmp
; Lautsprecherausgang initiieren
cbi pSpkO,bSpkO ; Ausgang Low
sbi pSpkD,bSpkD ; Richtung = Output
; Lautsprecher-Init-Ton
ldi rCtrH,High(cCtrInit) ; Lade Dauerzaehler
ldi rCtrL,Low(cCtrInit)
ldi rmp,High(cCmpInit) ; Lade Compare-Wert MSB
out OCR1AH,rmp ; MSB zuerst
ldi rmp,Low(cCmpInit) ; dto., LSB
out OCR1AL,rmp ; LSB danach
ldi rmp,1<<COM1A0 ; Toggele OC1A-Ausgabe-Pin bei Compare Match
out TCCR1A,rmp
ldi rmp,(1<<WGM12)|(1<<CS11) ; TC1=CTC, Prescaler = 8
out TCCR1B,rmp
; Starte die LCD
rcall LcdInit ; LCD initiieren
ldi ZH,High(2*LcdInit1) ; Starttext ausgeben
ldi ZL,Low(2*LcdInit1)
rcall LcdText
ldi rmp,40 ; Warte zwei Sekunden
Start1:
rcall LcdWait50ms ; Warte 50 ms
dec rmp ; Abwaerts zaehlen
brne Start1 ; Weiter warten
clr ZH ; Position erste Zeile
clr ZL ; Position erste Spalte
rcall LcdPos ; Position setzen
ldi ZH,High(2*LcdInit2) ; Zweite Maske ausgeben
ldi ZL,Low(2*LcdInit2)
rcall LcdText
; Initiere die SRAM-Daten
clr rmSec ; Init Millisekunden
clr rdSec ; Init Dezisekunden
clr rSec ; Init Sekunden
clr rMin ; Init Minuten
clr rHour ; Init Stunden
clr rFlag ; Loesche alle Flaggen
rcall ClearData ; Loesche SRAM-Daten
rcall Display ; Alle Kanaele anzeigen
; Initiiere TC2 als Millisekunden-Timer
.if clock == 2048000
; TC2 als normalen Timer mit Precaler 8 und Ueberlauf-Int
ldi rmp,(1<<CS21) ; Prescaler = 8
out TCCR2,rmp
ldi rmp,(1<<OCIE1A)|(1<<TOIE2) ; TC1=Comp Match Int, TC2=Overflow Int
out TIMSK,rmp
.else
ldi rmp,cCmp2A ; Setze CTC-Vergleichswert
out OCR2,rmp ; im Compare-Port
.if clock == 8000000
ldi rmp,(1<<WGM21)|(1<<CS22) ; CTC-Mode, Prescaler=64
.else
ldi rmp,(1<<WGM21)|(1<<CS21) ; CTC-Mode, Prescaler=8
.endif
out TCCR2,rmp ; in TC2 control port
ldi rmp,(1<<OCIE1A)|(1<<OCIE2) ; TC1-Comp Match Int, TC2-Comp Match Int
out TIMSK,rmp
.endif
; Schlafmodus
ldi rmp,1<<SE ; Schlafmodus an, Idle-Modus
out MCUCR,rmp
; Interrupt Enable
sei ; Interrupts an
;
; *********************************
; P R O G R A M M S C H L E I F E
; *********************************
;
Loop:
sleep ; Schlafen
nop ; Aufwachen
sbrc rFlag,bmS ; Millisekundenflagge?
rcall Millisecond ; Millisekunde bearbeiten
sbrc rFlag,bdS ; Dezisekundenflagge?
rcall Dezisecond ; Dezisekunde bearbeiten
rjmp loop ; Schleife
;
; Eine Millisekunde ist vorbei, pruefe Tasten
Millisecond:
cbr rFlag,1<<bmS ; Loesche Flagge
mov rmsBuf,rmSec ; Millisekundenstand in Puffer
ldi rmp,0b00000001 ; Starte mit Taste 1
mov rKeyCmp,rmp ; in Vergleichsregister
and rmp,rKey ; Pruefe Portbit
brne ChkStartStop ; Bit ist high, teste Taste 2
; Reset-Taste gedrueckt
clr rFlag ; Loesche alle Flaggen
clr rmSec ; Starte Zeit bei Null
clr rdSec
clr rSec
clr rMin
clr rHour
rcall ClearData ; Loesche SRAM-Daten
ldi rmp,4 ; Ton #4
rcall ToneStart ; Starten
clr ZH ; Erste Zeile
clr ZL ; Erste Spalte
rcall LcdPos ; LCD-Position
ldi ZH,High(2*LcdInit2) ; Maske 2 ausgeben
ldi ZL,Low(2*LcdInit2)
rcall LcdText
rjmp Display ; Alle Kanaele anzeigen
ChkStartStop:
; Start/Stop-Taste?
lsl rKeyCmp ; Naechstes Bit
mov rmp,rKey ; Tastenstand in rmp
and rmp,rKeyCmp ; Mit Bitmaske vergleichen
brne ChkToggle ; Eins, pruefe Toggel-Zaehler
tst rTgl ; Pruefe Toggelzaehler
brne ChkStartStopRestartToggle ; Nicht Null, Neustart
ldi rmp,1<<bRun ; bRun-Bit in rmp
eor rFlag,rmp ; Flagge umkehren
ChkStartStopRestartToggle:
; Toggel-Zaehler neu starten
ldi rmp,cToggle ; Toggle-Wert
mov rTgl,rmp ; in Toggle-Zaehler
rjmp ChkChannels ; Weiter mit Kanaltasten
ChkToggle:
; Eingang high, teste Toggelzaehler
tst rTgl ; Toggelzaehler Null?
breq ChkChannels ; Ja, weiter mit Kanaltasten
dec rTgl ; Toggelzaehler vermindern
;
; Alle Kanaltasten pruefen
; rKeyCmp zeigt auf Vergleichswert Start/Stop
ChkChannels:
mov rmp,rmSec ; Zeit = Null?
or rmp,rdSec
or rmp,rSec
or rmp,rMin
or rmp,rHour
breq ChkChannels4 ; Ja, Tasten nicht auswerten
clr ZH ; Beginne in LCD-Zeile 1
ldi XH,High(sData) ; Zeiger auf ersten Datensatz
ldi XL,Low(sData)
ChkChannels1:
lsl rKeyCmp ; Naechster Tastenvergleicher
mov rmp,rKeyCmp ; in rmp
and rmp,rKey ; Ist diese Taste gedrueckt?
brne ChkChannels2 ; Taste nicht gedrueckt
mov rmp,rKeyCmp ; Lese Tastenvergleicher
and rmp,rFlag ; Ist dieser Kanal angehalten?
brne ChkChannels2 ; Ja, zur naechsten Taste
; Kanaltaste ist erstmals gedrueckt
; Disable interrupts to avoid ms increase
; during copy operation
st X+,rHour ; Speichern Zeit
st X+,rMin
st X+,rSec
st X+,rdSec
st X+,rmsBuf ; Millisekunden aus Puffer!
sbiw XL,5 ; An den Pufferanfang
or rFlag,rKeyCmp ; Setze Kanalflagge
st Y+,ZH ; Kanal in Reihenfolgeliste
push ZH ; Zeile retten
ldi ZL,6 ; Ausgabeposition
rcall LcdPos ; in LCD waehlen
rcall DisplayX ; Zeile aus Zeiger X anzeigen
ldi rmp,7 ; Ton auswaehlen
pop ZH ; Zeile vom Stapel holen
push ZH ; und zurueck auf den Stapel
add rmp,ZH ; Addiere den Zeilenzaehler
rcall ToneStart ; Spiele den Ton
pop ZH ; Wiederherstellen Zeilenzaehler
rjmp ChkChannels3 ; Naechste Taste
ChkChannels2:
; Kanal inaktiv, passe Zeiger an
adiw XL,5 ; Zeige auf naechsten Datensatz
ChkChannels3:
inc ZH ; Erhoehe Zeilenzaehler
cpi ZH,4 ; Ende erreicht?
brcs ChkChannels1 ; Pruefe naechsten Kanal
ldi rmp,0b00111100 ; Alle Kanaele
and rmp,rFlag ; isolieren
cpi rmp,0b00111100 ; Alle Kanaele Eins?
breq DisplaySorted ; Ja, gib sortiert aus
ChkChannels4:
ret
;
; Gib die Zeiten sortiert aus
DisplaySorted:
.if Eep == 1
rcall Write2Eep ; Schreibe die Daten ins EEPROM
.endif
sbr rFlag,1<<bSort ; Setze Sortiertflagge
cbr rFlag,1<<bRun ; Halte Uhr an
clr ZH ; Zeilenzaehler, beginne bei Zeile 1
ldi YH,High(sChannelRow) ; Y auf sortierte Liste
ldi YL,Low(sChannelRow)
DisplaySorted1:
ld rmp,Y ; Lese Kanalnummer aus sortierter Liste
cpi rmp,0xFF ; Kanal nicht angehalten?
brne DisplaySorted3 ; Kanal angehalten
; Loesche diese Zeile
ldi ZL,0 ; Spalte = 0
rcall LcdPos ; LCD-Position setzen
ldi ZL,20 ; 20 Leerzeichen
DisplaySorted2:
ldi rmp,' ' ; Leerzeichen
rcall LcdChar ; auf LCD
dec ZL ; Weitere Leerzeichen?
brne DisplaySorted2 ; Ja, weiter
rjmp DisplaySorted6 ; Naechster Kanal
DisplaySorted3:
ldi XH,High(sData) ; Zeiger auf Anfang
ldi XL,Low(sData)
tst rmp ; Datensatz = 0?
breq DisplaySorted5 ; Ja
DisplaySorted4:
adiw XL,5 ; Naechster Datensatz
dec rmp ; Abwaerts
brne DisplaySorted4 ; Weiter
DisplaySorted5:
ldi ZL,5 ; Spaltenposition
rcall LcdPos ; Position auf LCD
ld rmp,Y ; Lese Kanalnummer erneut
subi rmp,-'0'-1 ; Addiere ASCII-Eins
rcall LcdChar ; Kanalnummer ausgeben
push ZH ; Zeilenzaehler retten
rcall DisplayX ; Datensatz ausgeben
pop ZL ; Zeilenzaehler wieder herstellen
DisplaySorted6:
adiw YL,1 ; Naechster Listeneintrag
inc ZH ; Naechste Zeile
cpi ZH,4 ; Alle ausgegeben?
brcs DisplaySorted1 ; Nein, weiter
ret
;
; Zeige Zeile aus Seicher an
; X zeigt auf Datensatz
DisplayX:
ldi rmp,'=' ; Mit Gleichheitszeichen beginnen
rcall LcdChar
ldi rmp,' ' ; und ein Leerzeichen
rcall LcdChar
ld rmp,X+ ; Lade Stunden
rcall LcdDec2 ; und zeige an
ldi rmp,':' ; Doppelpunkttrenner
rcall LcdChar ; anzeigen
ld rmp,X+ ; Lade Minuten
rcall LcdDec2 ; und zeige an
ldi rmp,':' ; Dopelpunkttrenner
rcall LcdChar ; anzeigen
ld rmp,X+ ; Lade Sekunden
rcall LcdDec2 ; und zeige an
.if LangEn == 1
ldi rmp,'.' ; Dezimalpunkt
.else
ldi rmp,',' ; Dezimalkomma
.endif
rcall LcdChar ; anzeigen
ld rmp,X+ ; Lade Dezisekunden
subi rmp,-'0' ; In ASCII
rcall LcdChar ; und anzeigen
ld rmp,X+ ; Lade Millisekunden
rjmp LcdDec2 ; und zeige an
;
; Debugging, schreibe SRAM-Inhalt in das EEPROM
Write2Eep:
ldi XH,High(sData) ; SRAM-Zeiger
ldi XL,Low(sData)
clr ZH ; EEPROM Adresszeiger
clr ZL
Write2Eep1:
out EEARH,ZH ; Adresse schreiben
out EEARL,ZL
ld rmp,X+ ; SRAM-Byte lesen
out EEDR,rmp ; in Datenport
ldi rmp,1<<EEMWE ; Master Write Enable
cli ; Interrupts abschalten
out EECR,rmp ; Starte EEPROM Write Enable
ldi rmp,1<<EEWE ; Write Enable
out EECR,rmp ; Starte EEPROM Write
sei ; Interrupts wieder zulassen
Write2Eep2:
sbic EECR,EEWE ; Fertig geschrieben?
rjmp Write2Eep2 ; Nein, warte weiter
adiw ZL,1 ; Naechste Adresse
cpi XL,Low(sDataEnd+4) ; Alles geschrieben?
brne Write2Eep1 ; Nein, weiter
ret
;
; Eine Zehntelsekunde ist vorbei, erhoehe Zeit
Dezisecond:
cbr rFlag,1<<bdS ; Loesche Flagge
inc rdSec ; Erhoehe Dezisekunde
cpi rdSec,10 ; Schon bei 10?
brcs Dezisecond2 ; Nein, weiter
clr rdSec ; Neustart Dezisekunde
inc rSec ; Erhoehe Sekunden
cpi rSec,60 ; 60 Sekunden?
ldi rmp,1 ; Ton #1
brcs Dezisecond1 ; Nein, starte Sekundenton
inc rmp ; Naechster Ton
clr rSec ; Neustart Sekunden
inc rMin ; Erhoehe Minuten
cpi rMin,60 ; 60 Minuten?
brcs Dezisecond1 ; Nein, starte Minutenton
inc rmp ; Naechstr Ton
clr rMin ; Neustart Minuten
inc rHour ; Erhoehe Stunden
cpi rHour,24 ; 24 Stunden?
brcs Dezisecond1 ; Nein
clr rHour ; Neustart Stunden
Dezisecond1:
rcall ToneStart ; Spiele Ton
DeziSecond2:
sbrs rFlag,bSort ; Anzeige im Sortiermodus?
rcall Display ; Nein, zeige Zeit an
; LED-Aktionen
cbi pLedRO,bLedRO ; LED an
sbrs rFlag,bRun ; Nicht aktiv?
ret ; Inaktiv
lsr rLed ; Schiebe niedrigstes Bit in Carry
brcc DeziSecond3 ; Null, lasse LED an
sbi pLedRO,bLedRO ; Eins, LED aus
DeziSecond3:
dec rLedCtr ; Erniedrige LED-Zaehler
brne DeziSecond4 ; Nicht bei Null
mov rmp,rFlag ; Kopiere Flaggen
ori rmp,0b11000011 ; Ausser Flaggen alles aus
mov rLed,rmp ; In LED-Register
ldi rmp,8 ; Neustart LED-Zaehler
mov rLedCtr,rmp
DeziSecond4:
ret
;
; Zeit auf allen aktiven Kanaelen anzeigen
Display:
ldi rmp,0b00000100 ; Vergleichswert Kanal 1
mov rKeyCmp,rmp ; Kopiere in Vergleichsregister
ldi ZH,0 ; Beginne mit Kanal 1 in Zeile 1
ldi ZL,8 ; Spalte 8
Display1:
push ZH ; Sichere Z
push ZL
mov rmp,rFlag ; Kopiere Flaggen in rmp
and rmp,rKeyCmp ; Ist Kanalflagge gesetzt?
brne Display2 ; Bit ist gesetzt, nicht anzeigen
rcall LcdPos ; Setze LCD-Cursor
rcall DisplayLine ; Zeige Zeile an
Display2:
lsl rKeyCmp ; Naechster Kanal
pop ZL ; Hole Spalte vom Stapel
pop ZH ; Hole Zeile vom Stapel
inc ZH ; Naechste Zeile
cpi ZH,4 ; Ende der Zeilen?
brcs Display1 ; Nein, wiederhole
ret
;
; Zeigt eine Zeile mit aktueller Zeit an
; LCD-Adresse voreingestellt
DisplayLine:
mov rmp,rHour ; Lese Stunden
rcall LcdDec2 ; Zeige an
ldi rmp,':'
rcall LcdChar
mov rmp,rMin ; Lese Minuten
rcall LcdDec2 ; Zeige an
ldi rmp,':'
rcall LcdChar
mov rmp,rSec ; Lese Sekunden
rcall LcdDec2 ; Zeige an
.if LangEn == 1
ldi rmp,'.' ; Dezimalpunkt
.else
ldi rmp,',' ; Dezimalkomma
.endif
rcall LcdChar ; Zeichen ausgeben
mov rmp,rdSec ; Lese Dezisekunden
subi rmp,-'0'
rjmp LcdChar
;
; Loesche Daten im SRAM
ClearData:
ldi ZH,High(sData) ; Z an den Datenanfang
ldi ZL,Low(sData)
clr rmp ; Schreibe Nullen
ClearData1:
st Z+,rmp ; Schreibe Byte in Datenpuffer
cpi ZL,Low(sDataEnd) ; Pufferende?
brne ClearData1 ; Nein, weiter
ldi YH,High(sChannelRow+4) ; Setze Reihenfolge-Zeiger
ldi YL,Low(sChannelRow+4)
ldi rmp,0xFF ; Fuelle Reihenfolge mit FF
st -Y,rmp ; In letztes Byte
st -Y,rmp ; In vorletztes
st -Y,rmp ; In vorvorletztes
st -Y,rmp ; In Vorvorvorletztes
ret
;
; Starte einen Ton
; Tonnummer in rmp
ToneStart:
lsl rmp ; Multipliziere mit 2
lsl rmp ; Multipliziere mit 4
ldi ZH,High(2*ToneTable) ; Tontabelle in Z
ldi ZL,Low(2*ToneTable)
add ZL,rmp ; Addiere Tonnumber*4 in LSB
ldi rmp,0 ; Zum Carry addieren
adc ZH,rmp ; Carry in MSB
lpm rCtrL,Z+ ; Lese LSB Tondauer
lpm rCtrH,Z+ ; Lese MSB Tondauer
lpm rmp,Z+ ; Lese LSB CTC-Wert
lpm ZH,Z ; Lese MSB CTC-Wert
out OCR1AH,ZH ; Schreibe MSB in Compare A
out OCR1AL,rmp ; Schreibe LSB in compare A
ldi rmp,1<<COM1A0 ; Lausprecherausgang torkeln
out TCCR1A,rmp ; Starte Ton
ret
;
; Tontabelle
ToneTable:
.dw cCtrInit,cCmpInit ; Ton 0
.dw cCtrSec,cCmpSec ; Ton 1
.dw cCtrMin,cCmpMin ; Ton 2
.dw cCtrHour,cCmpHour ; Ton 3
.dw cCtrReset,cCmpReset ; Ton 4
.dw cCtrStart,cCmpStart ; Ton 5
.dw cCtrStop,cCmpStop ; Ton 6
.dw cCtrCh1,cCmpCh1 ; Ton 7
.dw cCtrCh2,cCmpCh2 ; Ton 8
.dw cCtrCh3,cCmpCh3 ; Ton 9
.dw cCtrCh4,cCmpCh4 ; Ton 10
;
; Include der Lcd-Routinen
.include "lcd.inc"
;
; Lcd-Init Text
.if LangEn == 1
; Init Text englisch
LcdInit1:
.db " Stop watch ATmega8",0x0D
.db " Four channels at",0x0D,0xFF
.db " one millisecond",0x0D,0xFF
.db " (C)2018 by DG4FAC",0xFE
;
; Maske englisch
LcdInit2:
.db "Track1 00:00:00.0 ",0x0D,0xFF
.db "Track2 00:00:00.0 ",0x0D,0xFF
.db "Track3 00:00:00.0 ",0x0D,0xFF
.db "Track4 00:00:00.0 ",0xFE,0xFF
; 01234567890123456789
.else
; Init Text deutsch
LcdInit1:
.db " Stoppuhr ATmega8",0x0D,0xFF
.db " Vier Kanaele mit",0x0D,0xFF
.db " einer Millisekunde",0x0D
.db " (C)2018 by DG4FAC",0xFE
;
; Maske deutsch
LcdInit2:
.db "Bahn 1 00:00:00.0 ",0x0D,0xFF
.db "Bahn 2 00:00:00.0 ",0x0D,0xFF
.db "Bahn 3 00:00:00.0 ",0x0D,0xFF
.db "Bahn 4 00:00:00.0 ",0xFE,0xFF
; 01234567890123456789
.endif
;
; Copyright Info
.db "(C)2018 by avr-asm-tutorial.net " ; Normaler Text
.db "C(2)10 8yba rva-mst-turoai.len t" ; Wortweise
;
; Ende Quellcode
;