Pfad:
Home =>
AVR-Übersicht =>
Anwendungen =>
DCF77-Weckuhr m16 => Assembler-Quellcode
This page in english:
 |
Anwendungen von
AVR-Einchip-Prozessoren AT90S, ATtiny, ATmega und ATxmega
DCF77 Weckuhr mit LCD Der Assembler-Quellcode
|
 |
Der Assembler-Quellcode für die DCF77-Weckuhr mit LCD
Dieser Code im asm-Format
;
; **********************************************
; * ATmega16-Weckuhr mit LCD und DCF77-Synchr. *
; * Version 4.0 vom Mai 2018 *
; * (C)2018 by G.Schmidt, avr-asm-tutorial.net *
; **********************************************
;
; Include-Datei zum Prozessortyp
.NOLIST
.INCLUDE "m16def.inc" ; Header-Datei fuer ATmega16
.LIST
;
; ===================================
; S C H A L T E R
; ===================================
;
; Anzeigesprache: 0 = Deutsch, 1 = Englisch
.equ LangEN = 0 ; Sprachauswahl
;
; ===================================
; D E B U G G E R S C H A L T E R
; ===================================
;
; Alle Debug-Schalter sind bei der Endversion Null!
;
; 1. Debuggen der LCD
.equ dbgLcd = 0 ; Debuggen der LCD, 0=aus, 1=ein
;
; 2. Debuggen des AD-Wandlers
.equ dbgAdc = 0 ; Debuggen des ADC
.equ debugAdcChannel = 0 ; Auswahl des Kanals: 0, 1 oder 2
;
; 3. Debuggen der Tasten
.equ dbgKey = 0 ; Debuggen der angeschlossenen Tasten
.equ dbgKeyState = 0 ; Debuggen des Tastenstatusses in Zeile 4
;
; 4. Debuggen des Lautsprechers
.equ dbgSpk = 0 ; Debuggen des angeschlossenen Lautsprechers
;
; 5. Debuggen des DCF77-Signals
.equ dbgDcfDur = 0 ; Debuggen der DCF77-Signaldauer
.equ dbgDcfSig = 0 ; Debuggen von DCF77-Signalfehlern
.equ dbgDcfBits = 0 ; Debuggen emfangener DCF77-Bits
.equ dbgDcfRcvd = 0 ; Debuggen emfangener DCF77-Daten
;
; 6. Debuggen des Abspielen von Musikstuecken
.equ dbgMusic = 0 ; Debuggen des Musikabspielens
.equ dbgPlayStat = 0 ; Debuggen des Status von TC1
;
;
; =============================================
; H A R D W A R E - I N F O R M A T I O N E N
; =============================================
;
; ATmega16__________
; 1 / |40
; Sw-W o--|PB0 ADC0|--o Vpot
; Sw-Y o--|PB1 ADC1|--o Vopto
; Sw-R o--|PB2 PA2|--o DCF-signal strength
; LCD-E o--|PB3 PA3|--o NC
; LCD-RS o--|PB4 PA4|--o NC
; ISP6-MOSI o--|MOSI PA5|--o NC
; ISP6-MISO o--|MISO PA6|--o NC
; ISP6-SCK o--|SCK PA7|--o NC
; RESET o--|RST AREF|--o AREF
; VCC o--|VCC GND|--o GND
; GND o--|GND AVCC|--o AVCC
; XTAL2 o--|XTAL2 PC7|--o LCD-D7
; XTAL1 o--|XTAL1 PC6|--o LCD-D6
; NC o--|PD0 PC5|--o LCD-D5
; NC o--|PD1 PC4|--o LCD-D4
; INT0/DCF77 o--|INT0 PC3|--o LCD-D3
; NC o--|PD3 PC2|--o LCD-D2
; NC o--|PD4 PC1|--o LCD-D1
; LSP o--|OC1A PC0|--o LCD-D0
; NC o--|PD6 OC2|--o LCD-LED-K
; 20|___________|21
;
; ============================================
; P O R T S U N D P I N S
; ============================================
;
; Tastenports
.equ pKeyO = PORTB ; Port Tastenausgaenge
.equ pKeyD = DDRB ; Port Tastenrichtungen
.equ pKeyI = PINB ; Port Tasteneingaenge
.equ bKeyWO = PORTB0 ; Weisse Taste Ausgang-Pin
.equ bKeyWD = DDB0 ; Weisse Taste Richtung-Pin
.equ bKeyWI = PINB0 ; Weisse Taste Eingang-Pin
.equ bKeyYO = PORTB1 ; Gelbe Taste Ausgang-Pin
.equ bKeyYD = DDB1 ; Gelbe Taste Richtung-Pin
.equ bKeyYI = PINB1 ; Gelbe Taste Eingang-Pin
.equ bKeyRO = PORTB2 ; Rote Taste Ausgang-Pin
.equ bKeyRD = DDB2 ; Rote Taste Richtung-Pin
.equ bKeyRI = PINB2 ; Rote Taste Eingang-Pin
.equ cKeyMO = (1<<bKeyWO)|(1<<bKeyYO)|(1<<bKeyRO) ; Maske Ausgaenge
.equ cKeyMI =(1<<bKeyWI)|(1<<bKeyYI)|(1<<bKeyRI) ; Maske Eingaenge
;
; LCD-Ports
.EQU pLcdCO = PORTB ; LCD-Kontrollport Ausgang Port
.EQU pLcdCD = DDRB ; LCD-Kontrollport Richtung Port
.EQU bLcdE = PORTB3 ; LCD-Kontrollportbit Enable Pin
.EQU bLcdRs = PORTB4 ; LCD-Kontrollportbit RS Pin
.EQU bLcdRw = PORTB5 ; LCD-Kontrollportbit R/W in
.EQU pLcdDO = PORTC ; LCD-Datenport Ausgang
.EQU pLcdDD = DDRC ; LCD-Datenport Richtung
;
; DCF77-Ports
.EQU pDcfO = PORTD ; DCF77-Empfaenger-Port Ausgang
.EQU pDcfD = DDRD ; DCF77-Empfaenger-Port Richtung
.EQU pDcfI = PIND ; DCF77-Empfaenger-Port Eingang
.EQU bDcfD = DDD2 ; DCF77-Emfaenger-Portpin Richtung
.EQU bDcfI = PIND2 ; DCF77-Empfaenger-Portpin Eingang
;
; Lautsprecher-Port
.equ pSpkO = PORTD ; Lautsprecher-Port Ausgang
.equ pSpkD = DDRD ; Lautsprecher-Port Richtung
.equ bSpkO = PORTD5 ; Lautsprecher-Portpin Ausgang
.equ bSpkD = DDD5 ; Lautsrecher-Portpin Richtung
;
; LED LCD Hintergrundbeleuchtung
.EQU pLedO = PORTD ; LED-Kathode Port Ausgang
.EQU pLedD = DDRD ; LED-Kathode Port Richtung
.EQU bLedO = PORTD7 ; LED-Kathode Portpin Ausgang
.EQU bLedD = DDD7 ; LED-Kathode Portpin Richtung
;
; ADC Ports
.EQU pAdcO = PORTA ; ADC Port Ausgang
.EQU pAdcD = DDRA ; ADC Port Richtung
.EQU bAdcP = 0 ; ADC Portbit Poti-Anschluss
.EQU bAdcO = 1 ; ADC Optosensor
.equ bAdcS = 2 ; ADC DCF77-Empfaenger-Signalpegel
;
; ================================================
; T I M I N G U N D T I M E R
; ================================================
;
; TC0: 8-Bit-Timer fuer Bestimmung
; a) des Sekundenimpulses fuer die Uhr
; b) der DCF-Pulsdauer
;
; Quarzoszillator ==> TC0-Presc ==> TC0-Tick ==>
; f 3276800 Hz 64 51.200 Hz
; t 305,1758 ns 19,53125 us
; TC0-Overflow (256)
; f 200 Hz
; t 5 ms
; a) Sekundenimpuls ==> Abwaertszaehlen Sekundenteiler,
; wenn Null: neu starten und bSec-Flagge setzen
; b) DCF77-Zeitzaehler: Erhoehe DCF77-Zaehler,
; wenn Maximum Dauer erreicht: setze bDcfTO-Flagge
;
; TC1: 16-Bit-Timer fuer Tonerzeugung
; Quarz ==> TC1-Presc ==> TC1-CTC ==> 25 Hz bis
; 3276800 1 0..65535 1,6384 MHz
; CTC-COMPARE-A Interrupt:
; Erniedrigen R25:R24, wenn Null:
; Lese naechsten Compare-A-Wert (Tonhoehe),
; wenn MSB = 0xFF: Lautsprecher auf Clear (Pause)
; wenn nicht: Lautsprecher-Ausgang auf Toggle
; Lese Dauerzaehler und schreibe in R25:R24
; wenn LSB = 0x00: Musik ausschalten
;
; TC2: 8-Bit-Timer als PWM fuer die LCD-Hintergrundbeleuchtung
; und fuer Tastenerkennung
; Quarz ==> TC2-Presc ==> TC2-Ueberlauf ==> OvFlw-Int
; 3276800 ==> 64 ==> 256 200 Hz
; Lese Tasteneingaenge, wenn
; Keine Taste:
; wenn rKeyCnt nicht 0: rKeyCnt abwaerts, wenn Null:
; Flagge rKeyAct loeschen
; Irgendeine Taste:
; wenn Flagge bKeyAct Null: setze Flagge rKeyAct,
; Lade rKeyCnt
;
; ADC: Messen der drei Analogkanaele
; Quarz ==> ADC-Presc ==> ADC-1m ==> ADC-64m ==> 3 Ch
; 3276800 128 13 64 3
; = 10.256 cs/s or 97 ms per round
;
; ==========================================
; E I N S T E L L K O N S T A N T E N
; ==========================================
;
; Prozessortakt externer Quarz
.equ Clock = 3276800 ; Quarzfrequenz
;
; DCF77 Signaldauern in Millisekunden
.equ cDcfIgnoreShortTime = 20 ; Ignoriere Kurzimpulse mit weniger als 20 ms
.equ cDcfMinTime = 50 ; Minimum Signaldauer NUll- oder Eins-Bit
.equ cDcfMaxZeroTime = 150 ; Maximum Zeit Null-Bit
.equ cDcfMaxOneTime = 250 ; Maximum Zeit Eins-Bit
.equ cDcfMinInactiveTime = 700 ; Minimum inaktive Zeit bis naechstes Bit
.equ cDcfMaxInactiveTime = 1000 ; Maximum inaktive Zeit bis naechstes Bit
.equ cDcfMin59Time = 1700 ; Minimum Zeit fuer 59ste Sekunde
.equ cDcfMax59Time = 2000 ; Maximum Zeit fuer 59ste Sekunde
.equ cDcfTimeOutTime = 2500 ; Time-Out am DCF77-Signaleingang
;
; LCD Hintergrundbeleuchtung
.equ cBackPeriodsTime = 3000 ; Millisekunden Ansteuerung
.equ cBackMin = 10 ; Mindest-Wert der Helligkeit, 10 bis 255
;
; DCF77-Signalstaerke-Ausgabe in S-Stufen
.equ cSigStrTime = 2000 ; Erneuerung der Ausgabe alle ... Millisekunden
;
; Tastenprellunterdrueckung
.equ cKeyCntTime = 30 ; Taste inaktiv fuer ... Millisekunden
;
; Weckwiederholungen
.equ cAlarmRepet = 3 ; Anzahl Minuten Weckwiederholung
;
; Musikstueck-Auswahl bei Neustart
.equ cDefaultMelody = 8 ; Melodie, die beim Neustart gespielt wird
;
; =========================================================
; F E S T E + A B G E L E I T E T E K O N S T A N T E N
; =========================================================
;
; Konstanten fuer den TC0-Betrieb
.equ cTc0Presc = 64 ; TC0-Vorteiler
.equ cTc0Divider = 256 ; TC0-8-Bit-Teiler
.equ cSecDiv = clock / cTc0Presc / cTc0Divider ; Teiler bis Sekunde
.if cSecDiv>255
.error "cSecDiv out of range!"
.endif
;
; Konstanten fuer TC1-Betrieb (Tongenerator)
.equ cTc1Presc = 1 ; Vorteiler 1
;
; Konstanten fuer TC2-Betrieb (Hintergrundbeleuchtung PWM)
.equ cTc2Presc = 64 ; TC2-Vorteiler LCD-Hintergrundbeleuchtung PWM
.equ cTc2Cnt = Clock/cTc2Presc/256 ; 200 Interrupts pro Sekunde
.equ cBackMult = (255-cBackMin) ; Multiplikator fuer Optosensor-Spannung
;
; DCF-Signaldauern
.equ cPulse=(cTc0Presc*cTc0Divider*1000+clock/2)/clock ; Pulsdauer in ms
.equ cDcfIgnoreShort = cDcfIgnoreShortTime/cPulse ; Ignoriere kuerzere Pulse
.equ cDcfMin = cDcfMinTime/cPulse ; Minimum Pulse Anzahl
.equ cDcfMaxZero = cDcfMaxZeroTime/cPulse ; Maximum Null-Bit Pulse
.equ cDcfMaxOne = cDcfMaxOneTime/cPulse ; Maximum Eins-Bit Pulse
.equ cDcfMinInactive = cDcfMinInactiveTime/cPulse ; Minimum Inaktive Pulse
.equ cDcfMaxInactive = cDcfMaxInactiveTime/cPulse ; Maximum Inaktive Pulse
.equ cDcfMin59 = cDcfMin59Time/cPulse ; Minimum 59ster Sekunde Pulse
.equ cDcfMax59 = cDcfMax59Time/cPulse ; Maximum 59ster Sekunde Pulse
.equ cDcfTimeOut = cDcfTimeOutTime/cPulse ; Time-Out DCF-Eingang Pulse
;
; Tastenprell-Unterdrueckung
.equ cKeyCnt = cKeyCntTime/cPulse ; Pulse ueber die keine Taste aktiv sein darf
;
; Erneuerungszeit LCD-Hintergrundbeleuchtung
.equ cAdcPulse = 1000*128*13*64*3/clock ; Dauer einer ADC-Messrunde in ms
.equ cBackPeriods = cBackPeriodsTime/cAdcPulse ; Anzahl Perioden bis Update
.if cBackPeriods > 255
.error "cBackPeriodsTime too long!"
.endif
;
; DCF77-Signalstaerke Erneuerung
.equ cSigStr = cSigStrTime/cAdcPulse ; Anzahl Perioden bis Update
;
; Debugging aktiv?
.set fDbg=(dbgLcd>0)||(dbgAdc>0)||(dbgKey>0)||(dbgKeyState>0)
.set fDbg=fDbg||(dbgSpk>0)||(dbgDcfDur>0)||(dbgDcfSig>0)
.set fDbg=fDbg||(dbgDcfBits>0)||(dbgDcfRcvd>0)||(dbgMusic>0)
.set fDbg=fDbg||(dbgPlayStat>0)
;
; ===========================================
; R E G I S T E R - D E F I N I T I O N E N
; ===========================================
;
; R0, R1 verwendet fuer Berechnungen und diverse Zwecke
; frei: R2, R3
.def rMelody = R4 ; Melodie-Auswahl zufaellig, gesetzt durch Tasten
.def rSigStr = R5 ; Signalstaerke Update Zaehler
.def rBackCnt = R6 ; LCD-Hintergrundbeleuchtung Update Zaehler
.def rKeyCnt = R7 ; Tastendruck Zaehler
.def rDcfBitCnt = R8 ; Bit-Zaehler DCF-Bits
.def rDcfErr = R9 ; DCF77-Signal- und Auswertefehler
.def rAdc = R10 ; ADC MSB des Ergebnisses
.def rAdcL = R11 ; ADC Summe, LSB
.def rAdcH = R12 ; dto., MSB
.def rDcfL = R13 ; Letzte Puls-/Pausendauer-Laenge DCF-Signal, LSB
.def rDcfH = R14 ; dto., MSB
.def rSreg = R15 ; Sichern SREG in Interrupt-Service-Routinen
.def rmp = R16 ; Vielzweckregister
.def rimp = R17 ; Vielzweckregister in Interrupts
.def rFlag = R18 ; Flaggen
.equ bSec = 0 ; Sekunde vorbei
.equ bDcf = 1 ; Aktiver DCF77 Pulsflagge
.equ bDcfTO = 2 ; DCF77-Signal Time-Out-Flagge
.equ bAdc = 3 ; ADC 64 Messungen komplett-Flagge
.equ bKey = 4 ; Eine Taste ist gedrueckt-Flagge
.equ bKeyAct = 5 ; Tasten-Auswertung aktiv-Flagge
.equ bMinute = 6 ; Minutenwechsel, Wecken pruefen-Flagge
.def rSecDiv = R19 ; Abwaertszahler Sekunden
.def rDcfCntL = R20 ; DCF77-Pulsdauer-Zaehler, LSB
.def rDcfCntH = R21 ; dto., MSB
.def rAdcC = R22 ; ADC Messungen Zaehler
; frei: R23
.def rDurL = R24 ; Dauer von Toenen, LSB
.def rDurH = R25 ; dto., MSB
; benutzt: R27:R26 = X fuer diverse Zwecke
; benutzt: R29:R28 = Y fuer Musikspielen als Zeiger in SRAM
; benutzt: R31:R30 = Z fuer diverse Zwecke
;
; ===================================
; S R A M - D E F I N I T I O N E N
; ===================================
;
.DSEG
.ORG SRAM_START
; Aktuelle Datum- und Zeit-Information fuer Anzeige
sDateTime:
sWeekday:
.byte 1
sDay:
.byte 1
sMonth:
.byte 1
sYear:
.byte 1
sHour:
.byte 1
sMin:
.byte 1
sSec:
.byte 1
sDateTimeEnd:
sAlrmHr:
.byte 1
sAlrmMin:
.byte 1
;
; DCF77-Bits Schieberegisterbereich
sDcfBits:
.byte 8
;
; DCF77 empfangene und dekodierte Teile
sDcfRcv:
.byte 1 ; Wochentag 0..6
.byte 1 ; Tag 1..31
.byte 1 ; Monat 1..12
.byte 1 ; Jahr 0..99
.byte 1 ; Stunde 0..23
.byte 1 ; Minute 0..59
.byte 1 ; Sekunde 0..59
;
; Verarbeitung von Tastenereignissen
sKeyMode:
.byte 1 ; 0=Aus/Normal
; 1=Wecken aktiviert
; 2=Eingabe von Datum und Uhrzeit
; 3=Eingabe der Weckzeit
;
sKeyPos:
.byte 1 ; Mode 1 Mode 2 Mode 3
; min=4 min=0 min=7
; max=6 max=3 max=8
;
sKeyCnt:
.byte 1 ; ADC Periodenzaehler fuer Tastenaktionen
;
sAlarm:
.byte 2 ; Gespeicherte Weckzeit
;
sInputData:
.byte 9 ; Datum (Wochentag, Tag, Monat, Jahr),
; Zeit (Stunde, Minute, Sekunde),
; Weckzeit (Stunde, Minute)
;
sAlarmRepeat:
.byte 1 ; Wiederholzaehler Wecken
;
sMelody:
.byte 500 ; Puffer fuer Melodie zum Abspielen
sMelodyEnd:
;
; ===============================================
; R E S E T - U N D I N T - V E K T O R E N
; ===============================================
;
.CSEG
.ORG $0000
jmp Main ; Reset-Vektor
jmp Int0Isr ; INT0-Vektor
reti ; INT1-Vektor
nop
reti ; TC2COMP-Vektor
nop
jmp Tc2OvfIsr ; TC2OVF-Vektor
reti ; TC1CAPT-Vektor
nop
jmp Tc1CmpAIsr ; TC1 COMPA-Vektor
reti ; TC1COMPB-Vektor
nop
reti ; TC1OVF-Vektor
nop
jmp Tc0OvfIsr ; TC0OVF-Vektor
reti ; SPI-STC-Vektor
nop
reti ; USART-RXC-Vektor
nop
reti ; USART-UDRE-Vektor
nop
reti ; USART-TXC-Vektor
nop
jmp AdcIsr ; ADC-Vektor
reti ; EE_RDY-Vektor
nop
reti ; ANA_COMP-Vektor
nop
reti ; TWI-Vektor
nop
reti ; INT2-Vektor
nop
reti ; TC0_COMP-Vektor
nop
reti ; SPM_RDY-Vektor
nop
;
; =====================================================
; I N T E R R U P T - S E R V I C E - R O U T I N E N
; =====================================================
;
; DCF77-Empfaenger Puls-Eingang
Int0Isr:
in rSreg,SREG ; Sichere SREG
mov rimp,rDcfCntL ; Unterdruecke Kurzsignale
cpi rimp,cDcfIgnoreShort ; Kuerzer als Minimum Puls?
brcs Int0IsrRet ; Ja, ignoriere
; Valides DCF-Signal gefunden
mov rDcfL,rDcfCntL ; Kopie Zaehler LSB
mov rDcfH,rDcfCntH ; dto., MSB
sbr rFlag,1<<bDcf ; Setze Flagge
clr rDcfCntL ; Loeschen LSB Zaehler
clr rDcfCntH ; dto., MSB
Int0IsrRet:
out SREG,rSreg ; SREG wieder herstellen
reti
;
; TC0-Ueberlauf-Interrupt, Sekunden/DCF77-PUlsdauer-Timer
Tc0OvfIsr:
in rSreg,SREG ; Sichern Sreg
dec rSecDiv ; Sekundenteiler abwaerts
brne Tc0OvfIsr1 ; Nicht Null
ldi rSecDiv,cSecDiv ; Neustart Sekundenteiler
sbr rFlag,1<<bSec ; Setze Sekundenflagge
Tc0OvfIsr1:
inc rDcfCntL ; Erhoehe DCF-Pulsdauer-Zaehler
brne Tc0OvfIsr2 ; Kein Uebertrag zum MSB
inc rDcfCntH ; Uebertrag ins MSB
Tc0OvfIsr2:
cpi rDcfCntH,High(cDcfTimeOut) ; MSB bei Time-Out?
brcs Tc0OvfIsr3 ; Nein
cpi rDcfCntL,Low(cDcfTimeOut) ; LSB bei Time-Out?
brcs Tc0OvfIsr3 ; Nein
sbr rFlag,1<<bDcfTO ; Setze Time-Out-Flagge
clr rDcfCntL ; Neustart DCF-Pulszaehler
clr rDcfCntH
Tc0OvfIsr3:
out SREG,rSreg ; Stelle SREG wieder her
reti
;
; TC1 Compare Match Interrupt, spiele Musik
Tc1CmpAIsr:
in rSreg,SREG ; Sichere SREG
sbiw rDurL,1 ; Dauerzaehler abwaerts
brne Tc1CmpAIsrRet
ld rimp,Y+ ; Lese MSB zuerst
out OCR1AH,rimp ; In MSB Vergleichsregister
cpi rimp,0xFF ; MSB Lautsprecher aus?
ld rimp,Y+ ; Lese LSB als nachstes
out OCR1AL,rimp ; In LSB Vergleichsregister
brne Tc1CmpAIsr1 ; Lautsprecher nicht abschalten
cpi rimp,0xFF ; LSB Lautsrecher aus?
brne Tc1CmpAIsr1 ; Lautsprecher nicht abschalten
ldi rimp,(1<<COM1A1) ; Lautsprecher aus
rjmp Tc1CmpAIsr2
Tc1CmpAIsr1:
ldi rimp,(1<<COM1A0) ; Ausgang OCR1A auf Toggle
Tc1CmpAIsr2:
out TCCR1A,rimp ; Setze OCR1A auf Toggle/Clear
ld rDurH,Y+ ; Lese MSB Dauer
ld rDurL,Y+ ; Lese LSB Dauer
mov rimp,rDurH ; Teste auf 0x0000
or rimp,rDurL
brne Tc1CmpAIsrRet ; Musikstueck weiter spielen
; Musikausgabe ausschalten
ldi rimp,1<<COM1A1 ; OCR1A Ausgang auf Clear
out TCCR1A,rimp
ldi rimp,(1<<TOIE0)|(1<<TOIE2) ; Abschalten TC1 int
out TIMSK,rimp
ldi rimp,High(clock / 2000 - 1) ; CTC auf 1 kHz
out OCR1AH,rimp
ldi rimp,Low(clock / 2000 - 1)
out OCR1AL,rimp
Tc1CmpAIsrRet:
out SREG,rSreg ; Stelle SREG wieder her
reti
;
; TC2-Ueberlauf Interrupt, Hintergrundbeleuchtung und Tasten
Tc2OvfIsr:
in rSreg,SREG ; Sichere SREG
in rimp,pKeyI ; Lese Tasten-Eingang
andi rimp,cKeyMI ; Isoliere Tasteneingaenge
cpi rimp,0x07 ; Alle Tasten aus?
brne Tc2OvfIsrKey ; Nein, Taste verarbeiten
tst rKeyCnt ; Tastenzaehler auf Null?
breq Tc2OvfIsrRet ; Ja, ignoriere
dec rKeyCnt ; Erniedrige Tastenzaehler
brne Tc2OvfIsrRet ; Nicht bei Null, raus
cbr rFlag,1<<bKeyAct ; Loesche Taste-Aktiv-Flagge
rjmp Tc2OvfIsrRet
Tc2OvfIsrKey:
sbrs rFlag,bKeyAct ; Flagge bKeyAct schon gesetzt?
sbr rFlag,(1<<bKeyAct)|(1<<bKey) ; Setze Tastenflagge
ldi rimp,cKeyCnt ; Setze den Tastenzaehler auf Anfang
mov rKeyCnt,rimp
in rMelody,TCNT1L ; Lese Zaehler 1 LSB als Zufallsgenerator
Tc2OvfIsrRet:
out SREG,rSreg ; Stelle SREG wieder her
reti
;
; ADC-Messung komplett Interrupt Service Routine
AdcIsr:
in rSreg,SREG ; Sichere SREG
in rimp,ADCL ; Lese LSB Ergebnis
add rAdcL,rimp ; Addiere zur Summe
in rimp,ADCH ; Lese MSB Ergebnis
adc rAdcH,rimp ; Addiere zur Summe mit Carry
dec rAdcC ; Zaehler abwaerts
breq AdcIsr1 ; Zaehler ist Null, kein Neustart
ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(ADPS0)
out ADCSRA,rimp ; Starte naechste Wandlung
out SREG,rSreg ; Stelle SREG wieder her
reti
AdcIsr1:
mov rAdc,rAdcH ; Kopiere MSB
clr rAdcL ; Loesche Summe
clr rAdcH
sbr rFlag,1<<bAdc ; Setze ADC-Flagge
ldi rAdcC,64 ; Neustart Zaehler
out SREG,rSreg ; Stelle SREG wieder her
reti
;
; =====================================
; H A U P T P R O G R A M M I N I T
; =====================================
;
Main:
; Init Stapel
ldi rmp, HIGH(RAMEND) ; Init MSB Stapel
out SPH,rmp
ldi rmp, LOW(RAMEND) ; Init LSB Stapel
out SPL,rmp
; Init die Tasten-Eingangs-Pins
cbi pKeyD,bKeyWD ; Weisse Taste als Eingang
sbi pKeyO,bKeyWO ; Pull-Up Weisse Taste an
cbi pKeyD,bKeyYD ; Gelbe Taste als Eingang
sbi pKeyO,bKeyYO ; Pull-Up Gelbe Taste an
cbi pKeyD,bKeyRD ; Rote Taste als Eingang
sbi pKeyO,bKeyRO ; Pull-Up Rote Taste an
; Initiiere TC2 als LCD-Hintergrundbeleuchtungs-PWM
sbi pLedD,bLedD ; Setze OCR2 Portbit als Ausgang
ldi rmp,0x80 ; Halbe Helligkeit
out OCR2,rmp
ldi rmp,(1<<WGM21)|(1<<WGM20)|(1<<COM21)|(1<<COM20)|(1<<CS22) ; Fast PWM, Vort=64
out TCCR2,rmp
; Initiiere die LCD-Ports und die LCD
cbi pLcdCO,bLcdE
sbi pLcdCD,bLcdE ; Enable-Pin als Ausgang
sbi pLcdCD,bLcdRs ; RS-Pin als Ausgang
cbi pLcdCO,bLcdRs ; RS auf Low
sbi pLcdCD,bLcdRw ; R/W-Pin als Ausgang
cbi pLcdCO,bLcdRw ; R/W-Pin auf Low
ldi rmp,0xFF ; LCD-Datenport als Ausgang
out pLcdDD,rmp
call LcdInit ; Initieren der LCD
; Falls debugging aktiv
.if fDbg
call DebugActive ; Ausgabe Meldung auf LCD
.endif
; Fall debugging der LCD aktiv: Stop hier
.if dbgLcd == 1
LoopLcd:
rjmp LoopLcd ; Stop Betrieb
.endif
; Verzoegerung zwischen Eroeffnung und Maske
ldi rmp,20
Delay:
call LcdWtMax
dec rmp
brne Delay
call LcdFrame ; LCD-Ausgabemaske schreiben
call LcdNoBlink ; Kein Blinken
; Falls debugging des ADC aktiv: Fuehre Messungen aus
.if dbgAdc == 1
jmp DebugAdc ; Fuehre Messungen aus
.endif
; Falls Tasten-debugging aktiv: Stelle Tastenstatus dar
.if dbgKey == 1
jmp DebugKey ; Stelle Tastenstatus dar
.endif
; Falls Lautsprecher-Debug aktiv: Spiele Ton ab
.if dbgSpk == 1
jmp DebugSpeaker ; Ton an
.endif
; Falls DCF77-Signal-Debug aktiv: Stelle Signale dar
.if dbgDcfDur == 1 ; Debug DCF signal
call DebugDcfDur ; Zeige DCF77-Signale an
.endif
; Starte AD-Wandlung
clr rAdcL ; Loesche ADC-Summe
clr rAdcH
ldi rAdcC,64 ; 64 Messungen
ldi rmp,1<<REFS0 ; Beginne mit Kanal 0
out ADMUX,rmp
ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(ADPS0)
out ADCSRA,rmp ; Starte erste Wandlung
; Initiiere SRAM-Inhalte
rcall InitDT
lds rmp,sDateTime+7 ; Kopiere Weckzeit nach sAlarm
sts sAlarm,rmp
lds rmp,sDateTime+8
sts sAlarm+1,rmp
rcall SetAlarmOff ; Wecken ausschalten und anzeigen
rcall DateTimeLcd
rcall DcfClearBuffer ; Initiere den DCF-Emfangspuffer
; Initiiere Flaggen und Softwarezaehler
clr rFlag ; Alle Flaggen aus
clr rDcfErr ; Loesche DCF77-Fehler
clr rDcfBitCnt ; Loesche DCF77 Bitzaehler
ldi rmp,cBackPeriods ; Hintergrundbeleuchtungs-Updateperiode
mov rBackCnt,rmp
clr rmp ; Initiere Eingabemodus
sts sKeyMode,rmp ; Eingabe aus
sts sAlarmRepeat,rmp ; Weckwiederholung auf Null
rcall DisplMenue ; Initiiere Menuezeile
; Initiiere TC0 als Zeitzaehler fuer Sekunde und DCF77-Signal
ldi rSecDiv,cSecDiv ; Sekundenabwaertszaehler
ldi rmp,(1<<CS01)|(1<<CS00) ; Vorteiler auf 64
out TCCR0,rmp
; Initiiere TC1 als Musikspieler, CTC-Modus
ldi rmp,High(clock/2000-1) ; Init Compare A Register
out OCR1AH,rmp
ldi rmp,Low(clock/2000-1)
out OCR1AL,rmp
ldi rmp,(1<<COM1A1) ; Compare match OCR1A auf Clear
out TCCR1A,rmp
ldi rmp,(1<<WGM12)|(1<<CS10) ; CTC-A, Vorteiler=1
out TCCR1B,rmp
sbi pSpkD,bSpkD ; OCR1A als Ausgang
; Initiiere Musik-Ausgabe
ldi rmp,cDefaultMelody ; Zu Beginn zu startende Melodie
rcall MusicConvert ; In das SRAM konvertieren
; Ermoegliche Timer-Interrupts
ldi rmp,(1<<TOIE2)|(1<<OCIE1A)|(1<<TOIE0) ; Timer-Interrupts TC0 und TC2
out TIMSK,rmp
; Schlafmodus, Externe Interrupts
ldi rmp,(1<<SE)|(1<<ISC00) ; Schlafmodus Idle, INT0 bei beiden Flanken
out MCUCR,rmp
; Externe INT0 einschalten
ldi rmp,1<<INT0
out GICR,rmp
sei ; Interrupts ermoeglichen
;
; =================================
; P R O G R A M M S C H L E I F E
; =================================
;
Loop:
sleep ; Schlafen legen
nop ; Dummy fuer Aufwecken
sbrc rFlag,bDcf ; DCF77-Impuls erkannt?
rcall DcfAct ; Behandle aktiven DCF77-Impuls
sbrc rFlag,bDcfTO ; DCF77-Time-Out?
rcall DcfTimeOut ; Time-Out am DCF-Signaleingang
sbrc rFlag,bSec ; Sekunde abgelaufen?
rcall IncSec ; Erhoehe Sekunden
sbrc rFlag,bMinute ; Minutenflagge fuer Wecken?
rcall CheckMinute ; Ja, pruefe Wecken
sbrc rFlag,bKey ; Taste gedrueckt?
rcall KeyPress ; Gedrueckte Taste verarbeiten
sbrc rFlag,bAdc ; ADC-Wandlung komplett?
rcall AdcCmplt ; ADC Messung ist komplett
; Falls Tastenstatus dargestellt werden soll
.if dbgKeyState == 1
call KeyState ; Tastenstatus ausgeben
.endif
rjmp loop ; Zurueck nach Loop
;
; ===========================================
; V E R A R B E I T E D C F I M P U L S E
; ===========================================
;
; Aktiver Impuls DCF77
DcfAct:
cbr rFlag,1<<bDcf ; Loesche Flagge
; Falls debugging der DCF-Signaldauer aktiv
.if dbgDcfDur == 1
call DebugDcfDurDispl ; Debuggen der DCF77-Signaldauer
.endif
ldi rmp,Low(cDcfMin) ; Pruefe Minimum
sub rmp,rDcfL
ldi rmp,High(cDcfMin)
sbc rmp,rDcfH
ldi rmp,1 ; Fehlernummer 1
brcc GotoDcfErr
DcfAct1:
ldi rmp,Low(cDcfMaxOne) ; Pruefe Eins-Bit
sub rmp,rDcfL
ldi rmp,High(cDcfMaxOne)
sbc rmp,rDcfH
brcc Dcf01Ok ; Null oder Eins ok
ldi rmp,Low(cDcfMinInactive) ; Inaktivdauer Minimum pruefen
sub rmp,rDcfL
ldi rmp,High(cDcfMinInactive)
sbc rmp,rDcfH
ldi rmp,2 ; Fehlernummer 2
brcc GotoDcfErr
DcfAct2:
ldi rmp,Low(cDcfMaxInactive) ; Inaktivdauer Maximum pruefen
sub rmp,rDcfL
ldi rmp,High(cDcfMaxInactive)
sbc rmp,rDcfH
brcc DcfOk ; Inaktiv-Dauer ok
ldi rmp,Low(cDcfMin59) ; Fehlendes 59stes Bit Minimum?
sub rmp,rDcfL
ldi rmp,High(cDcfMin59)
sbc rmp,rDcfH
ldi rmp,3 ; Fehlernummer 3
brcc GotoDcfErr
ldi rmp,Low(cDcfMax59) ; Fehlendes 59stes Bit Maximum
sub rmp,rDcfL
ldi rmp,High(cDcfMax59)
sbc rmp,rDcfH
brcc Dcf59Ok ; Fehlendes Bit ok
ldi rmp,4 ; Fehlernummer 4
rjmp GotoDcfErr
DcfOk: ; Signaldauer ok
ret ; Zeit ok
GotoDcfErr:
mov rDcfErr,rmp ; Fehler Signaldauer
rjmp DcfReport ; Fehler anzeigen
;
; Der DCF77-Empfaenger hat ein korrektes Bit empfangen
; Schiebe dieses in den SRAM-Puffer
Dcf01Ok:
ldi rmp,Low(cDcfMaxZero) ; Null oder Eins?
sub rmp,rDcfL
ldi rmp,High(cDcfMaxZero)
sbc rmp,rDcfH ; Carry ist gesetzt wenn Eins
ldi ZH,High(sDcfBits+8) ; Zeige Z auf Bit-Puffer
ldi ZL,Low(sDcfBits+8)
ld rmp,-Z ; Schiebe in Byte 7
ror rmp
st Z,rmp
ld rmp,-Z ; Schiebe in Byte 6
ror rmp
st Z,rmp
ld rmp,-Z ; Schiebe in Byte 5
ror rmp
st Z,rmp
ld rmp,-Z ; Schiebe in Byte 4
ror rmp
st Z,rmp
ld rmp,-Z ; Schiebe in Byte 3
ror rmp
st Z,rmp
ld rmp,-Z ; Schiebe in Byte 2
ror rmp
st Z,rmp
ld rmp,-Z ; Schiebe in Byte 1
ror rmp
st Z,rmp
ld rmp,-Z ; Schiebe in Byte 0
ror rmp
st Z,rmp
inc rDcfBitCnt ; Zaehle empfangene Bits
; Falls DCF77 Bit debugging eingeschaltet
.if dbgDcfBits == 1
call DebugDcfBitEcho ; Echo DCF-Bits auf der LCD
.endif
ret
;
; Der DCF77-Empfaenger hat das fehlende Sekundenbit erkannt
; Pruefe ob alle Bits korrekt sind und setze Datum und Zeit
Dcf59Ok:
; Pruefe alle Signaldauern
tst rDcfErr ; Alle Signaldauern ok?
breq Dcf59Ok1 ; Ja
; Signaldauern nicht ok
clr rDcfBitCnt ; Neustart Bitzaehler
clr rDcfErr ; Neustart Fehlerzaehler
ret
Dcf59Ok1:
; Sind 59 Bits gezaehlt?
mov rmp,rDcfBitCnt
cpi rmp,59 ; Nur 58 oder weniger oder mehr als 59?
breq Dcf59Ok2 ; Nein, Anzahl korrekt
; Fehler gefunden
ldi rmp,6 ; Zeige Fehlermeldung Anzahl Bits an
mov rDcfErr,rmp ; Fehlernummer 6
rcall DcfReport ; Zeige Fehler an
clr rDcfBitCnt ; Neustart Bitzaehler
clr rDcfErr ; Neustart Fehlerzaehler
ret
Dcf59Ok2:
; Starte Bitanalyse
clr rDcfBitCnt ; Neustart Bitzaehler
ldi rmp,7 ; Beginne mit Fehlernummer 7
mov rDcfErr,rmp
; Pruefe ob Bit 1 = 0
lds rmp,sDcfBits ; Lese aeltestes Byte
sbrc rmp,5 ; DCF-Bit 1 ist Bit 5
rjmp DcfReport ; Fehler #7
; Pruefe Minuten-Einer
inc rDcfErr ; Naechste Fehler-#
lds ZL,sDcfBits+3 ; Lese Minuten-Einer
lsr ZL ; Rechtsschieben
lsr ZL ; Noch mal Rechtsschieben
andi ZL,0x0F ; Isoliere Einer
cpi ZL,10 ; Groesser als 9?
brcs Dcf59Ok4 ; Nein, Einer ok
rjmp DcfReport ; Fehlerausgabe
Dcf59Ok4:
; Pruefe Minuten-Zehner
inc rDcfErr ; Naechste Fehler-#
lds ZH,sDcfBits+4 ; Lese Minuten-Zehner
andi ZH,0x01 ; Isoliere letztes Bit
lds rmp,sDcfBits+3 ; Lese Minuten-20er und 10er
lsl rmp ; 20er Linksschieben
rol ZH ; und in ZH einrollen
lsl rmp ; 10er Linksschieben
rol ZH ; und in ZH einrollen
andi ZH,0x07 ; Isoliere 40/20/10 Minuten
cpi ZH,6 ; Zehner groesser als 6?
brcs Dcf59Ok5 ; Nein
rjmp DcfReport ; Fehlerausgabe
Dcf59Ok5:
rcall BitCombine ; Kombiniere Zehner und Einer
sts sDcfRcv+5,ZL ; Speichere Minuten in Puffer
; Paritaet Minuten pruefen
inc rDcfErr ; Naechste Fehler-#
lds ZL,sDcfBits+3 ; Lese Minuten-Einer
lds ZH,sDcfBits+4 ; Lese Minuten-Zehner
lsr ZH ; Zusammenschieben
ror ZL
lsr ZH
ror ZL
rcall CheckParityZL ; Pruefe Paritaet in ZL
breq Dcf59Ok6 ; In Ordnung
rjmp DcfReport ; Paritaetsfehler Minuten
Dcf59Ok6:
; Konvertiere Stunden-Einer
inc rDcfErr ; Naechste Fehler-#
lds ZL,sDcfBits+4 ; Lese Stunden-Einer
lsr ZL
lsr ZL
andi ZL,0x0F ; Isoliere Stunden-Einer
cpi ZL,10 ; Groesser als 9?
brcs Dcf59Ok7 ; Nein
rjmp DcfReport ; Fehlerausgabe
Dcf59Ok7:
; Konvertiere Stunden-Zehner
inc rDcfErr ; Naechste Fehler-#
lds ZH,sDcfBits+4 ; Lese Stunden-Zehner
lsl ZH ; Links schieben
rol ZH ; und von rechts her einschieben
rol ZH
andi ZH,0x03 ; Isoliere 20er und 10er
cpi ZH,0x03 ; Mehr als 20?
brcs Dcf59Ok8 ; Nein, ok
rjmp DcfReport ; Fehlerausgabe
Dcf59Ok8:
rcall BitCombine ; Kombiniere Zehner und Einer
sts sDcfRcv+4,ZL ; Speichern Stunden in Puffer
; Pruefe Paritaet Stunden
inc rDcfErr ; Naechste Fehler-#
lds ZL,sDcfBits+4 ; Lese Stunden-Einer
lds ZH,sDcfBits+5 ; und Zehner nochmals
lsr ZH ; Kombiniere in ZL
ror ZL
lsr ZL
rcall CheckParityZL ; Pruefe Paritaet in ZL
breq Dcf59Ok9 ; Korrekt
rjmp DcfReport ; Fehlerausgabe
Dcf59Ok9:
; Konvertiere Tag-Einer
inc rDcfErr ; Naechste Fehler-#
lds ZL,sDcfBits+5 ; Lese Tag-Einer
lsr ZL ; Rechtsschieben
andi ZL,0x0F ; Isoliere Tages-Einer
cpi ZL,10 ; Groesser als 9?
brcs Dcf59Ok10 ; Nein, ok
rjmp DcfReport ; Fehlerausgabe
Dcf59Ok10:
; Konvertiere Tages-Zehner
inc rDcfErr ; Naechste Fehler-#
lds ZH,sDcfBits+5 ; Lese Tages-Zehner
swap ZH ; Oberes in unteres Nibble
lsr ZH ; Eine osition rechts
andi ZH,0x03 ; Isoliere Bit 0 und 1
rcall BitCombine ; Kombiniere ZH und ZL in ZL
sts sDcfRcv+1,ZL ; Speichern Tag in Puffer
tst ZL ; Tag = 0?
brne Dcf59Ok11 ; Nein, ok
rjmp DcfReport ; Fehlerausgabe
Dcf59Ok11:
; Konvertiere Wochentag
inc rDcfErr ; Naechste Fehler-#
lds ZL,sDcfBits+5 ; Lese Wochentag, Teil 1
lds ZH,sDcfBits+6 ; Lese Wochentag, Teil 2
lsl ZL ; Kombiniere in ZH
rol ZH
andi ZH,0x07 ; Isoliere Wochentag
brne Dcf59Ok12 ; Nicht Null, ok
rjmp DcfReport ; Fehlerausgabe
Dcf59Ok12:
dec ZH ; Wochentag erniedrigen (0 bis 6)
sts sDcfRcv,ZH ; Speichern Wochentag in Puffer
; Konvertiere Monat
inc rDcfErr ; Naechste Fehler-#
lds rmp,sDcfBits+6 ; Lese Monat
andi rmp,0x7C ; Isolaiere Monat-Zehner und Einer
lsr rmp ; Linksschieben
lsr rmp
sbrc rmp,4 ; Monat-Zehner = 1?
subi rmp,-10-16 ; Ja, addiere zehn und loesche Bit 4
sts sDcfRcv+2,rmp ; Speichern Monat in Puffer
tst rmp ; Monat = 0?
brne Dcf59Ok13 ; Nein, ok
rjmp DcfReport ; Fehlerausgabe
Dcf59Ok13:
; Konvertiere Jahres-Einer
inc rDcfErr ; Naechste Fehler-#
lds ZL,sDcfBits+6 ; Lese Jahres-Einer, Teil 1
lds ZH,sDcfBits+7 ; Lese Jahres-Einer, Teil 2
lsl ZL
rol ZH
mov ZL,ZH
andi ZL,0x0F ; Isoliere Jahres-Einer
cpi ZL,10 ; Groesser als 9?
brcs Dcf59Ok14 ; Nein, ok
rjmp DcfReport ; Fehlerausgabe
Dcf59Ok14:
; Konvertiere Jahres-Zehner
inc rDcfErr ; Naechste Fehler-#
lds ZH,sDcfBits+7 ; Lese Jahres-Zehner
lsr ZH
lsr ZH
lsr ZH
andi ZH,0x0F ; Isoliere Jahres-Zehner
cpi ZH,10 ; Greoesser als 9?
brcs Dcf59Ok15 ; Nein, ok
rjmp DcfReport ; Fehlerausgabe
Dcf59Ok15:
rcall BitCombine ; Kombiniere Jahres-Zehner und -Einer
sts sDcfRcv+3,ZL ; Speichern Jahr in Puffer
; Pruefe Datumsparitaet
inc rDcfErr ; Naechste Fehler-#
lds ZL,sDcfBits+5 ; Lese Datum Byte 1
andi ZL,0xFE ; Loesche letztes Bit
rcall CheckParityZL ; Beginne Paritaetspruefung
lds ZL,sDcfBits+6 ; Lese Datum Byte 2
rcall CheckParityZL1 ; Setze Paritaetspruefung fort
lds ZL,sDcfBits+7 ; Lese Datum Byte 3
rcall CheckParityZL1 ; Setze Paritaetspruefung fort
breq Dcf59Ok16 ; Datumsparitaet ok
rjmp DcfReport ; Fehlerausgabe
Dcf59Ok16:
lds rmp,sKeyMode ; Eingabe-Modus aktiv?
cpi rmp,2 ; Modus 2 aktiv?
breq Dcf59Ok18 ; Ja, Datum/Zeit nicht aendern
clr rmp ; Sekunden im Puffer auf Null
sts sDcfRcv+6,rmp
ldi XH,High(sDateTime) ; Zeige X auf Datum/Zeit
ldi XL,Low(sDateTime)
ldi ZH,High(sDcfRcv) ; Zeige Z auf Emfangspuffer
ldi ZL,Low(sDcfRcv)
ldi rmp,7 ; Kopiere sieben Bytes
Dcf59Ok17:
ld R0,Z+ ; Lese empfangene Datum/Zeit
st X+,R0 ; Kopiere nach Datum/Zeit
dec rmp ; Anzahl abwaerts zaehlen
brne Dcf59Ok17 ; Weiter kopieren
; Neustart der Sekunden bei 0
; Verzoegerung durch Auswertung: 486 Takte
; Entspricht 486/64 Timer 0 Ticks
; Entspricht 486/64/256 Timer 0 Overflows
; = 0,03 Verzoegerung durch Auswertung
; Liegt unterhalb von Korrekturbedarf fuer cSecDiv
ldi rSecDiv,cSecDiv ; Neustart Sekunden-Teiler
ldi XH,High(sDateTime+3) ; Zeige mit X auf Jahr
ldi XL,Low(sDateTime+3)
rcall DateTimeLcd ; Erneuern der Datum/Zeit auf der LCD
Dcf59Ok18:
sbr rFlag,1<<bMinute ; Setze Minutenwechsel-Flagge
clr rDcfErr ; Error # = 0
rjmp DcfReport
;
; Kombiniert Einer-Bits in ZL mit Zehner-Bits in ZH
; Ergebnis in ZL
BitCombine:
mov rmp,ZH ; Kopiere Zehner
add ZH,ZH ; Multipliziere mit 2
add ZH,ZH ; Multipliziere mit 4
add ZH,rmp ; Addiere Zehner, 5-faches
add ZH,ZH ; Multipliziere mit 2, 10-faches
add ZL,ZH ; Addiere Zehner zu Einern
ret
;
; Pruefe Paritaet von ZL, Z gesetzt wenn gerade
; Zaehlt die Anzahl Einsen
; Kehrt mit Z gesetzt zurueck wenn Anzahl gerade
CheckParityZL: ; Beginne Paritaetspruefung
clr rmp
CheckParityZL1: ; Setze Paritaetspruefung fort
tst ZL ; Schon keine Einsen mehr?
breq CheckParityZL2 ; Ja
lsr ZL ; Schiebe ein Bit ins Carry
brcc CheckParityZL1 ; Bit ist eine Null
inc rmp ; Bit ist Eins, zaehle
rjmp CheckParityZL1 ; Weiter pruefen
CheckParityZL2:
andi rmp,0x01 ; Letztes Bit des Zaehlers=Null?
ret
;
; Loesche den DCF77-Empfangspuffer im SRAM
DcfClearBuffer:
ldi XH,High(sDcfRcv) ; An den Beginn des Puffers
ldi XL,Low(sDcfRcv)
ldi ZL,0xFF ; Mit FF fuellen
ldi rmp,6 ; Sechs Bytes
DcfClearBuffer1:
st X+,ZL
dec rmp
brne DcfClearBuffer1
ret
;
; DCF77-Signal-Eingang hat Time-out
; Erkennung in der TC0 Interrupt Service Routine
; Loese Fehler #5 aus
DcfTimeOut:
cbr rFlag,1<<bDcfTO ; Loesche die Flagge
ldi rmp,5 ; Fehler # 5
mov rDcfErr,rmp
rjmp DcfReport ; Fehler anzeigen
;
; Anzeige von DCF77-Resultaten/Fehlern
; Fehler # in rDcfErr
DcfReport:
ldi ZH,2 ; Kurzbericht-Position auf LCD
ldi ZL,cDcfPos ; Position der Ausgabe
call LcdPos ; LCD positionieren
mov rmp,rDcfErr ; Fehlernummer in rDcfErr nach rmp
cpi rmp,6 ; Anzahl empfangener Bits falsch?
brne DcfReport1 ; Nein, andere Fehler-#
mov R0,rDcfBitCnt ; Kopiere Anzahl empfangener Bits
call LcdDec2 ; Schreibe in Dezimalformat auf die LCD
rjmp DcfReport3 ; Pruefe auf zusaetzliche Ausgabe
; Fehlernummer nicht 6
DcfReport1:
cpi rmp,cDcfErrMax ; Pruefe ob innerhalb zulaessiger Grenzen
brcs DcfReport2 ; Ja
ldi rmp,cDcfErrMax-1 ; Ausserhalb, setze auf letzte Fehlernummer
DcfReport2:
; Multipliziere Fehlernummer mit 22
clr R0 ; R0 ist Addierer fuer Carry
lsl rmp ; Multipliziere mit 2
lsl rmp ; Multipliziere mit 4
mov XL,rmp ; Kopiere 4-faches nach X
clr XH
lsl XL ; Multipliziere mit 8
rol XH
lsl XL ; Multipliziere mit 16
rol XH
add XL,rmp ; Addiere 4-faches um 20-faches zu erhalten
adc XH,R0 ; Addiere Null und Carry
lsr rmp ; Division des 4-fachen durch 2
add XL,rmp ; Addiere zum 22-fachen
adc XH,R0 ; Addiere Null und Carry
; Addiere zum Tabellenanfang
ldi ZH,High(2*DcfReports) ; Z auf Texttabelle
ldi ZL,Low(2*DcfReports)
add ZL,XL ; Addiere Tabellenablage
adc ZH,XH
; Zeige Kuerzel an
lpm rmp,Z+ ; Lese erstes Zeichen aus Tabelle
call LcdChar ; und zeige an
lpm rmp,Z+ ; Lese zweites Zeichen aus Tabelle
rcall LcdChar ; und zeige an
DcfReport3:
; Wenn Langversion angezeigt werden soll
.if dbgDcfSig == 1
call DebugDcfSig2 ; Zeige lange Version an
.endif
; Wenn empfangene Signale angezeigt werden sollen
.if dbgDcfRcvd == 1
call DebugDcfRcvd ; Zeige empfangene Signale an
rcall DcfClearBuffer ; Setze den DCF-Puffer auf FF
.endif
rjmp ActivatePos ; Wenn Eingabe dann Eingabecursor setzen
;
; Alle DCF77-Fehlerberichte
; Zeichen 1+2: Kuerzel
; Zeichen 3..22: Lange Version fuer Debug
DcfReports:
; Wenn die englische Version erzeugt werden soll
.if LangEN == 1
.db "okAll DCF bits ok. ",0,0 ; 0 DCF kein Fehler
.db "s1Signal too short! ",0,0 ; 1
.db "s2Signal in between!",0,0 ; 2
.db "s3Signal too long! ",0,0 ; 3
.db "s4Signal minute! ",0,0 ; 4
.db "s5No signal! ",0,0 ; 5
.db "nnnn"
LcdErr6:
.db " bits! ",0,0 ; 6
.db "E0Bit 1 not zero! ",0,0 ; 7
.db "MOMinute ones! ",0,0 ; 8
.db "MTMinute tens! ",0,0 ; 9
.db "PMMinute parity! ",0,0 ; 10
.db "SOHours ones! ",0,0 ; 11
.db "STHours tens! ",0,0 ; 12
.db "PHHours parity! ",0,0 ; 13
.db "T1Day ones! ",0,0 ; 14
.db "T0Day zero! ",0,0 ; 15
.db "W0Weekday zero! ",0,0 ; 16
.db "M0Month zero! ",0,0 ; 17
.db "YOYear ones! ",0,0 ; 18
.db "YTYear tens! ",0,0 ; 19
.db "PDParity date! ",0,0 ; 20
.db "ueUnknown error! ",0,0 ; 21
.else
; Wenn die deutsche Version erzeugt werden soll
.db "okAlle DCF Bits ok. ",0,0 ; 0 DCF kein Fehler
.db "s1Signal zu kurz! ",0,0 ; 1
.db "s2Signal dazwischen!",0,0 ; 2
.db "s3Signal zu lang! ",0,0 ; 3
.db "s4Signal Minute! ",0,0 ; 4
.db "s5Kein Signal! ",0,0 ; 5
.db "nnnn"
LcdErr6:
.db " Bits! ",0,0 ; 6
.db "E0Bit 1 nicht Null! ",0,0 ; 7
.db "MEMinuten Einer! ",0,0 ; 8
.db "MZMinuten Zehner! ",0,0 ; 9
.db "PMMinuten Paritaet! ",0,0 ; 10
.db "SEStunden Einer! ",0,0 ; 11
.db "SZStunden Zehner! ",0,0 ; 12
.db "PSStunden Paritaet! ",0,0 ; 13
.db "TETag Einer! ",0,0 ; 14
.db "T0Tag ist Null! ",0,0 ; 15
.db "W0Wochentag Null! ",0,0 ; 16
.db "M0Monat gleich Null!",0,0 ; 17
.db "JEJahr Einer! ",0,0 ; 18
.db "JZJahr Zehner! ",0,0 ; 19
.db "PDParitaet Datum! ",0,0 ; 20
.db "ufUnbek. Fehler! ",0,0 ; 21
.endif
.equ cDcfErrMax = 22 ; Letzter Fehler + 1
;
; =====================================
; S E K U N D E N B E H A N D E L N
; =====================================
;
IncSec:
cbr rFlag,1<<bSec ; Sekundenflagge loeschen
; Wenn Debug der Musikausgabe gewaehlt Status ausgeben
.if dbgMusic == 1
call DebugMusic ; Anzeigen Status
.endif
; Wenn Debug von TC1 Spielstatus gewaehlt Status ausgeben
.if dbgPlayStat == 1
call DebugPlayStat
.endif
lds rmp,sKeyMode ; Eingabe von Datum/Zeit?
cpi rmp,2
brne IncSec1 ; Eingabemodus nicht aktiv, erhoehen
ret ; Eingabemodus aktiv, Zeit nicht aendern
IncSec1:
ldi ZH,High(sDateTime) ; Z auf sDateTime
ldi ZL,Low(sDateTime)
ldd rmp,Z+6
inc rmp ; Erhoehe Sekunden
std Z+6,rmp ; und speichere
cpi rmp,60 ; 60 Sekunden erreicht?
brcc IncMin
rjmp SecLcd ; Zeige Sekunden an
; Erhoehe Minuten
IncMin:
clr rmp ; Sekunden auf Null
std Z+6,rmp
ldd rmp,Z+5 ; Lese Minuten
inc rmp ; Erhoehe Minuten
sbr rFlag,1<<bMinute ; Setze Minuten-Flagge
std Z+5,rmp ; und sichere
cpi rmp,60 ; 60 Minuten vorueber?
brcs MinLcd ; Nein, zeige Minuten und Sekunden an
; Erhoehe Stunden
clr rmp ; Neustart Minuten
std Z+5,rmp ; und speichern
ldd rmp,Z+4 ; Lese Stunden
inc rmp ; Erhoehe Stunden
std Z+4,rmp ; Speichern Stunden
cpi rmp,24 ; 24 Stunden erreicht?
brcs HourLcd ; Stunden/Minuten/Sekunden auf LCD
clr rmp ; Loesche Stunden
std Z+4,rmp ; Speichern
; Erhoehe Wochentag
ld rmp,Z ; Lese Wochentag
inc rmp ; Erhoehe
cpi rmp,7 ; Wochentag = 7?
brcs IncDay ; Nein, erhoehe Tag
ldi rmp,0 ; Neustart Wochentag
; Erhoehe Tag
IncDay: ; Naechstet day
st Z,rmp ; Speichere Wochentag
ldd rmp,Z+1 ; Lese Tag
inc rmp ; Erhoehe Tag
std Z+1,rmp ; Speichern Tag
rcall GetDaysOfMonth
cp rmp,XH ; Vergleiche mit Tage des Monats
brcs WeekDayLcd ; Gib Tag/Wochentag/Zeit auf LCD aus
; Erhoehe Monat
ldi rmp,1 ; Tag = 1
std Z+1,rmp ; Speichern
ldd rmp,Z+2 ; Lese Monat
inc rmp ; Erhoehe Monat
std Z+2,rmp ; Speichern
cpi rmp,13 ; Monat = 13?
brcs MonthLcd ; Nein, alles auf LCD ausgeben
; Erhoehe Jahr
ldi rmp,1 ; Monat = 1
std Z+2,rmp ; Speichern
ldd rmp,Z+3 ; Lese Jahr
inc rmp ; Erhoehe Jahr
std Z+3,rmp ; Speichern
cpi rmp,100 ; Jahr = 100?
brcs YearLcd ; Alles auf LCD ausgeben
ldi rmp,0 ; Jahr = 0
std Z+3,rmp ; Jahr speichern
; Datum und Zeit auf LCD ausgeben
; Format: Tu-01.05.18-23:59:59
; Position 01234567890123456789
; X zeigt auf Position im SRAM
DateTimeLcd:
ldi ZH,High(sDateTime) ; Zeige auf sDateTime
ldi ZL,Low(sDateTime)
YearLcd:
ldd rmp,Z+3 ; Lese Jahr
ldi XL,9 ; Spalte Jahr auf LCD
rcall NumberLcd ; Zahl dezimal anzeigen
MonthLcd:
ldd rmp,Z+2 ; Lese Monat
ldi XL,6 ; Spalte Monat auf LCD
rcall NumberLcd ; Zahl dezimal anzeigen
WeekDayLcd:
rcall DisplWeekday ; Wochentag anzeigen
DayLcd:
ldd rmp,Z+1 ; Lese Tag
ldi XL,3 ; Spalte Tag auf LCD
rcall NumberLcd ; Zahl dezimal anzeigen
HourLcd:
ldd rmp,Z+4 ; Lese Stunden
ldi XL,12 ; Spalte Stunden auf LCD
rcall NumberLcd ; Zahl dezimal ausgeben
MinLcd:
ldd rmp,Z+5 ; Lese Minuten
ldi XL,15 ; Spalte Minuten auf LCD
rcall NumberLcd ; Zahl dezimal ausgeben
SecLcd:
ldd rmp,Z+6 ; Lese Sekunden
ldi XL,18 ; Salte Sekunden auf LCD
rcall NumberLcd ; Zahl dezimal ausgeben
rjmp ActivatePos ; Eingabeposition aktivieren
;
; Anzeige der Zahl in rmp in
; Dezimalformat auf der LCD
; in Zeile 1
NumberLcd:
push rmp ; Sichern rmp
lds rmp,sKeyMode ; Lese Eingabemodus
cpi rmp,2 ; Im Normal- oder Weckbetrieb?
brcs NumberLcd0 ; Ja, Zahl ausgeben
pop rmp ; Nein, Zahl nicht ausgeben
ret
NumberLcd0: ; Schreibe auf LCD
push ZH ; Sichern Z
push ZL
ldi ZH,0 ; Zeile 1
mov ZL,XL ; Spalte aus XL
rcall LcdPos
pop ZL ; Wiederhesrstellen von Z
pop ZH
ldi rmp,0x2F ; ASCII-Zehner-Zaehler
mov R0,rmp ; in R0
pop rmp ; Zahl wieder herstellen
NumberLcd1:
inc R0 ; ASCII-Zaehler erhoehen
subi rmp,10 ; Zehn abziehen
brcc NumberLcd1 ; Kein Unterlauf, weiter
push rmp ; Zweite Ziffer sichern
rcall LcdR0 ; Ziffer in R0 auf LCD ausgeben
pop rmp ; Zweite Ziffer wieder herstellen
subi rmp,-58 ; 10 und 48 addieren
rjmp LcdChar ; Zweite Ziffer ausgeben
;
; Tage im aktuellen Monat
; Gibt Anzahl Tage im aktuellen Monat +1 zurueck
; 1:31, 2:28/29, 3:31, 4:30, 5:31, 6:30
; 7:30, 8:31, 9:30, 10:31, 11:30, 12:31
GetDaysOfMonth:
ldd XL,Z+2 ; Lese Monat
cpi XL,2 ; Februar?
brne GetDaysOfMonth2 ; Nein
ldd XL,Z+3 ; Februar, lade Jahr
andi XL,0x03 ; Schaltjahr?
brne GetDaysOfMonth1 ; Nein
ldi XH,30 ; Schaltjahr, 29 Tage
ret
GetDaysOfMonth1:
ldi XH,29 ; Kein Schaltjahr, 28 Tage
ret
GetDaysOfMonth2:
cpi XL,7 ; Monat kleiner als Juli?
brcs GetDaysOfMonth3 ; Ja
dec XL ; 30/31-Folge umkehren
GetDaysOfMonth3:
lsr XL ; Monat ungerade?
brcc GetDaysOfMonth4 ; Nein
ldi XH,32 ; 31 Tage
ret
GetDaysOfMonth4:
ldi XH,31 ; 30 Tage
ret
;
; Zeige den Wochentag 0 bis 6 auf LCD an
; Text ist in der WeekDayTable:
DisplWeekday:
lds rmp,sKeyMode ; Eingabemodus
cpi rmp,2 ; Im Datum-/Zeit-Eingabemodus?
brne DisplWeekday0
ret
DisplWeekday0:
push ZH ; Sichern Z
push ZL
ld rmp,Z ; Lese Wochentag
push rmp
ldi ZH,0 ; Zeile 1
ldi ZL,0 ; Spalte 1
rcall LcdPos
ldi ZH,High(2*WeekdayTable) ; Zeige auf Wochentagstabelle
ldi ZL,Low(2*WeekdayTable)
pop rmp ; Wochentag wieder herstellen
lsl rmp ; Mal zwei
add ZL,rmp ; Zu Tabelle addieren
ldi rmp,0 ; MSB Null
adc ZH,rmp ; Null mit Carry addieren
lpm rmp,Z+ ; Erstes Zeichen Wochentag
rcall LcdChar ; auf LCD
lpm rmp,Z ; Zweites Zeichen Wochentag
pop ZL ; Z wieder herstellen
pop ZH
rjmp LcdChar ; Und auf LCD
;
; Tabelle der Wochentagsabkuerzungen
WeekdayTable:
.if LangEN == 1
; Wenn Englisch verwende diese
.db "MoTuWdThFrSaSu"
.else
; Wenn Deutsch verwende diese
.db "MoDiMiDoFrSaSo"
.endif
;
; Initiiere Datum und Zeit
InitDT:
ldi XH,High(sDateTime)
ldi XL,Low(sDateTime)
ldi ZH,High(2*InitDTTable)
ldi ZL,Low(2*InitDTTable)
InitDT1:
lpm rmp,Z+
st X+,rmp
cpi ZH,High(2*InitDTTableEnd-1)
brne InitDT1
cpi ZL,Low(2*InitDTTableEnd-1)
brne InitDT1
rcall DateTimeLcd
rjmp DisplAlarm
;
; Starttabelle fuer Datum und Zeit
; Datum/Zeit = Mo, 30.04.18-23:59:59
; Weckzeit = 00:03
InitDTtable:
.db 0,30,4,18,23,59,59,0,3,0
InitDTtableEnd:
;
; =====================================
; W E C K Z E I T E R R E I C H T ?
; =====================================
;
; Ueberpruefe jede Minute ob Wecken erreicht
CheckMinute:
cbr rFlag,1<<bMinute ; Loesche Flagge
lds rmp,sKeyMode ; Weckmodus lesen
cpi rmp,1 ; Wecken eingeschaltet?
brne CheckMinuteRet ; Wecken nicht aktiv
lds rmp,sAlarmRepeat ; Weckwiederholung?
tst rmp ; Weckwiederholungen = Null?
brne CheckMinute1 ; Nein
lds rmp,sDateTime+8 ; Lese Minuten Wecken
lds R0,sDateTime+5 ; Lese Minuten Zeit
cp rmp,R0 ; Vergleiche
brne CheckMinuteRet ; Nicht gleich
lds rmp,sDateTime+7 ; Lese Wecken Stunden
lds R0,sDateTime+4 ; Lese Stunden Zeit
brne CheckMinuteRet ; Nicht gleich
; Beginne Wecken
ldi rmp,cAlarmRepet ; Wiederholungen starten
sts sAlarmRepeat,rmp
rcall IncAlarm ; Weckzeit um eine Minute erhoehen
mov rmp,rMelody ; Lese Zufallsmelodie
rjmp MusicConvert ; Spiele diese Melodie
CheckMinute1: ; Wecken ist noch aktiv
; Neustart der Weckmelodie
rcall MusicPlay ; Neustart Musikausgabe
lds rmp,sAlarmRepeat ; Lese Weckwiederholungen
dec rmp ; Erniedrigen
sts sAlarmRepeat,rmp ; und speichern
breq CheckMinute2 ; Ist Null, Ausgabe aktualisieren
rjmp IncAlarm ; Weckzeit um eine Minute erhoehen
CheckMinute2:
; Weckwiederholung ausschalten
rjmp SetAlarmOff
CheckMinuteRet:
ret
;
; Die Weckzeit um eine Minute erhoehen
IncAlarm:
ldi ZH,High(sDateTime) ; Zeige auf sDateTime
ldi ZL,Low(sDateTime)
ldd rmp,Z+8 ; Lese Weckminute
inc rmp ; Erhoehe
std Z+8,rmp ; Speichere
cpi rmp,60 ; Naechste Stunde?
brcs IncAlarmRet ; Nein
clr rmp ; Loesche Minute
std Z+8,rmp
ldd rmp,Z+7 ; Lese Stunde
inc rmp ; Erhoehe Stunde
std Z+7,rmp ; Speichere
cpi rmp,24 ; Naechster Tag?
brcs IncAlarmRet ; Nein
clr rmp ; Loesche Stunde
std Z+7,rmp
IncAlarmRet:
rjmp DisplAlarm
;
; ===================================
; T A S T E N A U S W E R T E N
; ===================================
;
; Eine Taste wurde gedrueckt
;
KeyPress:
cbr rFlag,1<<bKey ; Loesche Flagge
lds rmp,sKeyMode ; Lese Eingabemodus
tst rmp ; Null?
brne KeyPress1 ; Nein
sbis pKeyI,bKeyRI ; Rote Taste?
rjmp KeyPressRed ; Werte rote Taste aus
sbis pKeyI,bKeyYI ; Gelbe Taste?
rjmp KeyPressYellow ; Werte gelbe Taste aus
sbis pKeyI,bKeyWI ; Weisse Taste?
rjmp KeyPressWhite ; Werte weisse Taste aus
ret
KeyPress1: ; Modus nicht Null
cpi rmp,2 ; Modus = 2
brcs KeyPressMode1 ; Nein, Null oder Eins
breq KeyPressMode2 ; Ja
rjmp KeyPressMode3 ; Modus = 3
ret
;
KeyPressMode1:
sbis pKeyI,bKeyRI ; Rote Taste in Mode 0 oder 1?
rjmp KeyPress1Red ; Ja, werte rote Taste aus
sbis pKeyI,bKeyYI ; Gelbe Taste in Mode 0 oder 1?
rjmp KeyPress1Yellow ; Ja, werte gelbe Taste aus
sbis pKeyI,bKeyWI ; Weisse Taste in Mode 0 oder 1?
rjmp KeyPress1White ; Ja, werte weisse Taste aus
ret
;
KeyPressMode2:
sbis pKeyI,bKeyRI ; Rote Taste in Mode 2?
rjmp KeyPress2Red ; Ja, werte rote Taste aus
sbis pKeyI,bKeyYI ; Gelbe Taste in Mode 2?
rjmp KeyPress2Yellow ; Ja, werte gelbe Taste aus
sbis pKeyI,bKeyWI ; Weisse Taste in Mode 2?
rjmp KeyPress2White ; Ja, werte weisse Taste aus
ret
;
KeyPressMode3:
sbis pKeyI,bKeyRI ; Rote Taste in Mode 3?
rjmp KeyPress3Red ; Ja, werte rote Taste aus
sbis pKeyI,bKeyYI ; Gelbe Taste in Mode 3?
rjmp KeyPress3Yellow ; Ja, werte gelbe Taste aus
sbis pKeyI,bKeyWI ; Weisse Taste in Mode 3?
rjmp KeyPress3White ; Ja, werte weisse Taste aus
ret
;
; Neue Tasten auswerten
;
; Rote Taste gedrueckt
KeyPressRed:
lds rmp,sKeyMode ; Lese Eingabemodus
cpi rmp,1 ; Modus = 1?
brcs SetAlarmOn ; Nein, Modus = 0
breq SetAlarmOff ; Ja, Modus = 1
rjmp DisplAlarm ; Modus 2 oder 3
SetAlarmOff:
ldi rmp,(1<<COM1A1) ; Lautsprecherausgang Clear
out TCCR1A,rmp
ldi rmp,(1<<TOIE0)|(1<<TOIE2) ; TC1 Interrupt aus
out TIMSK,rmp
ldi rmp,High(clock/2000-1) ; CTC TC1 auf 1000 Hz
out OCR1AH,rmp
ldi rmp,Low(clock/2000-1)
out OCR1AL,rmp
ldi rmp,0 ; Setze Modus = 0
sts sKeyMode,rmp ; Wecken ausschalten
sts sAlarmRepeat,rmp ; Weckwiederholungen aus
lds rmp,sAlarm ; Neustart Weckzeit
sts sDateTime+7,rmp
lds rmp,sAlarm+1
sts sDateTime+8,rmp
rjmp DisplAlarm
SetAlarmOn:
ldi rmp,1 ; Setze Modus = 1
sts sKeyMode,rmp
ldi rmp,0 ; Loesche Weckwiederholung
sts sAlarmRepeat,rmp
rjmp DisplAlarm
;
; Die gelbe Taste wurde gedrueckt
; Beginne mit Datum-/Zeit-Eingabe
KeyPressYellow:
ldi rmp,2 ; Mode 2
sts sKeyMode,rmp
clr rmp ; Eingabeposition auf Wochentag
sts sKeyPos,rmp
ldi ZH,High(sDateTime) ; Z zeigt auf Wochentag
ldi ZL,Low(sDateTime)
ldi XH,High(sInputData) ; X zeigt auf Eingabepuffer
ldi XL,Low(sInputData)
ldi rmp,7 ; Sieben Bytes kopieren
KeyPressYellow1:
ld R0,Z+ ; Lese Datum/Zeit
st X+,R0 ; Schreibe in Eingabepuffer
dec rmp ; Zaehle abwaerts
brne KeyPressYellow1 ; Weiter lesen/schreiben
rcall LcdBlink ; Cursor blinken an
rjmp ActivatePos
;
; Die weisse Taste ist gedrueckt
; Beginne mit Weckzeit-Eingabe
KeyPressWhite:
ldi rmp,3 ; Modus = 3
sts sKeyMode,rmp
ldi rmp,7 ; Position im Eingabepuffer auf Stunden
sts sKeyPos,rmp
lds rmp,sDateTime+7 ; Weckzeit in Eingabepuffer kopieren
sts sInputData+7,rmp
lds rmp,sDateTime+8
sts sInputData+8,rmp
rcall DisplAlarm ; Weckzeit anzeigen
rcall DisplMenue ; Menue anzeigen
rcall ActivatePos ; Aktivieren der Eingabeposition
ldi ZH,1 ; Cursor auf Weckzeitstunden
ldi ZL,12
rcall LcdPos
rjmp LcdBlink ; Blinken an
;
; Tasten in Modus 1
;
; Rote Taste in Modus 1
KeyPress1Red:
; Wecken ausschalten
rjmp SetAlarmOff
;
; Den Weck-Status und die Weckzeit anzeigen
DisplAlarm:
ldi ZH,1 ; Position Weckstatus einstellen
ldi ZL,cAlarmPos
rcall LcdPos
ldi ZH,High(2*AlarmText0) ; Text fuer Wecken Aus
ldi ZL,Low(2*AlarmText0)
lds rmp,sKeyMode ; Ist Wecken aktive?
cpi rmp,1
brcs DisplAlarm1 ; Nein, gib Text aus
ldi ZH,High(2*AlarmText1) ; Text fuer Wecken an
ldi ZL,Low(2*AlarmText1)
breq DisplAlarm1 ; Ja, gib Text aus
ldi ZH,High(2*AlarmText2) ; Text fuer Wecken suspendiert
ldi ZL,Low(2*AlarmText2)
DisplAlarm1:
rcall LcdDisplZ ; Gib Text ab Z aus
ldi rmp,' ' ; Leerzeichen ausgeben
rcall LcdChar
lds R0,sDateTime+7 ; Weckzeit Stunden
rcall LcdDec2 ; dezimal ausgeben
ldi rmp,':' ; Doppelpunkt ausgeben
rcall LcdChar
lds R0,sDateTime+8 ; Weckzeit Minuten
rcall LcdDec2 ; dezimal ausgeben
ldi rmp,' ' ; Leerzeichen ausgeben
rcall LcdChar
lds rmp,sAlarmRepeat ; Weckwiederholungen
tst rmp ; auf Null?
breq DisplAlarm3 ; Ja, gib zwei Leerzeichen aus
dec rmp ; Um Eins erniedrigen
cpi rmp,100 ; Mehr als 100 mal?
brcs DisplAlarm2
ldi rmp,99 ; Lade Maximum
DisplAlarm2:
mov R0,rmp ; Kopiere nach R0
rjmp LcdDec2 ; und dezimal ausgeben
DisplAlarm3:
ldi rmp,' ' ; Gib zwei Leerzeichen aus
rcall LcdChar
ldi rmp,' '
rjmp LcdChar
;
; Wecken aktiv/inaktiv/susendiert Text
.if LangEN == 1
; Wenn englische Version:
AlarmText0:
.db "off",0
AlarmText1:
.db " on",0
AlarmText2:
.db " ..",0
.else
; Wenn deutsche Version:
AlarmText0:
.db "aus",0
AlarmText1:
.db " an",0
AlarmText2:
.db " ..",0
.endif
;
; Gelbe Taste in Modus 1
KeyPress1Yellow:
; Erhoehen der Weckzeit um 5 Minuten
lds rmp,sDateTime+8 ; Lese Minuten
subi rmp,-5 ; Addiere fuenf Minuten
SetAlarmTime:
sts sDateTime+8,rmp ; Speichere
cpi rmp,60 ; Minuten kleiner als 60?
brcs SetAlarmTimeFinal
subi rmp,60 ; Subtrahiere 60
sts sDateTime+8,rmp
lds rmp,sDateTime+7 ; Stunden erhoehen
inc rmp
cpi rmp,24 ; Tagesende?
brcs SetAlarmTimeFinal
subi rmp,24 ; Subrahiere 24
sts sDateTime+8,rmp
SetAlarmTimeFinal:
rjmp DisplAlarm ; Weckzeit anzeigen
;
; Weisse Taste in Modus 1
KeyPress1White:
; Erhoehe Weckzeit um 10 Minuten
lds rmp,sDateTime+8 ; Lese Minuten
subi rmp,-10 ; Addiere zehn Minuten
rjmp SetAlarmTime ; Setze Weckzeit
;
; Tasten in Modus 2
;
; Rote Taste in Modus 2
KeyPress2Red:
; Nach links
lds rmp,sKeyPos ; Eingabeposition lesen
tst rmp ; Bei Null?
breq KeyPress2Red1 ; Ja, beende Eingabe
dec rmp ; Eine Position links
sts sKeyPos,rmp ; und speichern
rjmp ActivatePos ; und aktivieren
KeyPress2Red1:
; Eingabe abbrechen und in Normalmodus
lds rmp,sAlarm ; Original-Weckzeit wieder herstellen
sts sDateTime+7,rmp
lds rmp,sAlarm+1
sts sDateTime+8,rmp
rcall SetAlarmOff ; Wecken ausschalten
rcall DisplMenue ; Menue anzeigen
rjmp LcdNoBlink ; Nicht mehr blinken
;
; Gelbe Taste in Modus 2
KeyPress2Yellow:
; Originalwert wieder herstellen
lds rmp,sKeyPos ; Lese Eingabeposition
ldi XH,High(sDateTime) ; Zeige X auf sDateTime:
ldi XL,Low(sDateTime)
ldi ZH,High(sInputData) ; Zeige Z auf Eingabedaten
ldi ZL,Low(sInputData)
clr R0 ; Null-Addierer
add XL,rmp ; Zu X dazu addieren
adc XH,R0 ; Plus Carry
add ZL,rmp ; Zu Z dazu addieren
adc ZH,R0 ; Plus Carry
ld rmp,X ; aus X laden
st Z,rmp ; in Z schreiben
rcall DisplInp ; Anzeige aktuelle Eingabe
rjmp KeyPress2White ; Naechste Position
;
; Zeige Eingabe an aktueller osition an
DisplInp:
rcall ActivatePos ; Aktive Position
lds rmp,sKeyPos ; Lese Eingabeposition
tst rmp ; Wochentag?
brne DisplInp1
; Zeige Wochentag an
ldi ZH,High(2*WeekdayTable)
ldi ZL,Low(2*WeekdayTable)
lds rmp,sInputData ; Lese Wochentag
lsl rmp ; Multipliziere mit 2
add ZL,rmp ; Addiere Versatz
ldi rmp,0
adc ZH,rmp ; und Carry
lpm rmp,Z+ ; Lese erstes Zeichen aus Tabelle
rcall LcdChar
lpm rmp,Z ; Lese zweites Zeichen aus Tabelle
rjmp LcdChar
DisplInp1:
ldi ZH,High(sInputData) ; Zeige auf Eingaebedaten
ldi ZL,Low(sInputData)
add ZL,rmp
ldi rmp,0
adc ZH,rmp
ld R0,Z ; Lese Byte
rjmp LcdDec2
;
; Weisse Taste in Modus 2
KeyPress2White:
; Naechste Position
lds rmp,sKeyPos ; Lese Position
cpi rmp,6 ; Letzte Position?
brcc KeyPress2White1 ; Ja
inc rmp ; Naechste Position
sts sKeyPos,rmp ; Sichere neue Position
rjmp ActivatePos ; Position aktivieren
KeyPress2White1:
ldi XH,High(sInputData) ; Zeige auf Eingabedaten
ldi XL,Low(sInputData)
ldi ZH,High(sDateTime) ; Zeige auf Datum/Uhrzeit
ldi ZL,Low(sDateTime)
ldi rmp,7 ; Sechs Bytes zu uebertragen
KeyPress2White2:
ld R0,X+ ; Byte lesen
st Z+,R0 ; Byte schreiben
dec rmp ; Zaehler abwaerts
brne KeyPress2White2 ; Weiter kopieren
ldi rmp,0 ; Weckmodus ausschalten
sts sKeyMode,rmp
rcall LcdNoBlink
rcall DateTimeLcd ; Anzeige Datum/Zeit
rjmp DisplMenue ; Anzeige Menue
;
; Tasten in Modus 3
;
; Rote Taste in Modus 3
KeyPress3Red:
; Eine Position links
lds rmp,sKeyPos ; Lese Position
cpi rmp,7 ; Linkeste Position?
breq KeyPress3Red1 ; Ja, beende Eingabe
dec rmp ; Eine Position links
sts sKeyPos,rmp ; Speichern
rjmp ActivatePos ; Und aktivieren
KeyPress3Red1:
lds rmp,sAlarm ; Beende, restauriere Original-Weckzeit
sts sDateTime+7,rmp
lds rmp,sAlarm+1
sts sDateTime+8,rmp
rcall SetAlarmOff ; Alarm aus
rcall DisplMenue ; Menue anzeigen
rjmp LcdNoBlink ; Nicht mehr blinken
;
; Gelbe Taste in Modus 3
KeyPress3Yellow:
; Alten Wert wieder herstellen
lds rmp,sKeyPos ; Lese Position
cpi rmp,8 ; Letzte Stelle?
brcc KeyPress3Yellow1 ; Nein, erste Stelle
lds rmp,sDateTime+8 ; Alten MinutenwWert wieder herstellen
sts sInputData+8,rmp
sts sAlarm+1,rmp
rcall DisplAlarmInp
rjmp KeyPress3White
KeyPress3Yellow1:
lds rmp,sDateTime+7 ; Alten Stundenwert wieder herstellen
sts sInputData+7,rmp
sts sAlarm,rmp
lds rmp,sKeyPos
inc rmp ; Naechste Position
sts sKeyPos,rmp
rjmp DisplAlarmInp
;
; Display alarm input
DisplAlarmInp:
ldi ZH,1 ; Alarm position hour
ldi ZL,12
rcall LcdPos
lds R0,sInputData+8
rcall LcdDec2
ldi ZH,1 ; Alarm position minute
ldi ZL,15
rcall LcdPos
lds R0,sInputData+9
rcall LcdDec2
rjmp ActivatePos
;
; Weisse Taste in Modus 3
KeyPress3White:
; Naechste Position
lds rmp,sKeyPos ; Lese Position
cpi rmp,8 ; Letzte Position?
breq KeyPress3White1 ; Ja, beende Eingabe
inc rmp ; Nein, erhoehe
sts sKeyPos,rmp ; und schreibe
rjmp ActivatePos ; Aktivieren
KeyPress3White1:
lds rmp,sInputData+7 ; Lese Weckzeit Stunden
sts sAlarm,rmp ; Setze neue Weckzeit
sts sDateTime+7,rmp ; und in Anzeige
lds rmp,sInputData+8 ; Lese Weckzeit Minuten
sts sAlarm+1,rmp ; Setze neue Weckzeit
sts sDateTime+8,rmp ; und in Anzeige
rcall SetAlarmOn ; Wecken einschalten
rcall DisplMenue ; Menue anzeigen
rjmp LcdNoBlink ; Blinken ausschalten
;
; Menue fuer aktuellen Modus ausgeben
;
DisplMenue:
.if ! fDbg ; Display in non-debug mode only
ldi ZH,3 ; Menue in line 3
ldi ZL,0
rcall LcdPos
ldi ZH,High(2*Menuetext)
ldi ZL,Low(2*Menuetext)
clr R1
lds R0,sKeyMode
ldi rmp,22
DisplMenue1:
tst R0
breq DisplMenue2
add ZL,rmp
adc ZH,R1
dec R0
rjmp DisplMenue1
DisplMenue2:
lds rmp,sKeyMode ; Lese Modus
cpi rmp,2
brcs DisplMenue3
breq Displ2Menue
lds rmp,sKeyPos ; Mode = 3
cpi rmp,7
ldi ZH,High(2*MenueFirst)
ldi ZL,Low(2*MenueFirst)
breq DisplMenue3
ldi ZH,High(2*MenueLast)
ldi ZL,Low(2*MenueLast)
rjmp DisplMenue3
Displ2Menue:
lds rmp,sKeyPos ; Mode = 2
tst rmp
brne Displ2Menue1
ldi ZH,High(2*MenueFirst)
ldi ZL,Low(2*MenueFirst)
rjmp DisplMenue3
Displ2Menue1:
cpi rmp,6
brne DisplMenue3
ldi ZH,High(2*MenueLast)
ldi ZL,Low(2*MenueLast)
DisplMenue3:
rjmp LcdDisplZ
.else
ret
.endif
;
; Aktiviere die aktuelle Eingabeposition
; auf der LCD zum Blinken
ActivatePos:
lds rmp,sKeyPos
cpi rmp,7
brcs ActivatePos1
ldi ZH,1
ldi ZL,12
breq ActivatePosSet
ldi ZL,15
rjmp ActivatePosSet
ActivatePos1:
ldi ZH,0
cpi rmp,1
ldi ZL,0
brcs ActivatePosSet
ldi ZL,3
breq ActivatePosSet
ldi ZL,6
cpi rmp,3
brcs ActivatePosSet
ldi ZL,9
breq ActivatePosSet
ldi ZL,12
cpi rmp,5
brcs ActivatePosSet
ldi ZL,15
breq ActivatePosSet
ldi ZL,18
ActivatePosSet:
rjmp LcdPos
;
Menuetext:
.if LangEN == 1
.db "Alarm Time Alarmtime",0,0
.db "NoAlarm +5min +10min",0,0
.db "Prev Default Next",0,0
.db "Prev Default Next",0,0
MenueFirst:
.db "Skip Default Next",0,0
MenueLast:
.db "Prev Default Set",0,0
.else
.db "Wecken Zeit Weckzeit",0,0
.db "Weckaus +5min +10min",0,0
.db "Links Alt Rechts",0,0
.db "Links Alt Rechts",0,0
MenueFirst:
.db "Abbr.Default Naechst",0,0
MenueLast:
.db "Links Alt Setzen",0,0
.endif
;
; ===================================================
; V E R A R B E I T E A D C E R G E B N I S S E
; ===================================================
;
; ADC hat 64 Werte im aktuellen Kanal aufsummiert
; MSB der summierten Ergebnisse ist in rAdc
;
AdcCmplt:
cbr rFlag,1<<bAdc ; Loesche Flagge
in rmp,ADMUX ; Read channel
andi rmp,0x1F ; Isolate channel bits
push rmp ; Retten Kanal
inc rmp ; Naechster Kanal
cpi rmp,0x03 ; Kanal = 3?
brcs AdcCmplt1 ; Nein, kleiner
clr rmp
AdcCmplt1:
ori rmp,(1<<REFS0) ; Setze REFSO-Bit
out ADMUX,rmp
ldi rAdcC,64 ; 64 Messungen
ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(ADPS0)
out ADCSRA,rmp ; Starte naechste Wandlung
pop rmp ; Vorherigen Kanal
cpi rmp,1 ; Kanal = 1?
brcs AdcSet0 ; Kanal = 0
brne AdcSet2 ; Kanal = 2
rjmp AdcSet1 ; Kanal = 1
;
; Kanal 2 komplett
; Zeige Signalstaerke an
AdcSet2:
; Abwaertszaehlen Signalstaerke-Zaehler
dec rSigStr
breq AdcSet2A ; Null erreicht
AdcSet2Ret:
ret ; Fertig
AdcSet2A:
ldi rmp,cSigStr ; Neustart Zaehler
mov rSigStr,rmp
lds rmp,sKeyMode ; Betriebsmodus?
cpi rmp,2 ; Eingabe Datum/Zeit/Weckzei?
brcc AdcSet2Ret ; Ja, keine Anzeige
; Kanal 2
ldi ZH,2 ; Ausgabeposition Zeile 3 fuer +
ldi ZL,7
rcall LcdPos
mov rmp,rAdc ; Lese MSB ADC-Summe
cpi rmp,30 ; Groesser oder gleich 30?
ldi rmp,' ' ; Schreibe Leerzeichen
brcs SignalNoPlus ; Kleiner 30
ldi rmp,'+' ; Schreibe +
SignalNoPlus:
rcall LcdChar
ldi ZH,2 ; Ausgabeposition S-Wert
ldi ZL,6
rcall LcdPos
ldi ZH,High(2*SignalTable) ; Tabelle mit Signalstaerken
ldi ZL,Low(2*SignalTable)
clr R0 ; R0 ist Zaehler
SignalStrength:
inc R0 ; Zaehler erhoehen
lpm rmp,Z+ ; Lese Tabellenwert
cp rAdc,rmp ; Vergleiche mit Tabellenwert
brcc SignalStrength ; rAdc ist groesser oder gleich Tabellenwert
ldi rmp,'0'-1 ; Addiere ASCII-Null - 1
add rmp,R0
rjmp LcdChar ; Zahl ausgeben
;
; Tabelle mit Signalstaerken
SignalTable:
.db 3,6,9,12,15,18,21,24,27,255
;
; Channel 0: Potentiometer-Stellung
; Wenn im Input-Modus: Erneuern Byte an der aktuellen Position
AdcSet0:
; Pruefe Eingabemodus aktiv
lds rmp,sKeyMode
cpi rmp,2
brcc AdcSet0_1 ; Im Eingabemodus
AdcSet0Ret:
ret ; Fertig
AdcSet0_1:
lds rmp,sKeyCnt ; Tastenzaehler lesen
tst rmp ; Tastenzaehler Null?
breq AdcSet0a ; Bei Null, weitermachen
dec rmp ; Tastenzaehler abwaerts
sts sKeyCnt,rmp ; und speichern
rjmp AdcSet0Ret ; Keine weitere Aktion
AdcSet0a:
ldi rmp,5 ; Neustart mit 5 Zyklen Verzoegerung
sts sKeyCnt,rmp
rcall GetMinMax ; Hole Minimum (R0) und Maximum (R1)
mov rmp,R1 ; Maximum - Minimum
sub rmp,R0 ; Subtrahiere Minimum
mov XL,rAdc ; Multipliziere rAdc mit Differenz
clr XH
clr ZL ; Z ist Resultat
mov ZH,R0 ; Addiere Minimum zum MSB
AdcSet0b:
tst rmp ; Null erreicht?
breq AdcSet0d ; Ja, Multiplikation fertig
lsr rmp ; Schiebe niedrigstes Bit in Carry
brcc AdcSet0c
add ZL,XL ; Addiere den Multiplikanten
adc ZH,XH
AdcSet0c:
lsl XL ; Schiebe Multiplikator links
rol XH
rjmp AdcSet0b ; Weiter multiplizieren
AdcSet0d:
ldi XH,High(sInputData) ; Zeige auf Eingabedaten
ldi XL,Low(sInputData)
lds rmp,sKeyPos
add XL,rmp ; Addiere Position
ldi rmp,0
adc XH,rmp ; und Carry
st X,ZH ; Schreibe Multiplikationsergebnis
rcall DisplInp
rjmp ActivatePos ; Aktiviere erneut
;
; Hole Minimum- und Maximum-Wert fuer aktuelle Position
; Minimum in R0, Maximum +1 in R1
GetMinMax:
ldi ZH,High(2*MinMaxTab) ; Z zeigt auf Tabelle
ldi ZL,Low(2*MinMaxTab)
clr R0 ; R0 ist Carry-Addierer
lds rmp,sKeyPos ; Aktuelle Position
lsl rmp ; Multiplikation mit 2
add ZL,rmp
adc ZH,R0 ; Carry addieren
lpm R0,Z+ ; Lese Minimum
lpm R1,Z ; Lese Maximum
ret
;
; Minima/Maxima-Tabelle nach Position
MinMaxTab:
; wt tag mon jahr std min sec alstd almin
.db 0,7,1,32,1,13,0,100,0,24,0,60,0,60,0,24,0,60
;
; ADC Kanal 1 komplett
; Zeige Lichtsensor in hex an
AdcSet1:
; Pruefe ob Verzoegerungsperiode vorbei
dec rBackCnt ; Zaehler abwaerts
brne AdcSet1Ret ; Nicht Null
ldi rmp,cBackPeriods ; Starte Zaehler neu
mov rBackCnt,rmp ; Neustart Zaehler
ldi ZH,2 ; Zeige Sensormessung auf LCD an
ldi ZL,18
rcall LcdPos
mov rmp,rAdc
rcall LcdHex2
AdcSet1a:
tst rAdc ; Sensor bei Null?
brne Light0 ; Nein
inc rAdc ; Erhoehe auf Eins
Light0:
neg rAdc ; Umkehrwert
; Multiplikation des Sensor-Ergebnisses
ldi rmp,cBackMin ; Addiere Minimum zu MSB
mov R1,rmp ; Ergebnis in R1:R0
clr R0 ; LSB Ergebnis = 0
ldi ZL,cBackMult ; Multiplikator in ZL
clr ZH ; Null in ZH
Light1: ; Multipliziere mit Konstante
lsr rAdc ; Bit in Carry schieben
brcc Light2 ; Null = nicht addieren
add R0,ZL ; Addiere
adc R1,ZH
brcc Light2 ; Kein Uebertrag
clr R1 ; Uebertrag, setze MSB auf FF
dec R1
Light2:
lsl ZL ; Multipliziere mit 2
rol ZH
tst rAdc ; Fertig?
brne Light1 ; Nein, weiter multilizieren
out OCR2,R1 ; Hintergrundbeleuchtung PWM setzen
AdcSet1Ret:
ret
;
; Musik-Code-Routinen hinzu laden
.include "musik_code.inc"
;
; LCD-Routinen hinzu laden
.include "lcd_8_routinen.inc"
;
.if fDbg
; Wenn Debuggen aktiv: Debug-Code hinzu laden
.include "debug_code_de.inc"
.endif
;
; Lcd texte
;
LcdTxtInit1:
.if LangEN == 1
; Englische Version
.db " DCF77_m16 V4 clock ",0,0
.else
; Deutsche Version
.db "DCF77_m16 V4 Atomuhr",0,0
.endif
.db " ",7,"2018 DG4FAC ",0,0
.db " ",0,0
.db " ",0,0
;
LcdTxtFrame:
.db " - . . - : : ",0,0
.if LangEN == 1
; Englische Version
.db " Alarm: ___ : ",0,0
.db "DCF: S0 __ Light: ",0,0
.else
; Deutsche Version
.db "Wecken: ___ : ",0,0
.db "DCF: S0 __ Licht: ",0,0
.endif
.db " ",0,0
;
; Edit-Positionen
.equ cAlarmPos = 8
.equ cDcfPos = 8
;
; Ende des Quellcodes
; Copyright
.db "(C)2018 by Gerhard Schmidt " ; menschenlesbar
.db "C(2)10 8ybG reahdrS hcimtd " ; wortgerecht
;
Dieser Code im asm-Format
; ****************************************************************
; * Include-Datei fuer LCD-Routinen DCF77-ATmega16, V. 1.1 05/18 *
; * (C)2018 by Gerhard Schmidt, info (bei) avr-asm-tutorial.net *
; ****************************************************************
;
;
; --------------------------------------------------------------
; Routine Register Funktion
; --------------------------------------------------------------
; LcdInit rmp,R0,Z Initiiert das LCD-Display zu Beginn auf:
; 8-Bit-Interface, 4 Zeilen, kein Shift,
; kein Cursor und nicht blinkend
; LcdR0 rmp Gibt das Zeichen in R0 auf der aktuellen
; Position des LCD aus, R0 bleibt erhalten
; LcdChar rmp,R0 Gibt das Zeichen in rmp auf der aktuellen
; Position des LCD aus, rmp wird geaendert,
; Zeichen bleibt in R0 erhalten
; LcdDisplZ rmp,R0,Z Gibt den Text im Flash ab Z auf der LCD
; aus, stoppt beim NULL-Zeichen
; LcdLine rmp Setzt die Ausgabe der LCD auf die Zeile
; in rmp, erwartet 0..3 in rmp fuer die
; Zeilen 1..4
; LcdFrame rmp Gibt Standard-Frame aus
; LcdLineCl rmp,R0,Z Loescht die Zeile des LCD, auf die rmp
; zeigt, durch Schreiben von Leerzeichen
; LcdPos rmp,Z Setzt die LCD auf die Position in Z,
; ZH ist die Zeile (0..3), ZL ist die Position
; in dieser Zeile (0..19)
; LcdSram rmp,Z,X Schreibt rmp Zeichen von Position X im
; SRAM an Position Z der LCD
; LcdDec2 rmp,ZL Schreibt die Binaerzahl in R0 in dezimal
; (2 Ziffern) auf die LCD
; LcdDec16 Schreibt die Binaerzahl in R1:R0 in
; Dezimalformat auf die LCD
; LcdHex2 rmp Schreibt den Inhalt von rmp in Hex an die
; aktuelle LCD-Position (debug)
; LcdHex4 Z,rmp Schreibt die Binaerzahl in R1:R0 in
; Hex an die aktuelle LCD-Position
; LcdBlink rmp Blinken der LCD an
; LcdNoBlink Blinken der LCD aus
; --------------------------------------------------------------
;
; Verzoegerungsroutinen fuer die Display-Initiierung
; Taktrate des ATmega16: 3,276800 MHz (Externer Quarz)
; clock = 3276800 Hz im Hauptprogramm definiert
.EQU clk100 = clock / 100
.EQU clkDiv = 10000
;
; Verzoegerungsschleifen
; Anzahl der Takte einschlieszlich rcall: 11+4*(z-1)+3+8 = 4*Z+18
; Verzoegerung in Mikrosekunden: (clk100*(4*Z+18))/clkDiv
; Berechnung von Z = (clk100*(n-18)/4)/clkDiv
;
; Verzoegerung maximal
;
.EQU cLcdWtMax = 64535
LcdWtMax:
push ZH
push ZL
ldi ZH,HIGH(cLcdWtMax)
ldi ZL,LOW(cLcdWtMax)
rjmp LcdWt
;
; Verzoegerung 15 ms
;
.EQU cLcdWt15000 = (clk100*(15000-18+2)/4)/clkDiv ; = 9206
LcdWt15000:
push ZH ; 2
push ZL ; 4
ldi ZH,HIGH(cLcdWt15000) ; 5
ldi ZL,LOW(cLcdWt15000) ; 6
rjmp LcdWt ; 8
;
; Verzoegerung 4,1 ms
;
.EQU cLcdWt4100 = (clk100*(4100-18+2)/4)/clkDiv ; = 2509
LcdWt4100:
push ZH ; 2
push ZL ; 4
ldi ZH,HIGH(cLcdWt4100) ; 5
ldi ZL,LOW(cLcdWt4100) ; 6
rjmp LcdWt ; 8
;
; Verzoegerung 4,5 ms
;
.EQU cLcdWt4500 = (clk100*(4500-18+2)/4)/clkDiv ; = 2754
LcdWt4500:
push ZH ; 2
push ZL ; 4
ldi ZH,HIGH(cLcdWt4500) ; 5
ldi ZL,LOW(cLcdWt4500) ; 6
rjmp LcdWt ; 8
;
; Verzoegerung 1,64 ms
;
.EQU cLcdWt1640 = (clk100*(1640-18+2)/4)/clkDiv ; = 997
LcdWt1640:
push ZH ; 2
push ZL ; 4
ldi ZH,HIGH(cLcdWt1640) ; 5
ldi ZL,LOW(cLcdWt1640) ; 6
rjmp LcdWt ; 8
;
; Verzegerung 100 us
;
.EQU cLcdWt100 = (clk100*(100-18+2)/4)/clkDiv ; = 51
LcdWt100:
push ZH ; 2
push ZL ; 4
ldi ZH,HIGH(cLcdWt100) ; 5
ldi ZL,LOW(cLcdWt100) ; 6
rjmp LcdWt ; 8
;
; Verzoegerung 40 us
;
.EQU cLcdWt40 = (clk100*(40-18+2)/4)/clkDiv ; = 14
LcdWt40:
push ZH ; 2
push ZL ; 4
ldi ZH,HIGH(cLcdWt40) ; 5
ldi ZL,LOW(cLcdWt40) ; 6
nop ; 7 ; Ersatz rcall
nop ; 8
;
; Wartet auf Z Verzoegerung
;
LcdWt:
sbiw ZL,1 ; 2 Takte
brne LcdWt ; 2/1 Takte
pop ZL ; 2 Takte
pop ZH ; 2 Takte
ret ; 4 Takte
;
; Aktiviere E fuer 1 us
;
LcdE:
sbi pLcdCO,bLcdE ; setze E aktiv
nop ; One clock cycle delay
.if clock > 1000000
nop
.if clock > 2000000
nop
.if clock > 3000000
nop
.if clock > 4000000
nop
.if clock > 5000000
nop
.if clock > 8000000
nop
nop
nop
.if clock > 10000000
nop
nop
nop
nop
nop
.if clock > 15000000
nop
nop
nop
nop
nop
.endif
.endif
.endif
.endif
.endif
.endif
.endif
.endif
cbi pLcdCO,bLcdE ; loesche E
ret
;
; Funktionseinstellung der LCD zu Beginn
;
LcdInitFunct:
sbi pLcdCD,bLcdE ; Enable-Ausgang
sbi pLcdCD,bLcdRs ; RS-Ausgang
cbi pLcdCO,bLcdRs ; loesche RS
ldi rmp,0x38 ; Setze 8 bit interface, 4 Zeilen
out pLcdDO,rmp ; an Datenausgang
rcall LcdE
rcall LcdWt4100 ; warte 4100 us
rcall LcdE
rcall LcdWt4100 ; warte 100 us
rcall LcdE
rcall LcdWt4100 ; warte 100 us
ldi rmp,0x08 ; Display aus
out pLcdDO,rmp
rcall LcdE
rcall LcdWt4100 ; warte 40 us
ldi rmp,0x01 ; loesche Display
out pLcdDO,rmp
rcall LcdE
rcall LcdWt4500 ; warte 4,5 ms
ldi rmp,0x06 ; Eingabemodus: erhoehen, keine Shift
out pLcdDO,rmp
rcall LcdE
rcall LcdWt40
ldi rmp,0x10 ; Display Cursor Shift
out pLcdDO,rmp
rcall LcdE
rcall LcdWt40 ; warte 40 us
ldi rmp,0x0C ; Display aktiv, Cursor aus, nicht blinkend
out pLcdDO,rmp
rcall LcdE
rcall LcdWt40 ; warte 40 us
ldi rmp,0x02 ; Display/Cursor Home
out pLcdDO,rmp
rcall LcdE
rjmp LcdWt1640 ; warte fuer 1,64 ms
;
; Sende Kontrollanweisung in rmp an LCD
;
LcdCtrl:
out pLcdDO,rmp ; Daten an Port
cbi pLcdCO,bLcdRs ; loesche RS
rcall LcdE ; aktiviere E fuer 1 us
rjmp LcdWt100 ; Verzoegerung 100 us
;
; Schreibe Zeichen in rmp an LCD
;
LcdChar:
push rmp
push R0
mov R0,rmp ; kopiere Zeichen in R0
rcall LcdR0
pop R0
pop rmp
ret
;
; Schreibe Zeichen in R0 an LCD
;
LcdR0:
out pLcdDO,R0 ; Zeichen auf Ausgabeport
sbi pLcdCO,bLcdRs ; Setze RS
rcall LcdE ; aktiviere E fuer 1 µs
rjmp LcdWt100 ; warten 40 us
;
; Schreibe Text im Flash ab Z auf Display
;
LcdDisplZ:
lpm R0,Z+ ; lese ein Zeichen aus Flash
tst R0 ; NULL-Zeichen?
breq LcdDisplZ1
rcall LcdR0 ; Zeichen an Display
rjmp LcdDisplZ
LcdDisplZ1:
adiw ZL,1 ; ueberlese zweites Null-Zeichen
ret
;
; Initiiere die LCD und gib Begruessung aus
;
LcdInit:
; init LCD hardware
cbi pLcdCO,bLcdRS ; init LCD control bits
sbi pLcdCD,bLcdRS
cbi pLcdCO,bLcdE
sbi pLcdCD,bLcdE
ldi rmp, 0xFF ; init data port als output
out pLcdDD,rmp
; init LCD Betrieb
rcall LcdInitFunct ; Setze 8-bit interface und Anzahl Zeilen
rcall LcdWt15000 ; Warten fuer 15 ms
rcall UserChars ; Init Spezialcharacter
; Ausgabe LCD init text
ldi ZH,HIGH(2*LcdTxtInit1) ; Z auf Text
ldi ZL,LOW(2*LcdTxtInit1)
LcdTxt: ; schreibt Lcd-Text ab Z auf Display
ldi rmp,0 ; setze LCD-Zeile 1
rcall LcdLine
rcall LcdDisplZ ; schreibe Text auf LCD
ldi rmp,1 ; setze LCD-Zeile 2
rcall LcdLine
rcall LcdDisplZ
ldi rmp,2 ; setze LCD-Zeile 3
rcall LcdLine
rcall LcdDisplZ
ldi rmp,3 ; setze LCD-Zeile 4
rcall LcdLine
rjmp LcdDisplZ
LcdFrame:
ldi ZH,HIGH(2*LcdTxtFrame)
ldi ZL,LOW(2*LcdTxtFrame)
rjmp LcdTxt
;
; LcdLine setzt den Eingabecursor in die ausgewahlte Zeile in rmp
;
LcdLine:
cpi rmp,1 ; rmp = 0 oder 1
brcs LcdLine0 ; rmp=0, setzt Zeile 1
breq LcdLine1 ; rmp=1, setzt Zeile 2
cpi rmp,2
breq LcdLine3 ; rmp=2, setzt Zeile 3
LcdLine4:
ldi rmp,0x54 ; rmp>2, setzt Zeile 4
rjmp LcdLineSet
LcdLine0:
clr rmp ; Zeile 1 startet bei 0
rjmp LcdLineSet
LcdLine1:
ldi rmp,0x40 ; Zeile 2 startet bei 0x40
rjmp LcdLineSet
LcdLine3:
ldi rmp,20 ; Zeile 3 startet bei 20
LcdLineSet:
sbr rmp,0x80 ; setze Kontrollbit
rjmp LcdCtrl
;
; LCD loesche Zeile
;
LcdLineCl:
mov ZH,rmp ; sichere Zeilennummer
rcall LcdLine ; setze Zeile
ldi rmp,' ' ; Leerzeichen
mov R0,rmp ; nach R0
ldi ZL,20 ; ZL ist Zaehler
LcdLineCl1:
rcall LcdR0 ; schreibe Zeichen in R0
dec ZL ; naechstes
brne LcdLineCl1 ; noch mal
mov rmp,ZH ; stelle rmp wieder her
rjmp LcdLine
;
; LcdPos setzt die LCD auf die Position in Z
; ZH: Zeile, 0, 1, 2 oder 3; ZL: Spalte, 0 bis 19
;
LcdPos:
push rmp ; sichern rmp
clr rmp
cpi ZH,1 ; Zeile = 1 oder 2
brcs LcdPos1 ; Zeile = 1
ldi rmp,0x40 ; Zeile = 2
breq LcdPos1
ldi rmp,20 ; Zeile=3
cpi ZH,3
brcs LcdPos1
ldi rmp,0x40+20
LcdPos1:
sbr rmp,0x80
add rmp,ZL
rcall LcdCtrl
pop rmp ; wiederherstellen rmp
ret
;
; LcdChAt setzt die LCD auf die Position in Z und gibt
; rmp als Zeichen aus
LcdChAt:
rcall LcdPos
rjmp LcdChar
;
; LcdSram schreibt rmp Zeichen von Position X im SRAM
; an Position Z der LCD
LcdSram:
rcall LcdPos ; setze Position LCD
LcdSram1:
ld R0,X+ ; lese Zeichen
rcall LcdR0 ; schreibe Zeichen
dec rmp
brne LcdSram1
ret
;
; LcdDec2 schreibt die Binaerzahl in R0 mit
; zwei Dezimalziffern auf die LCD
LcdDec2:
ldi rmp,100 ; Zahlen groesser 99
cp R0,rmp
brcs LcdDec2a
ldi rmp,99
mov R0,rmp
LcdDec2a:
ldi ZL,10 ; Teiler fuer Zehner
ldi rmp,'0'-1
LcdDec2b:
inc rmp
sub R0,ZL
brcc LcdDec2b
add R0,ZL
rcall LcdChar
ldi rmp,'0'
add rmp,R0
rjmp LcdChar
;
; LCDDEC16 schreibt die Binaerzahl in R1:R0 auf
; die LCD
;
LcdDec16:
set ; T Flagge zur Unterdrueckung fuehrender Nullen
ldi ZH,High(10000)
ldi ZL,Low(10000)
rcall LcdDecZ
ldi ZH,High(1000)
ldi ZL,Low(1000)
rcall LcdDecZ
ldi ZH,High(100)
ldi ZL,Low(100)
rcall LcdDecZ
ldi ZH,High(10)
ldi ZL,Low(10)
rcall LcdDecZ
ldi rmp,'0'
add rmp,R0
rjmp LcdChar
;
; Ermittelt die Dezimalziffer und schreibt sie auf die LCD
; Binaerzahl in R1:R0
; Dezimalzahl in ZH:ZL
; T-Flagge: Unterdrueckung fuehrender Nullen
LcdDecZ:
ldi rmp,'0'-1
LcdDecZ1:
inc rmp
sub R0,ZL
sbc R1,ZH
brcc LcdDecZ1
add R0,ZL
adc R1,ZH
brtc LcdDecZ3
cpi rmp,'0'
breq LcdDecZ2
clt
rjmp LcdDecZ3
LcdDecZ2:
ldi rmp,' '
LcdDecZ3:
rjmp LcdChar
;
; LCDHEX4 schreibt den Inhalt von Z in Hex
;
LcdHex4:
mov rmp,ZH ; ZH ist High Byte
rcall LcdHex2
mov rmp,ZL ; schreibe Low Byte
;
; LCDHEX2 schreibt den Inhalt von rmp in Hex (debug)
;
LcdHex2:
push rmp
swap rmp
rcall LcdHexN
pop rmp
LcdHexN:
andi rmp,0x0F
cpi rmp,0x0A
brcs LcdHexN1
subi rmp,-7
LcdHexN1:
subi rmp,-'0'
rcall LcdChar
ret
;
; Blinken der LCD einschalten
;
LcdBlink:
ldi rmp,0x0F
rjmp LcdCtrl
;
; Blinken der LCD ausschalten
;
LcdNoBlink:
ldi rmp,0x0C
rjmp LcdCtrl
;
; Schreibe User-Character
;
UserChars:
ldi ZH,HIGH(2*CharTab)
ldi ZL,LOW(2*CharTab)
UserChars1:
lpm rmp,Z+
tst rmp
breq UserChars3
rcall LcdCtrl
ldi XL,8
UserChars2:
rcall LcdWt40
lpm rmp,Z+
rcall LcdChar
dec XL
brne UserChars2
lpm rmp,Z+
rjmp UserChars1
UserChars3:
rcall LcdWt100
ret
CharTab:
.DB 72,4,4,4,4,21,14,4,0,0 ; 0x01 Pfeil abwaerts
.DB 80,4,14,21,4,4,4,4,0,0 ; 0x02 Pfeil aufwaerts
.DB 88,5,11,19,23,16,8,4,0,0 ; 0x03 Drehpfeil rechts
.DB 96,20,26,17,29,1,10,4,0,0 ; 0x04 Drehpfeil links
.DB 120,4,10,23,25,25,23,10,4,0 ; 0x07 Copyright
.DW 0
;
; Ende der LCD Include-Datei
;
Dieser Code im asm-Format
;
; ***********************************
; * Musik spielen mit dem ATmega16 *
; * (C)2018 by avr-asm-tutorial.net *
; ***********************************
;
; Ein Musikstueck in das RAM kopieren und Ausgabe starten
; rmp zeigt auf eine Melodie (0..8 direkt, ab 9 mit modulo)
MusicConvert:
cpi rmp,cMaxMelody+1 ; rmp innerhalb der Anzahl Melodien?
brcs MusicConvert1
subi rmp,cMaxMelody
brcc MusicConvert
subi rmp,-cMaxMelody ; Addiere Anzahl Melodien
MusicConvert1:
; Melody to play
ldi ZH,High(2*MelodyTab)
ldi ZL,Low(2*MelodyTab)
lsl rmp
add ZL,rmp
ldi rmp,0
adc ZH,rmp
lpm R0,Z+
lpm ZH,Z
mov ZL,R0
; Z points to melody
ldi XH,High(sMelody) ; Auf SRAM-Melodie zeigen
ldi XL,Low(sMelody)
MusicConvert2:
lpm rmp,Z+ ; Lese Note
cpi rmp,MelodyEnd ; Ende der Melodie?
breq MusicConvertEnd
rcall ConvertNote ; CTC nach Y, Dauer nach R1:R0
lpm rmp,Z ; Lese Dauer-Multiplikator
andi rmp,0x0F ; Niedrigeres Nibble
MusicConvert3:
tst rmp
breq MusicConvert6
lsr R1 ; Teile Dauer durch zwei
brne MusicConvert5 ; Nicht Null, ohne runden
ror R0 ; MSB Null, Rollen mit Runden
brcc MusicConvert4 ; Keine Eins im Carry
inc R0 ; Aufrunden
brne MusicConvert4 ; Kein Carry
inc R1 ; Erhoehe MSB
MusicConvert4:
dec rmp ; Vermindere Anzahl
rjmp MusicConvert3
MusicConvert5:
ror R0 ; MSB groesser als 0, rolle ohne Runden
dec rmp ; Vermindere Anzahl
rjmp MusicConvert3
MusicConvert6:
lpm rmp,Z+ ; Teiler nochmals lesen
swap rmp ; Oberes Nibble in unteres Nibble
andi rmp,0x0F ; Isoliere Addierer
breq MusicConvert8 ; Kein Addieren
push ZH ; Sichern Z zum Addieren
push ZL
mov ZH,R1 ; Kopiere aktuellen Zaehler
mov ZL,R0
MusicConvert7:
add R0,ZL ; Addiere einmal
adc R1,ZH
dec rmp ; Vermindere Addierer
brne MusicConvert7 ; Addiere weiter
pop ZL ; Wiederherstellen von Z
pop ZH
MusicConvert8:
mov rmp,R0 ; Zaehler bei Null?
or rmp,R1 ; Oder mit MSB
brne MusicConvert9
inc R0 ; Minimum ein CTC-Zyklus
MusicConvert9:
st X+,YH ; Schreibe MSB CTC in SRAM
st X+,YL ; Dann LSB CTC in SRAM
st X+,R1 ; MSB Dauer in SRAM
st X+,R0 ; LSB Dauer in SRAM
cpi XH,High(sMelodyEnd+4) ; MSB Ende des Puffers erreicht?
brcs MusicConvert2 ; Nein
cpi XL,Low(sMelodyEnd+4) ; LSB?
brcs MusicConvert2 ; Nein
MusicConvertEnd:
ldi rmp,0xFF ; Ende der Melodie, 0xFFFF ins SRAM
st X+,rmp
st X+,rmp
clr rmp ; Und 0x0000 dazu
st X+,rmp
st X,rmp
;
; Spiele Musik im SRAM (neu oder erneut)
MusicPlay:
ldi YH,High(sMelody) ; Y zeigt auf SRAM-Puffer
ldi YL,Low(sMelody)
ldi rmp,(1<<TOIE0)|(1<<OCIE1A)|(1<<TOIE2) ; Enable Int TC1
out TIMSK,rmp ; in Timer-Int-Maske
ldi rDurL,1 ; Starte mit Dauer = 1
clr rDurH
ret
;
; Konvertiert die Musiknote in rmp
; Liefert CTC 16-Bit-Wert in Y
; Dauer 16-Bit-Wert in R1:R0
ConvertNote:
push ZH ; Sichern Z
push ZL
ldi ZH,High(2*NotesTimerTable)
ldi ZL,Low(2*NotesTimerTable)
mov R0,rmp
clr R1
lsl R0 ; Multiplikation mit 4
rol R1
lsl R0
rol R1
add ZL,R0 ; Addiere zur Notentabelle
adc ZH,R1
lpm YL,Z+
lpm YH,Z+
lpm R0,Z+
lpm R1,Z
pop ZL
pop ZH
ret
;
; Notenfrequenztabelle
; Zur Frequenzberechnung der Noten aus dem Takt
; fe wegen Konflikt mit FE (Framing Error, UART)
; in fee umbenannt!
NoteFrequencyTable:
.equ fA2m=55 ; #0, f = 27,5
.equ fH2m=62 ; #1, f = 30,8677
.equ fC1m=65 ; #2, f = 32,7032
.equ fD1m=73 ; #3, f = 36,7081
.equ fE1m=82 ; #4, f = 41,2034
.equ fF1m=87 ; #5, f = 43,6535
.equ fG1m=98 ; #6, f = 48,9994
.equ fA1m=110 ; #7, f = 55
.equ fH1m=123 ; #8, f = 61,7354
.equ fCm=131 ; #9, f = 65,4064
.equ fDm=147 ; #10, f = 73,4162
.equ fEm=165 ; #11, f = 82,4069
.equ fFm=175 ; #12, f = 87,3071
.equ fGm=196 ; #13, f = 97,9989
.equ fAm=220 ; #14, f = 110
.equ fHm=247 ; #15, f = 123,471
.equ fc=262 ; #16, f = 130,813
.equ fd=294 ; #17, f = 146,832
.equ fee=330 ; #18, f = 164,814
.equ ff=349 ; #19, f = 174,614
.equ fg=392 ; #20, f = 195,998
.equ fa=440 ; #21, f = 220
.equ fh=494 ; #22, f = 246,942
.equ fc1=523 ; #23, f = 261,626
.equ fd1=587 ; #24, f = 293,665
.equ fe1=659 ; #25, f = 329,628
.equ ff1=698 ; #26, f = 349,228
.equ fg1=784 ; #27, f = 391,995
.equ fa1=880 ; #28, f = 440
.equ fh1=988 ; #29, f = 493,883
.equ fc2=1047 ; #30, f = 523,251
.equ fd2=1175 ; #31, f = 587,33
.equ fe2=1319 ; #32, f = 659,255
.equ ff2=1397 ; #33, f = 698,456
.equ fg2=1568 ; #34, f = 783,991
.equ fa2=1760 ; #35, f = 880
.equ fh2=1976 ; #36, f = 987,767
.equ fc3=2093 ; #37, f = 1046,5
.equ fd3=2349 ; #38, f = 1174,66
.equ fe3=2637 ; #39, f = 1318,51
.equ ff3=2794 ; #40, f = 1396,91
.equ fg3=3136 ; #41, f = 1567,98
.equ fa3=3520 ; #42, f = 1760
.equ fh3=3951 ; #43, f = 1975,53
.equ fc4=4186 ; #44, f = 2093
.equ fd4=4699 ; #45, f = 2349,32
.equ fe4=5274 ; #46, f = 2637,02
.equ ff4=5588 ; #47, f = 2793,83
.equ fg4=6272 ; #48, f = 3135,96
.equ fa4=7040 ; #49, f = 3520
.equ fh4=7902 ; #50, f = 3951,07
.equ fc5=8372 ; #51, f = 4186,01
.equ fd5=9397 ; #52, f = 4698,65
.equ fe5=10548 ; #53, f = 5274,05
.equ ff5=11175 ; #54, f = 5587,67
.equ fg5=12544 ; #55, f = 6271,93
.equ fa5=14080 ; #56, f = 7040
.equ fh5=15804 ; #57, f = 7902,13
.equ fc6=16744 ; #58, f = 8372,02
.equ fd6=18795 ; #59, f = 9397,28
.equ fe6=21096 ; #60, f = 10548,08
.equ ff6=22351 ; #61, f = 11175,3
.equ fg6=25088 ; #62, f = 12543,86
.equ fa6=28160 ; #63, f = 14080
.equ fh6=31609 ; #64, f = 15804,26
.equ fc7=33488 ; #65, f = 16744,03
.equ fd7=37589 ; #66, f = 18794,56
.equ fe7=42192 ; #67, f = 21096,16
.equ ff7=44701 ; #68, f = 22350,59
.equ fg7=50175 ; #69, f = 25087,71
;
; Notentabelle: liefert den CTC-Wert fuer TC1 und
; Anzahl CTC-Durchlaeufe fuer eine Dauer von
; einer Sekunde fuer alle spielbaren Noten
; Abgestimmt auf 16-Bit-Timer, Taktfrequenz 3,2768 MHz
; und Vorteiler = 1
; Timerabelle fuer die Musiknoten 0 bis 70
; Erstes Wort: Compare-Wert fuer CTC
; Zweites Wort: Dauer = Anzahl CTC-Zyklen fuer 1 Sekunde
; = 2*Notenfrequenz
NotesTimerTable:
.dw clock/fA2m-1, 55 ; #0, f=27,5
.dw clock/fH2m-1, 62 ; #1, f=30,8677
.dw clock/fC1m-1, 65 ; #2, f=32,7032
.dw clock/fD1m-1, 73 ; #3, f=36,7081
.dw clock/fE1m-1, 82 ; #4, f=41,2034
.dw clock/fF1m-1, 87 ; #5, f=43,6535
.dw clock/fG1m-1, 98 ; #6, f=48,9994
.dw clock/fA1m-1, 110 ; #7, f=55
.dw clock/fH1m-1, 123 ; #8, f=61,7354
.dw clock/fCm-1, 131 ; #9, f=65,4064
.dw clock/fDm-1, 147 ; #10, f=73,4162
.dw clock/fEm-1, 165 ; #11, f=82,4069
.dw clock/fFm-1, 175 ; #12, f=87,3071
.dw clock/fGm-1, 196 ; #13, f=97,9989
.dw clock/fAm-1, 220 ; #14, f=110
.dw clock/fHm-1, 247 ; #15, f=123,471
.dw clock/fc-1, 262 ; #16, f=130,813
.dw clock/fd-1, 294 ; #17, f=146,832
.dw clock/fee-1, 330 ; #18, f=164,814
.dw clock/ff-1, 349 ; #19, f=174,614
.dw clock/fg-1, 392 ; #20, f=195,998
.dw clock/fa-1, 440 ; #21, f=220
.dw clock/fh-1, 494 ; #22, f=246,942
.dw clock/fc1-1, 523 ; #23, f=261,626
.dw clock/fd1-1, 587 ; #24, f=293,665
.dw clock/fe1-1, 659 ; #25, f=329,628
.dw clock/ff1-1, 698 ; #26, f=349,228
.dw clock/fg1-1, 784 ; #27, f=391,995
.dw clock/fa1-1, 880 ; #28, f=440
.dw clock/fh1-1, 988 ; #29, f=493,883
.dw clock/fc2-1, 1047 ; #30, f=523,251
.dw clock/fd2-1, 1175 ; #31, f=587,33
.dw clock/fe2-1, 1319 ; #32, f=659,255
.dw clock/ff2-1, 1397 ; #33, f=698,456
.dw clock/fg2-1, 1568 ; #34, f=783,991
.dw clock/fa2-1, 1760 ; #35, f=880
.dw clock/fh2-1, 1976 ; #36, f=987,767
.dw clock/fc3-1, 2093 ; #37, f=1046,5
.dw clock/fd3-1, 2349 ; #38, f=1174,66
.dw clock/fe3-1, 2637 ; #39, f=1318,51
.dw clock/ff3-1, 2794 ; #40, f=1396,91
.dw clock/fg3-1, 3136 ; #41, f=1567,98
.dw clock/fa3-1, 3520 ; #42, f=1760
.dw clock/fh3-1, 3951 ; #43, f=1975,53
.dw clock/fc4-1, 4186 ; #44, f=2093
.dw clock/fd4-1, 4699 ; #45, f=2349,32
.dw clock/fe4-1, 5274 ; #46, f=2637,02
.dw clock/ff4-1, 5588 ; #47, f=2793,83
.dw clock/fg4-1, 6272 ; #48, f=3135,96
.dw clock/fa4-1, 7040 ; #49, f=3520
.dw clock/fh4-1, 7902 ; #50, f=3951,07
.dw clock/fc5-1, 8372 ; #51, f=4186,01
.dw clock/fd5-1, 9397 ; #52, f=4698,65
.dw clock/fe5-1, 10548 ; #53, f=5274,05
.dw clock/ff5-1, 11175 ; #54, f=5587,67
.dw clock/fg5-1, 12544 ; #55, f=6271,93
.dw clock/fa5-1, 14080 ; #56, f=7040
.dw clock/fh5-1, 15804 ; #57, f=7902,13
.dw clock/fc6-1, 16744 ; #58, f=8372,02
.dw clock/fd6-1, 18795 ; #59, f=9397,28
.dw clock/fe6-1, 21096 ; #60, f=10548,08
.dw clock/ff6-1, 22351 ; #61, f=11175,3
.dw clock/fg6-1, 25088 ; #62, f=12543,86
.dw clock/fa6-1, 28160 ; #63, f=14080
.dw clock/fh6-1, 31609 ; #64, f=15804,26
.dw clock/fc7-1, 33488 ; #65, f=16744,03
.dw clock/fd7-1, 37589 ; #66, f=18794,56
.dw clock/fe7-1, 42192 ; #67, f=21096,16
.dw clock/ff7-1, 44701 ; #68, f=22350,59
.dw clock/fg7-1, 50175 ; #69, f=25087,71
; Pause
.dw 0xFFFF,clock/0xFFFF ; #70, f = clock/131130
;
; Note names
NotesNameTable:
.equ nA2m = 0 ; A2, f = 27,5 Hz, #0
.equ nH2m = 1 ; H2, f = 30,8677 Hz
.equ nC1m = 2 ; C1, f = 32,7032 Hz
.equ nD1m = 3 ; D1, f = 36,7081 Hz
.equ nE1m = 4 ; E1, f = 41,2034 Hz
.equ nF1m = 5 ; F1, f = 43,6535 Hz
.equ nG1m = 6 ; G1, f = 48,9994 Hz
.equ nA1m = 7 ; A1, f = 55 Hz
.equ nH1m = 8 ; H1, f = 61,7354 Hz
.equ nCm = 9 ; C, f = 65,4064 Hz
.equ nDm = 10 ; D, f = 73,4162 Hz
.equ nEm = 11 ; E, f = 82,4069 Hz
.equ nFm = 12 ; F, f = 87,3071 Hz
.equ nGm = 13 ; G, f = 97,9989 Hz
.equ nAm = 14 ; A, f = 110 Hz
.equ nHm = 15 ; H, f = 123,471 Hz
.equ nc = 16 ; c, f = 130,813 Hz
.equ nd = 17 ; d, f = 146,832 Hz
.equ ne = 18 ; e, f = 164,814 Hz
.equ nf = 19 ; f, f = 174,614 Hz
.equ ng = 20 ; g, f = 195,998 Hz
.equ na = 21 ; a, f = 220 Hz
.equ nh = 22 ; h, f = 246,942 Hz
.equ nc1 = 23 ; c1, f = 261,626 Hz
.equ nd1 = 24 ; d1, f = 293,665 Hz
.equ ne1 = 25 ; e1, f = 329,628 Hz
.equ nf1 = 26 ; f1, f = 349,228 Hz
.equ ng1 = 27 ; g1, f = 391,995 Hz
.equ na1 = 28 ; a1, f = 440 Hz
.equ nh1 = 29 ; h1, f = 493,883 Hz
.equ nc2 = 30 ; c2, f = 523,251 Hz
.equ nd2 = 31 ; d2, f = 587,33 Hz
.equ ne2 = 32 ; e2, f = 659,255 Hz
.equ nf2 = 33 ; f2, f = 698,456 Hz
.equ ng2 = 34 ; g2, f = 783,991 Hz
.equ na2 = 35 ; a2, f = 880 Hz
.equ nh2 = 36 ; h2, f = 987,767 Hz
.equ nc3 = 37 ; c3, f = 1046,5 Hz
.equ nd3 = 38 ; d3, f = 1174,66 Hz
.equ ne3 = 39 ; e3, f = 1318,51 Hz
.equ nf3 = 40 ; f3, f = 1396,91 Hz
.equ ng3 = 41 ; g3, f = 1567,98 Hz
.equ na3 = 42 ; a3, f = 1760 Hz
.equ nh3 = 43 ; h3, f = 1975,53 Hz
.equ nc4 = 44 ; c4, f = 2093 Hz
.equ nd4 = 45 ; d4, f = 2349,32 Hz
.equ ne4 = 46 ; e4, f = 2637,02 Hz
.equ nf4 = 47 ; f4, f = 2793,83 Hz
.equ ng4 = 48 ; g4, f = 3135,96 Hz
.equ na4 = 49 ; a4, f = 3520 Hz
.equ nh4 = 50 ; h4, f = 3951,07 Hz
.equ nc5 = 51 ; c5, f = 4186,01 Hz
.equ nd5 = 52 ; d5, f = 4698,65 Hz
.equ ne5 = 53 ; e5, f = 5274,05 Hz
.equ nf5 = 54 ; f5, f = 5587,67 Hz
.equ ng5 = 55 ; g5, f = 6271,93 Hz
.equ na5 = 56 ; a5, f = 7040 Hz
.equ nh5 = 57 ; h5, f = 7902,13 Hz
.equ nc6 = 58 ; c6, f = 8372,02 Hz
.equ nd6 = 59 ; d6, f = 9397,28 Hz
.equ ne6 = 60 ; e6, f = 10548,08 Hz
.equ nf6 = 61 ; f6, f = 11175,3 Hz
.equ ng6 = 62 ; g6, f = 12543,86 Hz
.equ na6 = 63 ; a6, f = 14080 Hz
.equ nh6 = 64 ; h6, f = 15804,26 Hz
.equ nc7 = 65 ; c7, f = 16744,03 Hz
.equ nd7 = 66 ; d7, f = 18794,56 Hz
.equ ne7 = 67 ; e7, f = 21096,16 Hz
.equ nf7 = 68 ; f7, f = 22350,59 Hz
.equ ng7 = 69 ; g7, f = 25087,71 Hz
; Pause
.equ p = 70 ; Pause, stumm, #70
; End
.equ MelodyEnd = 0xFF
;
; Melodien
; Melodien die die Uhr zufaellig spielen kann
; Maximum 16 Melodien (0..15)
;
.equ cMaxMelody = 8 ; Anzahl der programmierten Melodien
;
; Melodietabelle
; Tabelleneintraege zeigen auf den Beginn der Melodie
; im Flash mal zwei
;
MelodyTab:
.dw 2*Melody0
.if cMaxMelody>=1
.dw 2*Melody1
.endif
.if cMaxMelody>=2
.dw 2*Melody2
.endif
.if cMaxMelody>=3
.dw 2*Melody3
.endif
.if cMaxMelody>=4
.dw 2*Melody4
.endif
.if cMaxMelody>=5
.dw 2*Melody5
.endif
.if cMaxMelody>=6
.dw 2*Melody6
.endif
.if cMaxMelody>=7
.dw 2*Melody7
.endif
.if cMaxMelody>=8
.dw 2*Melody8
.endif
.if cMaxMelody>=9
.dw 2*Melody9
.endif
.if cMaxMelody>=10
.dw 2*Melody10
.endif
.if cMaxMelody>=11
.dw 2*Melody11
.endif
.if cMaxMelody>=12
.dw 2*Melody12
.endif
.if cMaxMelody>=13
.dw 2*Melody13
.endif
.if cMaxMelody>=14
.dw 2*Melody14
.endif
.if cMaxMelody>=15
.dw 2*Melody15
.endif
.if cMaxMelody>=16
.error "Too many melodies"
.endif
;
; Dauerteiler fuer Toene und Pausen:
.equ d1 = 0 ; 16/16
.equ d2 = 1 ; 8/16
.equ d4 = 2 ; 4/16
.equ d8 = 3 ; 2/16
.equ d16 = 4 ; 1/16
.equ d32 = 5 ; 1/32
.equ d316 = 0x34 ; 3/16
.equ d38 = 0x33 ; 6/16
;
; Melodies
; Each melody encodes
; - notes to be played (nx) and one duration byte
; - pauses (p) to be made and one duration byte
; - an end signature (MelodyEnd)
;
Melody0: ; Ode an die Freude
; Freu- de schoe- ner
.db nh,d4,p,d8,nh,d4,p,d8,nc1,d4,p,d8,nd1,d4,p,d8
; Goet- ter- fun- ken
.db nd1,d4,p,d8,nc1,d4,p,d8,nh,d4,p,d8,na,d4,p,d8
; Toch- ter aus E-
.db ng,d4,p,d8,ng,d4,p,d8,na,d4,p,d8,nh,d4,p,d8
; ly- si- um,
.db nh,d4,p,d8,na,d8,p,d8,na,d4,p,d1
.db MelodyEnd,MelodyEnd
;
Melody1: ; Marmor, Stein und Eisen bricht
; Wei- ne nicht, wenn der
.db na,d8,p,d8,na,d4,p,d8,na,d4,p,d8,ng,d8,p,d8,na,d4,p,d8
; Re- gen faellt,
.db nc1,d8,p,d8,na,d4,p,d8,na,d38,p,d4
; dam, dam, dam, dam
.db na,d4,p,d2,nh,d4,p,d2,nd1,d4,p,d8,nc1,d2,p,d2
; Mar- mor, Stein und
.db na1,d8,p,d8,na1,d4,p,d8,na1,d38,p,d8,na1,d4,p,d8
; Ei- sen bricht,
.db ng1,d8,p,d8,nf1,d4,p,d8,nf1,d38,p,d8
; a- ber un- se- re
.db ng1,d8,p,d8,ng1,d4,p,d8,ng1,d4,p,d8,nf1,d8,p,d8,ng1,d4,p,d8
; Lie- be nicht.
.db na1,d4,ne1,d4,nf1,d4,p,d8,ne1,d38,p,d2
; Al- les, al- les geht
.db na1,d4,p,d16,na1,d4,p,d4,na1,d4,p,d16,na1,d4,p,d4,ng1,d8,p,d8
; vor- bei, doch wir
.db nf1,d4,p,d8,nf1,d38,p,6,ng1,d4,p,d8,ng1,d4,p,d8
; sind uns treu
.db nf1,d8,p,d8,ng1,d4,p,d8,na1,d2
.db MelodyEnd,MelodyEnd
;
Melody2: ; When I'm sixty four
; When I get old- er
.db nc,d4,p,d16,nhm,d4,p,d16,nc,d4,p,d16,ne,d4,p,d8,ne,d4,p,d8
; loo- sing my hair...
.db ne,d4,p,d16,nf,d4,p,d16,ne,d4,p,d16,na,d2,p,d8
; ma- ny years... from now,
.db na,d8,p,d8,nc1,d4,p,d8,na,d38,p,d8,nf,d4,p,d8,nh,d2,p,d2
; will you still be send-
.db ng,d4,p,d16,na,d4,p,d16,na,d4,p,d16,nc1,d4,p,d8,ng,d4,p,d16
; ing me a val- en- tine...
.db na,d4,p,d16,na,d4,p,d16,nc1,d4,p,d8,ng,d8,p,d8,ng,d4,p,d8,nf,d38,p,d8
; birth- day greet- ings,
.db ng,d4,p,d8,na,d4,p,d8,na,d4,p,d8,nh,d4,p,d8
; bott- le of wine?
.db nc1,d4,p,d16,nc1,d4,p,d16,nh,d4,p,d16,na,d38,p,d2
; If I'd been out...
.db nc,d4,p,d16,nhm,d4,p,d16,nc,d4,p,d16,ne,d4,p,d8
; till quar- ter to three...
.db nc,d4,p,d8,ne,d4,p,d16,nf,d4,p,d16,ne,d4,p,d16,na,d8,na,d4,p,d2
; would you lock the door?
.db nc1,d8,p,d8,nc1,d316,p,d8,nh,d4,p,d8,nf,d4,p,d16,na,d2,p,d2
; will you still need me,
.db na,d4,p,d16,nf,d4,p,d16,na,d4,p,d16,nc1,d38,p,d8,nh,d4,p,d8
; will you still feed me
.db na,d4,p,d16,nf,d4,p,d16,na,d4,p,d16,ng,d38,p,d16,nf,d4,p,d8
; when I'm six- ty four?
.db nc1,d8,p,d8,nc1,d316,p,d8,nc1,d8,p,d8,nc1,d4,p,d8,na,d2
.db MelodyEnd,MelodyEnd
;
Melody3: ; Smoke on the water
; Smoke on the wa- ter
.db ne1,d2,p,d8,nd1,d4,p,d16,nc1,d4,p,d8,ne1,d4,p,d8,nc1,d4,p,d2
; A Fi- re in the sky
.db ng,d8,p,d8,nh,d4,p,d16,ng,d4,p,d8,nh,d4,p,d16,ng,d4,p,d8,nf,d4,ng,d2
.db MelodyEnd,MelodyEnd
;
Melody4: ; The wall
; We don't need no
.db nd,d8,p,d8,ne,d4,p,d8,nf,d4,p,d8,ne,d4,p,d8
; ed- u- ca- tion
.db nd,d4,p,d8,ne,d4,p,d16,nf,d38,p,d8,ne,d4,p,d2
; We don't need no
.db nd,d8,p,d8,ne,d4,p,d8,nf,d4,p,d8,ne,d4,p,d8
; thought control,
.db nd,d4,p,d8,ne,d4,p,d8
; dark sar- ca- sm
.db nd,d4,p,d8,ne,d4,p,d16,nf,d4,p,d16,ne,d4,p,d4
; in the class- room
.db nd,d4,p,d8,ne,d38,p,d8,nf,d4,p,d8,ne,d4,p,d2
; teach or leave them
.db nd,d4,p,d8,ne,d4,p,d16,nf,d38,p,d8,ne,d4,p,d4
; kids a- lone
.db nd,d4,p,d8,ne,d4,p,d16,nf,d2
.db MelodyEnd,MelodyEnd
;
Melody5: ; Emanuela
; Lass die Fin-
.db nf,d4,p,d4,nc1,d4,p,d4,nc1,d4,p,d16
; ger von E-
.db nc1,d4,p,d16,na,d4,p,d16,nf,d38,p,d4
; ma- nu- e- la
.db nc1,d4,p,d4,nd1,d8,p,d4,nc1,d4,p,d4,na,d4
.db MelodyEnd,MelodyEnd
;
Melody6: ; Morse
; G e r h a r d
; --. . .-. .... .- .-. -..
; '.' = 1/16 = d16, '-' = 3/16 = d316, p = 1/16 = d16, ' ' = 3/16 = d316
; - - .
.db nc3,d316,p,d16,nc3,d316,p,d16,nc3,d16,p,d316 ; G
; .
.db nc3,d16,p,d316 ; e
; . - .
.db nc3,d16,p,d16,nc3,d316,p,d16,nc3,d16,p,d316 ; r
; . . . .
.db nc3,d16,p,d16,nc3,d16,p,d16,nc3,d16,p,d16,nc3,d16,p,d316 ; h
; . -
.db nc3,d16,p,d16,nc3,d316,p,d316 ; a
; . - .
.db nc3,d16,p,d16,nc3,d316,p,d16,nc3,d16,p,d316 ; r
; - . .
.db nc3,d316,p,d16,nc3,d16,p,d16,nc3,d16,p,d1 ; d
; . . .
.db nc3,d16,p,d16,nc3,d16,p,d16,nc3,d16,p,d316 ; S
; - . - .
.db nc3,d316,p,d16,nc3,d16,p,d16,nc3,d316,p,d16,nc3,d16,p,d316 ; c
; . . . .
.db nc3,d16,p,d16,nc3,d16,p,d16,nc3,d16,p,d16,nc3,d16,p,d316 ; h
; - -
.db nc3,d316,p,d16,nc3,d316,p,d316 ; m
; . .
.db nc3,d16,p,d16,nc3,d16,p,d316 ; i
; - . .
.db nc3,d316,p,d16,nc3,d16,p,d16,nc3,d16,p,d316 ; d
; -
.db nc3,d316,p,d316 ; t
; . . .
.db nc3,d16,p,d16,nc3,d16,p,d16,nc3,d16,p,d16
; - . -
.db nc3,d316,p,d16,nc3,d16,p,d16,nc3,d316,p,d1
.db MelodyEnd,MelodyEnd
;
Melody7:
; Voel- ker hoert
.db ne1,d4,ne1,d8,nd1,d4,p,d8,nc1,d2,p,d8
; die Sig- na- le!
.db ng,d38,p,d8,ne,d8,p,d8,na,d2,p,d8,nf,d4,p,d4
; Auf zum letzt-
.db nd1,d38,p,d8,nc1,d4,p,d8,nh,d2,p,d8
; ten Ge- fecht!
.db na,d38,p,d8,ng,d8,p,d8,ng,d2
; Die In- ter-
.db ng,d4,p,d8,ne1,d4,p,d4,ne1,d8,p,d8
; na- tio- na- le
.db ne1,d8,p,d8,nd1,d4,p,d8,ng,d4,p,d8,nc1,d2,p,d8
; er- kaempft das
.db nh,d8,p,d8,na,d38,p,d8,ng,d8,p,d8
; Men- schen- recht!
.db na,d4,p,d8,nd1,d4,p,d8,nd1,d2
.db MelodyEnd,MelodyEnd
;
Melody8: ; The whole gamut
.db nA2m,d2,p,d16,nH2m,d2,p,d4
.db nC1m,d2,p,d16,nD1m,d2,p,d16,nE1m,d2,p,d16,nF1m,d2,p,d16
.db nG1m,d2,p,d16,nA1m,d2,p,d16,nH1m,d2,p,d4
.db nCm,d2,p,d16,nDm,d2,p,d16,nEm,d2,p,d16,nFm,d2,p,d16
.db nGm,d2,p,d16,nAm,d2,p,d16,nHm,d2,p,d4
.db nc,d2,p,d16,nd,d2,p,d16,ne,d2,p,d16,nf,d2,p,d16
.db ng,d2,p,d16,na,d2,p,d16,nh,d2,p,d4
.db nc1,d2,p,d16,nd1,d2,p,d16,ne1,d2,p,d16,nf1,d2,p,d16
.db ng1,d2,p,d16,na1,d2,p,d16,nh1,d2,p,d4
.db nc2,d2,p,d16,nd2,d2,p,d16,ne2,d2,p,d16,nf2,d2,p,d16
.db ng2,d2,p,d16,na2,d2,p,d16,nh2,d2,p,d4
.db nc3,d2,p,d16,nd3,d2,p,d16,ne3,d2,p,d16,nf3,d2,p,d16
.db ng3,d2,p,d16,na3,d2,p,d16,nh3,d2,p,d4
.db nc4,d2,p,d16,nd4,d2,p,d16,ne4,d2,p,d16,nf4,d2,p,d16
.db ng4,d2,p,d16,na4,d2,p,d16,nh4,d2,p,d4
.db nc5,d2,p,d16,nd5,d2,p,d16,ne5,d2,p,d16,nf5,d2,p,d16
.db ng5,d2,p,d16,na5,d2,p,d16,nh5,d2,p,d4
.db nc6,d2,p,d16,nd6,d2,p,d16,ne6,d2,p,d16,nf6,d2,p,d16
.db ng6,d2,p,d16,na6,d2,p,d16,nh6,d2,p,d4
.db nc7,d2,p,d16,nd7,d2
.db MelodyEnd,MelodyEnd
;
; Note: Update cMaxMelody after adding/removing melodies
;
; End of include file
;
Dieser Code im asm-Format
;
; *********************************
; * Debug Codes fuer dcf77_m16_v4 *
; * (C)2018 by Gerhard Schmidt *
; *********************************
;
; Debuggen der Auswertung von DCF-Signalbits in Datum/Zeit-Format
; Setze empfangene DCF-Signale auf Mo, 31.12.99-23:59
; DCF-Bytes 7:0 = 4C C8 E3 8D 64 00 00 00
DebugDcfBitConversion:
ldi rmp,0x4C
sts sDcfBits+7,rmp
ldi rmp,0xC8
sts sDcfBits+6,rmp
ldi rmp,0xE3
sts sDcfBits+5,rmp
ldi rmp,0x8D
sts sDcfBits+4,rmp
ldi rmp,0x64
sts sDcfBits+3,rmp
ldi rmp,0x00
sts sDcfBits+2,rmp
sts sDcfBits+1,rmp
sts sDcfBits,rmp
mov rDcfErr,rmp
ldi rmp,58
mov rDcfBitCnt,rmp
call Dcf59Ok ; Debuggen der Auswerteroutine
DbgMinuteLoop: ; Weitere Ausfuehrung anhalten
rjmp DbgMinuteLoop
;
; Debuggen des ADC durch Anzeige der Rohresultate
; des ausgewaehlten Kanals
DebugAdc:
ldi rmp,debugAdcChannel|(1<<REFS0) ; Kanalauswahl
out ADMUX,rmp
ldi ZH,3
ldi ZL,0
rcall LcdPos
ldi rmp,'A'
rcall LcdChar
ldi rmp,'D'
rcall LcdChar
ldi rmp,'C'
rcall LcdChar
ldi rmp,debugAdcChannel+48
rcall LcdChar
ldi rmp,':'
rcall LcdChar
LoopAdc:
ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(ADPS0)
out ADCSRA,rmp ; Start first conversion
WaitAdc:
sbic ADCSRA,ADSC
rjmp WaitAdc
ldi ZH,3
ldi ZL,5
rcall LcdPos
in ZL,ADCL
in ZH,ADCH
rcall LcdHex4
rjmp LoopAdc
;
; Debuggen der angeschlossenen Tasten durch fortlaufende
; Anzeige ihres aktuellen Zustands
DebugKey:
ldi ZH,3
ldi ZL,0
rcall LcdPos
in ZL,pKeyI
ldi rmp,'0'
sbrc ZL,bKeyRI
ldi rmp,'1'
rcall LcdChar
ldi rmp,'0'
sbrc ZL,bKeyYI
ldi rmp,'1'
rcall LcdChar
ldi rmp,'0'
sbrc ZL,bKeyWI
ldi rmp,'1'
rcall LcdChar
rjmp DebugKey
;
; Lautsprecher auf 1.000 Hz
DebugSpeaker:
sbi pSpkD,bSpkD ; Ausgangspin aktivieren
ldi rmp,High(clock/1000/2-1)
out OCR1AH,rmp
ldi rmp,Low(clock/1000/2-1)
out OCR1AL,rmp
ldi rmp,1<<COM1A0 ; Toggle bei COM1A
out TCCR1A,rmp
ldi rmp,(1<<WGM12)|(1<<CS10) ; CTC, Prescaler = 1
out TCCR1B,rmp
DebugSpeaker1:
rjmp DebugSpeaker1
;
; Initiierung des Debugging von DCF-Signaldauern
DebugDcfDur:
ldi ZH,3
ldi ZL,12
rcall LcdPos
ldi ZH,High(2*DebugDcfText)
ldi ZL,Low(2*DebugDcfText)
rjmp LcdDisplZ
;
; Anzeige der DCF-Signaldauer
DebugDcfDurDispl:
ldi ZH,3
ldi ZL,0
sbic pDcfI,bDcfI
ldi ZL,6
rcall LcdPos
mov R0,rDcfL ; Kopiere Pulsdauer
mov R1,rDcfH
lsl R0 ; Multiplikation mit 5
rol R1
lsl R0
rol R1
add R0,rDcfL
adc R1,rDcfH
rjmp LcdDec16 ; Anzeige in ms auf LCD
;
; DCF-Bits anzeigen
DebugDcfBitEcho:
ldi ZH,3
ldi ZL,0
rcall LcdPos
mov R0,rDcfBitCnt ; Bitanzahl anzeigen
rcall LcdDec2
ldi rmp,':'
rcall LcdChar
ldi rmp,' '
rcall LcdChar
lds ZL,sDcfBits+7 ; Neuestes Byte lesen
ldi ZH,8
Dcf01Ok1:
ldi rmp,'0' ; Null anzeigen
lsl ZL
brcc Dcf01Ok2
ldi rmp,'1' ; Eins anzeigen
Dcf01Ok2:
rcall LcdChar
dec ZH
brne Dcf01Ok1
ret
;
; Debuggen des DCF-Signals durch Anzeige der letzten 8 Bits
DebugDcfSig1:
ldi ZH,3 ; Langversion des Berichts auf Zeile 4
ldi ZL,0
rcall LcdPos
mov R0,rDcfBitCnt ; Anzeige der Bitanzahl
rcall LcdDec2
ldi ZH,High(2*LcdErr6) ; Fuege Rest des Textes hinzu
ldi ZL,Low(2*LcdErr6)
rjmp LcdDisplZ
;
; Debuggen des DCF-Signals mit langen Fehlermeldungen
DebugDcfSig2:
mov XH,ZH ; Sichere Z-Position im Flash nach X
mov XL,ZL
ldi ZH,3
ldi ZL,0
rcall LcdPos
mov ZH,XH ; Stelle Z-Position im Flash wieder her
mov ZL,XL
rjmp LcdDisplZ
;
; Anzeige des Tastenstatus in Zeile 3
KeyState:
ldi ZH,3
ldi ZL,0
rcall LcdPos
ldi rmp,'0'
sbic pKeyI,bKeyRI
ldi rmp,'1'
rcall LcdChar
ldi rmp,'0'
sbic pKeyI,bKeyYI
ldi rmp,'1'
rcall LcdChar
ldi rmp,'0'
sbic pKeyI,bKeyWI
ldi rmp,'1'
rcall LcdChar
ldi rmp,' '
rcall LcdChar
ldi rmp,'f'
rcall LcdChar
ldi rmp,'0'
sbrc rFlag,bKeyAct
ldi rmp,'1'
rcall LcdChar
ldi rmp,' '
rcall LcdChar
ldi rmp,'C'
rcall LcdChar
mov rmp,rKeyCnt
rcall LcdHex2
ldi rmp,' '
rcall LcdChar
ldi rmp,'m'
rcall LcdChar
lds rmp,sKeyMode
subi rmp,-'0'
rcall LcdChar
ldi rmp,' '
rcall LcdChar
ldi rmp,'P'
rcall LcdChar
lds rmp,sKeyPos
subi rmp,-'0'
rcall LcdChar
ret
;
; Zeige Debug-Aktivierung in Zeile 3 an
;
DebugActive:
ldi rmp,2
rcall LcdLine
ldi ZH,High(2*DebugActiveText)
ldi ZL,Low(2*DebugActiveText)
rjmp LcdDisplZ
;
DebugActiveText:
.db "Debugging activated!",0,0
;
DebugDcfText:
.db "ms hi/lo ",0
;
; Debuggen der empgfangenen DCF77-Informationen
;
DebugDcfRcvd:
ldi ZH,3
ldi ZL,0
rcall LcdPos
ldi XH,High(sDcfRcv)
ldi XL,Low(sDcfRcv)
ldi rmp,6
DebugDcfRcvd1:
push rmp
ld rmp,X+
rcall LcdHex2
ldi rmp,' '
rcall LcdChar
pop rmp
dec rmp
brne DebugDcfRcvd1
ret
;
; Teste Blinken der LCD
Blinktest:
clr XL
rcall LcdBlink
Blinktest1:
ldi rmp,20
DelayA:
rcall LcdWtMax
dec rmp
brne DelayA
rcall LcdFrame ; LCD auf Ausgabemaske
ldi ZH,0
inc XL
mov ZL,XL
rcall LcdPos
rjmp Blinktest1
;
; Debugging Anzeige der Musikausgabe
DebugMusic:
ldi ZH,3 ; Letzte Zeile
clr ZL
rcall LcdPos
ldi rmp,'a'
rcall LcdChar
mov ZH,YH
mov ZL,YL
rcall LcdHex4
ldi rmp,' '
rcall LcdChar
ldi rmp,'c'
rcall LcdChar
in ZL,OCR1AL
in ZH,OCR1AH
rcall LcdHex4
ldi rmp,' '
rcall LcdChar
ldi rmp,'d'
rcall LcdChar
mov ZH,rDurH
mov ZL,rDurL
rjmp LcdHex4
;
; Debuggen des Statusses von TC1
DebugPlayStat:
ldi ZH,3
clr ZL
rcall LcdPos
in ZL,TIMSK
ldi rmp,'i'
sbrc ZL,OCIE1A
ldi rmp,'I'
rcall LcdChar
in ZL,TCCR1A
ldi rmp,'t'
sbrc ZL,COM1A0
ldi rmp,'T'
sbrc ZL,COM1A1
ldi rmp,'C'
rjmp LcdChar
;
; Zum Datum/Uhrzeit-Debugging
DebugDateTime:
ldi ZH,3
ldi ZL,0
rcall LcdPos
ldi XH,High(sDateTime)
ldi XL,Low(sDateTime)
ldi ZL,7
DebugDateTime1:
ld rmp,X+
rcall LcdHex2
cpi ZL,1
breq DebugDateTime2
ldi rmp,' '
rcall LcdChar
dec ZL
rjmp DebugDateTime1
DebugDateTime2:
ret
;
; Ende des Include-Files
;
©2018 by http://www.avr-asm-tutorial.net