Pfad:
Home =>
AVR-Übersicht =>
Anwendungen =>
Digitaluhr => Sourcecode
(This page in English:
Digitaluhr mit ATmega16 - Quellcode
; *********************************************
; * Digitale AVR Uhr mit einem ATmega16 *
; * (C)2010 by info (at) avr-asm-tutorial.net *
; *********************************************
;
.nolist
.include "m16def.inc"
.list
;
; ===================================================
; D e b u g g i n g P a r a m e t e r
; ===================================================
; dbg = 0: Debugging aus, normale Programmausfuehrung
; dbg = 1: Erstes Display an
; dbg = 2: Zweites Display an
; dbg = 3: Drittes Display an
; dbg = 4: Viertes Display an
; dbg = 5: Sekundenanzeige auf der Minutenposition der Weckzeit
; dbg = 6: Zeige die ADC-Werte in Hex auf dem Weckzeit-Display an
;
.equ dbg = 0
;
; ===================================================
; H a r d w a r e
; ===================================================
; Processortyp: ATmega16
; _________
; / |
; Taste rt --|PB0 ADC0|-- Potentiometereingang
; Taste sw --|PB1 ADC1|-- Fototransistoreingang
; Taste ws --|PB2 |--
; Lautspr. --|OC0 |--
; --| PA4|-- 1 Digit Anodentreiber
; ISP MOSI --|MOSI PA5|-- 2
; MISO --|MISO PA6|-- 3
; SCK --|SCK PA7|-- 4
; RESET --|RESET AREF|-- +5V
; VCC --|VCC GND|-- GND
; GND --|GND AVCC|-- +5V
; Quarz 2 --|XTAL2 PC7|-- Grosser LED Doppelpunkt
; Quarz 1 --|XTAL1 PC6|-- g Grosse
; Kleine a --|PD0 PC5|-- f Sieben-
; Sieben b --|PD1 PC4|-- e Segment-
; Segm. c --|PD2 PC3|-- d Anzeige
; Anzeiged --|PD3 PC2|-- c Kathoden
; Kath. e --|PD4 PC1|-- b
; f --|PD5 PC0|-- a
; g --|PD6 PD7|-- Kleiner LED Doppelpunkt
; |___________
;
; ===================================================
; B e s c h r e i b u n g d e r F u n k t i o n
; ===================================================
;
; a) Anzeigen und Anzeigenmodi
; Die obere, grosse Siebensegmentanzeige zeigt die Zeit
; an, die untere kleine zeigt die Weckzeit an.
; Der grosse Doppelpunkt in der oberen Anzeige blinkt
; in Sekundenintervallen. Der kleinere Doppelpunkt
; zeigt an, ob der Weckalarm aktiviert ist. Sind diese
; LEED dauernd an, ist der Weckalarm ausgeschaltet.
; b) Anzeigenmultiplex
; Die Anzeige verwendet Zaehler/Zeitgeber 1 im CTC-
; Modus, um alle 5 ms eine Unterbrechung auszuloesen.
; Die vier Anzeigen werden alle 20 ms erneuert, was
; zu einer Multiplexfrequenz von 50 Hz fuehrt.
; Die Anzeige wird zuerst ausgeschaltet, der Zeiger
; auf die angezeigte Ziffer wird erhoeht und das
; Byte an der Ziffernposition wird an Port C (grosse
; Zeitanzeige) bzw. D (kleine Weckalarmanzeige) kopiert.
; Dann wird das Portbit fuer den richtigen Anodentreiber
; im oberen Nibble von Port A aktiviert (=0).
; c) Anzeige dimmen
; Das Abblenden der Anzeige verwendet den Zaehler/Zeitgeber
; 1 im CTC-Modus und die Unterbrechung bei Compare Match B
; und schaltet die Anodentreiber alle fruehzeitig aus. Der
; Wert fuer den Compare Match B wird aus dem ADC-Wert des
; Fototransistors ermittelt: je weniger Licht am Foto-
; transistor ankommt, desto hoeher ist seine Kollektor-
; spannung, desto groesser ist der ADC-Wert und der
; Wert von Compare Match B. Beachte, dass dies nicht-linear
; ist (es gibt nur zwei Helligkeitsstufen).
; d) Zeitbestimmung
; Die Zeit wird vom Zaehler/Zeitgeber 1 und der Compare
; Match A Unterbrechung gesteuert. Die Unterbrechung
; zaehlt einen Zaehler von 200 auf Null abwaerts. Wenn
; Null erreicht wird, wird eine Flagge gesetzt und der
; 5-ms-Zaehler neu gestartet.
; Ausserhalb der Unterbrechungsroutine wird die Zeit
; um eine Sekunde erhoeht, beim Erreichen von 60 wird
; die Zeit um eine Minute erhoeht. Wenn das Wecken akti-
; viert ist, wird die Zeit mit der Weckzeit verglichen
; und bei Gleichheit die Alarmierung aktiviert.
; e) Tasten
; Die Tasten werden im Anschluss an jede Unterbrechung
; gelesen. Eine gedrueckte Taste wird nach drei
; Unterbrechungen erkannt und gespeichert. Die entsprechende
; Aktion wird ausgefuehrt wenn fuer mindestens 15 ms keine
; der Tasten mehr gedrueckt ist.
; Die folgende Tabelle zeigt die ausgefuehrten Aktionen in
; den verschiedenen Betriebszustaenden:
; Modus Taste Aktion
; ----------------------------------------------------------
; Normal Schwarz Ein-/Ausschalten Wecken
; Rot Beginne Einstellung der Zeit
; Weiss Beginne Einstellung der Weckzeit
; Zeiteinstellung Schwarz Beende Zeiteinstellung vorzeitig
; Rot Stelle Zeit (Stunden oder Minuten)
; Weckeinstellung Schwarz Beende Weckzeiteinstellung vorzeitig
; Weiss Stelle Weckzeit (Stunden oder Minuten)
; Alarmierung an Schwarz Stelle Alarmierung aus
; Rot Stelle Alarmzeit um 5 Minuten spaeter
; Alarmiert Schwarz Alarm aus und Weckzeit auf Original
; Rot Alarm aus und Weckzeit um 5 Minuten spaeter
; f) AD-Wandlung
; Der AD-Wandler laeuft mit einem Vorteiler von 128, misst zwei
; Kanaele und wird mittels Unterbrechung bedient.
; Die Analogspannungen am Potentiometer (Kanal ADC0) und am
; Kollektor des Fototransistors (Kanal ADC1) werden gemessen,
; die oberen acht Byte des linksbuendigen Ergebnisses eingelesen
; und zu einer 16-Bit-Summe addiert. Wenn 256 Messungen pro
; Kanal addiert sind, werden die oberen 8 Bit der Summe an
; eine Routine ausserhalb der Unterbrechungsroutine uebergeben.
; Dies wird verwendet um bei
; Kanal 0: falls Zeit- oder Weckzeiteinstellung aktiv sind,
; wird das Ergebnis in Stunden bzw. Minuten umgerechnet und
; auf der jeweiligen Stunden-/Minutenposition ausgegeben.
; Kanal 1: in laengeren Abstaenden wird daraus der Compare
; Match B Wert zu bestimmen und zu setzen (Dim-Funktion).
; Messfrequenz: f = 2457600 / 128 / 13 / 256 / 2 = 2,88 Hz
; (beide Kanaele)
; g) Wecken ueber Melodie
; Der Zaehler/Zeitgeber 0 wird als programmierbarer Tongenerator
; betrieben, so dass er im CTC-Modus bei aktiviertem Ausgabeport
; (Umpolung bei Compare Match) Melodien spielen kann. Der TC0
; wird mit einem Vorteiler von 8 betrieben, so dass NF zwischen
; 600 (OCR0=255) und 9600 (OCR=16) Hz erzeugt werden kann.
; Wenn der oberste CTC-Wert erreicht wird, wird die naechste Note
; und die naechste Spieldauer des Tons aus dem EEPROM gelesen.
; Ist der letzte Tabellenwert gespielt, wird TC0 stillgelegt
; und der OC0-Ausgang abgeschaltet.
;
; ===================================================
; K o n s t a n t e n
; ===================================================
;
.equ clock = 2457600 ; Quarzfrequenz
.equ cSnooze = 5 ; Zeit in Minuten fuer Schlummern
.equ cPauseLong = $4000 ; lange Pause fuer Melodiewiederholung
;
; ===================================================
; R e g i s t e r
; ===================================================
;
; verwendet mit LPM fuer Lesen aus dem Flash und fuer Berechnungen
; verwendet fuer Berechnungen
; frei R2..R8
.def rFChk = R9 ; Zaehler fuer Dimm-Funktion
.def rAdcC = R10 ; ADC Zaehler
.def rAdcFL = R11 ; LSB ADC-Summe Fototransistor
.def rAdcFH = R12 ; dto., MSB
.def rAdcPL = R13 ; LSB ADC-Summe Potentiometer
.def rAdcPH = R14 ; dto., MSB
.def rSreg = R15 ; SREG temporaer innerhalb Unterbrechung
.def rmp = R16 ; Vielzweckregister ausserhalb Unterbrechungen
.def rimp = R17 ; Vielzweckregister innerhalb Unterbrechungen
.def rFlag = R18 ; Flaggenregister
.equ bArmed = 0 ; Alarm ist aktiviert
.equ bAlarm = 1 ; Alarm ist ausgeloest
.equ bSetC = 2 ; Stelle Uhrzeit
.equ bSetCm = 3 ; Stelle Uhrzeit Minuten
.equ bSetA = 4 ; Stelle Weckzeit
.equ bSetAm = 5 ; Stelle Weckzeit Minuten
.equ bSec = 6 ; Naechste Sekunde erreicht
.equ bAdc = 7 ; Neues ADC-Ergebnis fertig
.def rC5ms = R19 ; 5 ms Zaehler fuer Sekunde
.def rDCnt = R20 ; Anzeige Anodentreiber
.def rKey = R21 ; Tastenspeicher letzte aktive Taste
.def rEep = R23 ; EEPROM-Leseadresse
.def rDurL = R24 ; LSB Zaehler fuer Dauer Toene
.def rDurH = R25 ; dto., MSB
; R27:R26 verwendet als Zeiger ausserhalb Unterbrechung
; R29:R28 verwendet als Zeiger fuer Anzeigenmultiplex innerhalb Unterbrechung
; R31:R30 verwendet als Zeiger ausserhalb Unterbrechung
;
; ===================================================
; S R A M A d r e s s e n
; ===================================================
;
.DSEG
.ORG Sram_Start
sTime:
.byte 4 ; vier Ziffern fuer die grosse Anzeige
sAlarm:
.byte 4 ; vier Ziffern fuer die kleine Anzeige
sCs:
.byte 1 ; Uhrzeit Sekunden
sCm:
.byte 1 ; Uhrzeit Minuten
sCh:
.byte 1 ; Uhrzeit Stunden
sSm:
.byte 1 ; Eingestellte Weckzeit Minuten
sSh:
.byte 1 ; Eingestellte Weckzeit Stunden
sAm:
.byte 1 ; Aktuelle Weckzeit Minuten
sAh:
.byte 1 ; Aktuelle Weckzeit Stunden
sAdcP:
.byte 1 ; Adc Ergebnis Potentiometer
sAdcF:
.byte 1 ; Adc Ergebnis Fototransistor
sKey:
.byte 1 ; gedrueckte Taste
sKeyC:
.byte 1 ; Zaehler fuer Tastendruecke
sKeyS:
.byte 1 ; letzte aktive Taste
;
; ===================================================
; R e s e t u n d I n t - V e k t o r e n
; ===================================================
;
.cseg
.org $0000
rjmp start ; Reset Vektor
nop
reti ; INT0
nop
reti ; INT1
nop
reti ; TC2COMP
nop
reti ; TC2OVF
nop
rjmp TC1Capt ; TC1CAPT
nop
rjmp TC1CompA ; TC1COMPA
nop
rjmp TC1CompB ; TC1COMPB
nop
reti ; TC1OVF
nop
reti ; TC0OVF
nop
reti ; SPI, STC
nop
reti ; USART RXC
nop
reti ; USART UDRE
nop
reti ; USART TXC
nop
rjmp AdcInt ; ADC
nop
reti ; EERDY
nop
reti ; ANACOMP
nop
reti ; TWI
nop
reti ; INT2
nop
rjmp Tc0Comp ; TC0COMP
nop
reti ; SPM RDY
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
; ===================================================
;
; TC1: ICR Periodenende, zeige naechste Ziffer an
;
TC1Capt:
in rsreg,SREG ; rette SREG
ldi rimp,0xF0 ; Zifferntreiber aus
out PORTA,rimp
in rKey,PINB ; lese Tasten
ori rDCnt,0x08 ; Setze Bit 3 Anodenregister
lsl rDCnt ; Schiebe Anodenregister links
brcc TC1Capt1 ; Wenn 0, dann Zyklusende
adiw YL,1 ; naechste Ziffer
ld rimp,Y ; Lese naechste Ziffer Uhrzeit
out PORTC,rimp ; Schreibe auf Port
ldd rimp,Y+4 ; Lese naechste Ziffer Weckzeit
out PORTD,rimp ; Schreibe auf Port
out PORTA,rDCnt ; Setze aktiven Anodentreiber
out SREG,rsreg ; stelle SREG wieder her
reti
TC1Capt1:
ldi YH,HIGH(sTime) ; Neustart Zeiger auf Ziffer
ldi YL,LOW(sTime)
ld rimp,Y ; Lese erste Ziffer Uhrzeit
out PORTC,rimp ; Schreibe auf Port
ldd rimp,Y+4 ; Lese erste Ziffer Weckzeit
out PORTD,rimp ; Schreibe auf Port
ldi rDCnt,0xE0 ; Starte Anodentreiberport mit Bit 4 = 0
out PORTA,rDCnt ; an Anodentreiber
out SREG,rsreg ; stelle SREG wieder her
reti
;
; TC1 Comp A erreicht
;
TC1CompA:
in rsreg,SREG ; Rette SREG
dec rC5ms ; Zaehle 5-ms-Zaehler herunter
brne TC1CompA1 ; Nicht Null
sbr rFlag,1<<bSec ; Setze Sekundenflagge
ldi rC5ms,200 ; Neustart
TC1CompA1:
out SREG,rSreg ; stelle SREG wieder her
reti
;
; TC1 Comp B erreicht
;
TC1CompB:
ldi rimp,0xF0 ; Schalte Anodentreiber aus
out PORTA,rimp
reti
;
; ADC Ready Unterbrechung
;
AdcInt:
in rsreg,SREG ; Rette SREG
in rimp,ADMUX ; Lese gemessenen Kanal
sbrc rimp,MUX0 ; Ueberspringe bei Kanal ADC0
rjmp AdcInt1
in rimp,ADCH ; Lese MSB ADC Ergebnis
add rAdcPL,rimp ; Addiere zur Summe
ldi rimp,0 ; Addiere Uebertrag zu MSB
adc rAdcPH,rimp
ldi rimp,(1<<ADLAR)|(1<<MUX0) ; Messe Kanal ADC1
out ADMUX,rimp
ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0) ; Start Wandlung
out ADCSRA,rimp
out SREG,rsreg ; Stelle SREG wieder her
reti
AdcInt1:
in rimp,ADCH ; Lese MSB ADC Ergebnis
add rAdcFL,rimp ; Addiere zur Summe
ldi rimp,0 ; Addiere Uebertrag zu MSB
adc rAdcFH,rimp
dec rAdcC ; Erniedrige Zaehler
brne AdcInt2 ; Nicht Null
sts sAdcP,rAdcPH ; Kopiere MSB Potentiometer
sts sAdcF,rAdcFH ; Kopiere MSB Fototransistor
clr rAdcPL ; Leere Summen
clr rAdcPH
clr rAdcFL
clr rAdcFH
sbr rFlag,1<<bAdc ; Setze ADC-Flagge
AdcInt2:
ldi rimp,1<<ADLAR ; Setze Kanal ADC0
out ADMUX,rimp
ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0) ; Starte Wandlung
out ADCSRA,rimp
out SREG,rsreg ; Stelle SREG wieder her
reti
;
; TC0 Compare Match Unterbrechung
;
Tc0Comp:
in rSreg,SREG ; Rette SREG
tst rDurL ; Restdauer Null?
brne Tc0Comp0 ; Nein
tst rDurH ; MSB Null?
breq Tc0CompR ; Ja, ueberspringe
Tc0Comp0:
sbiw rDurL,1 ; erniedrige Dauerzaehler
brne Tc0CompR ; Nicht Null, weiter
inc rEep ; Erhoehe EEPROM Adresse
mov rimp,rEep ; Kopiere EEPROM Leseadresse
lsl rimp ; Multipliziere mit 2
out EEARL,rimp ; an Leseadress-Port
ldi rimp,0 ; Ueberlauf auf oberes Byte
adc rimp,rimp
out EEARH,rimp
ldi rimp,1<<EERE ; Ermoegliche Lesen EEPROM
out EECR,rimp
in rimp,EEDR ; Lese erstes Byte
tst rimp ; Teste auf Null
brne Tc0Comp01 ; nicht Null
dec rimp
out OCR0,rimp ; Schreibe auf Compare Match Register
ldi rimp,(1<<WGM01)|(1<<COM01)|(1<<CS01) ; Loesche COM Ausgang
out TCCR0,rimp ; Setze TC0-Modus und COM
rjmp Tc0Comp2
Tc0Comp01:
out OCR0,rimp ; Schreibe auf Compare Match Register
ldi rimp,(1<<WGM01)|(1<<COM00)|(1<<CS01) ; COM output torkeln
out TCCR0,rimp ; Setze TC0 Modus und COM
Tc0Comp2:
in rimp,EEARL ; Lese LSB EEPROM-Adresse
inc rimp ; Erhoehe Adresse
out EEARL,rimp ; Setze LSB Adresse
ldi rimp,1<<EERE ; Lesen ermoeglichen
out EECR,rimp ; in Kontrollregister
in rDurH,EEDR ; Lese Tondauer
clr rDurL
tst rDurH ; Test auf Ende der Melodie
brne Tc0Comp3 ; Nein, weiter
ldi rimp,(1<<WGM01)|(1<<COM01)|(1<<CS01) ; Loesche COM Ausgang
out TCCR0,rimp ; Setze TC0-Modus und COM
ldi rDurH,High(cPauseLong) ; Lange Pause
ldi rDurL,Low(cPauseLong)
ldi rEep,0xFF ; Neustart der Melodie nach der Pause
rjmp Tc0CompR
Tc0Comp3:
lsr rDurH ; / 2 auf Dauer
ror rDurL
lsr rDurH ; / 4
ror rDurL
lsr rDurH ; / 8
ror rDurL
lsr rDurH ; / 16
ror rDurL
lsr rDurH ; / 32
ror rDurL
Tc0CompR:
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 i i e r u n g
; ===================================================
;
Start:
; Richte Stapel ein
ldi rmp,HIGH(RAMEND) ; MSB Stapelzeiger
out SPH,rmp
ldi rmp,LOW(RAMEND) ; LSB Stapelzeiger
out SPL,rmp
; Initiiere Ports
ldi rmp,0xFF ; Ports C und D als Ausgaenge
out DDRC,rmp
out DDRD,rmp
ldi rmp,0xF0 ; Port A obere vier Bit Ausgang
out DDRA,rmp
ldi rmp,0xE0 ; Anodentreiber Ziffer 1 an
out PORTA,rmp
ldi rmp,0x00 ; Ports C und D Kathoden aktiv
out PORTC,rmp
out PORTD,rmp
; Debug teste Anzeigen
.if dbg==1
ldi rmp,0xE0
out PORTA,rmp
ex1: rjmp ex1
.endif
.if dbg==2
ldi rmp,0xD0
out PORTA,rmp
ex2: rjmp ex2
.endif
.if dbg==3
ldi rmp,0xB0
out PORTA,rmp
ex3: rjmp ex3
.endif
.if dbg==4
ldi rmp,0x70
out PORTA,rmp
ex4: rjmp ex4
.endif
; Ende Debug Test Anzeigen
ldi rmp,0x08 ; Setze Port B Bits 0..2 als Eingang, 3 als Ausgang
out DDRB,rmp
ldi rmp,0x07 ; Setze Pull-up-Widerstaende auf Tasteneingaenge an
out PORTB,rmp
; Initiiere Anzeigentreiber, alle Anzeigen auf 0
ldi YH,HIGH(sCs) ; Zeige auf Uhrzeit/Weckzeit
ldi YL,LOW(sCs)
clr R0
ldi rmp,7 ; alle Zeiten/Weckzeiten auf Null
Start1:
st Y+,R0
dec rmp
brne Start1
rcall UpDateTime ; Zeit anzeigen
rcall UpDateAlarm ; Weckzeit anzeigen
out PORTC,rmp ; Anzeigen auf 8
out PORTD,rmp
ldi rDCnt,0x70 ; Setze letzte Anzeigentreiber
ldi YH,HIGH(sTime+3) ; Zeige auf letzte Anzeigenziffer
ldi YL,LOW(sTime+3)
clr rFlag ; Setze alle Flaggen inaktiv
clr rmp ; Schalte Watchdog aus
out WDTCR,rmp
; Initiiere ADC
ldi rmp,1<<ADLAR ; Linksbuendig, Kanal 0, externe Referenzspannung
out ADMUX,rmp
clr rmp ; Setze Spezial-IO-Register auf Null
out SFIOR,rmp
ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0) ; starte ADC
out ADCSRA,rmp ;
ldi rmp,1 ; Setze Dimm-Funktion aktiv fuer ersten ADC-Zyklus
mov rFChk,rmp
; Initiiere TC 0
clr rDurL ; Setze Tondauer auf Null und blockiere TC0
clr rDurH
ldi rmp,175 ; Setze Vergleichswert auf 175 (880 Hz)
out OCR0,rmp
ldi rmp,(1<<WGM01)|(1<<COM00) ; Stop TC0 und Ausgang
out TCCR0,rmp ; Setze TC0 Modus und COM
; Initiiere TC1 als Uhr und Anzeigentreiber
.equ divider = (5 * clock + 500) / 1000 ; 5 ms Zeitintervall
ldi rmp,HIGH(divider) ; Setze Compare Match Register ICR1
out ICR1H,rmp
ldi rmp,LOW(divider)
out ICR1L,rmp
ldi rmp,HIGH(divider / 2) ; Setze Compare Match A als 5-ms-int
out OCR1AH,rmp
ldi rmp,LOW(divider / 2)
out OCR1AL,rmp
ldi rmp,HIGH(divider - 2) ; Setze Compare Match B als Dimmer
out OCR1BH,rmp
ldi rmp,LOW(divider - 2)
out OCR1BL,rmp
clr rmp ; Loesche Modebits WGM11 and WGM10
out TCCR1A,rmp
ldi rmp,(1<<WGM13)|(1<<WGM12)|(1<<CS10) ; ICR1-CTC, Vorteiler = 1
out TCCR1B,rmp
; Ermoegliche Timer-Unterbrechungen
ldi rmp,(1<<OCIE1A)|(1<<OCIE1B)|(1<<TICIE1)|(1<<OCIE0) ; ICR1-,COMP1-,TC0 int enable
out TIMSK,rmp
; Setze Schlafmodus
ldi rmp,1<<SE ; Schlafen ermoeglichen und Modus 0
out MCUCR,rmp
; Ermoegliche Unterbrechungen
sei
; ===================================================
; H a u p t p r o g r a m m s c h l e i f e
; ===================================================
Loop:
sleep ; schlafen
nop ; Nach Aufwachen
;
rcall keys ; Pruefe Tasten nach jedem Aufwachen
; Pruefe Flaggen
sbrc rFlag,bSec ; Sekundenflagge nicht gesetzt
rcall Second
sbrc rFlag,bAdc ; Neues ADC-Ergebnis nicht gesetzt
rcall AdcNew
rjmp Loop
;
; ===================================================
; T a s t e n a u s w e r t u n g
; ===================================================
;
.equ cKeyRed = 0x06
.equ cKeyBlack = 0x05
.equ cKeyWhite = 0x03
keys:
andi rKey,0x07 ; Isoliere Tasten
lds rmp,sKey ; Lese gespeicherten letzten Wert
cp rmp,rKey ; Vergleiche mit aktuellem Wert
breq key1 ; Gleiche Taste
; Taste ungleich letzte Taste
sts sKey,rKey ; Speichere neue Taste
ldi rmp,0 ; Loesche Zaehler
sts sKeyC,rmp
ret
key1: ; Taste gleich der letzten Taste
lds rmp,sKeyC ; Lese Tastenzaehler
inc rmp ; Erhoehe Tastenzaehler
sts sKeyC,rmp ; Speichern
cpi rmp,3 ; Drei identische Tastenereignisse?
breq key2 ; Drei
brcs key1a ; Weniger als drei
ldi rmp,5 ; Mehr als drei, setze auf 5 zum Blockieren
sts sKeyC,rmp
key1a:
ret
key2: ; Taste drei mal gleich
lds rmp,sKey ; Lese Tastencode
cpi rmp,0x07 ; Keine Taste gedrueckt?
breq key3 ; Ja
sts sKeyS,rmp ; Speichere diese Taste
ret
key3:
lds rmp,sKeyS ; Lese gespeicherte Taste
ldi ZL,0 ; Schreibe Null in Tastenspeicher
sts sKeyS,ZL
tst rmp ; Gespeicherte Taste Null?
brne key4 ; Nein, werte aus
ret
key4: ; Bedingte Spruenge bei verschiedenen Flaggen
sbrc rFlag,bAlarm ; Ist gerade Alarm?
rjmp keyAlarm ; Tastenauswertung bei aktivem Alarm
sbrc rFlag,bArmed ; Alarm aktiviert?
rjmp keyArmed ; Tastenauswertung bei gesetztem Alarm
sbrc rFlag,bSetC ; Uhrzeiteinstellung aktiv?
rjmp keySetC ; Tastenauswertung waehrend Uhrzeiteinstellung
sbrc rFlag,bSetA ; Weckzeiteinstellung aktiv?
rjmp keySetA ; Tastenauswertung waehrend Weckzeiteinstellung
; Normale Tastenauswertung
cpi rmp,cKeyRed ; Rote Taste?
brne key5 ; Nein
sbr rFlag,1<<bSetC ; Setze Flagge Uhrzeiteinstellung
ret
key5:
cpi rmp,cKeyWhite ; Weisse Taste?
brne key6 ; Nein
sbr rFlag,1<<bSetA ; Setze Flagge Weckzeiteinstellung
ret
key6:
cpi rmp,cKeyBlack ; Schwarze Taste?
brne key7
ldi rmp,1<<bArmed ; Torkele Alarm
eor rFlag,rmp
ret
key7: ; Alle Tasten ausgewertet
ret
keySetC: ; Auswertung waehrend Uhrzeiteinstellung
cpi rmp,cKeyBlack ; Schwarze Taste?
brne keySetC1 ; Nein
cbr rFlag,(1<<bSetC)|(1<<bSetCM) ; Abbruch Zeiteinstellung, loesche Flaggen
rjmp UpDateTime ; zeige Uhrzeit an
keySetC1:
cpi rmp,cKeyRed ; Rote Taste?
brne keySetC3 ; Nein
sbrs rFlag,bSetCm ; Minutenflagge gesetzt
rjmp keySetC2 ; Nein, setze Stunden
; Setze Minuten
clr rmp ; Loesche Sekunden
sts sCs,rmp
lds rmp,sAdcP ; Lese Potentiometerwert
ldi ZL,60 ; mal 60
mul rmp,ZL
sts sCm,R1 ; setze Minuten
cbr rFlag,(1<<bSetC)|(1<<bSetCm) ; Loesche Flaggen
rjmp UpDateTime ; zeige Uhrzeit an
keySetC2: ; Setze Stunden
lds rmp,sAdcP ; lese Potentiometerwert
ldi ZL,24 ; mal 24
mul rmp,ZL
sts sCh,R1 ; speichere Stunden
clr rmp ; Loesche Sekunden
sts sCs,rmp
sbr rFlag,(1<<bSetC)|(1<<bSetCm) ; Setze Minuten-Einstellflagge
ret
keySetC3: ; illegale Taste bei Zeiteinstellung
ret
keySetA: ; Auswertung waehrend Weckzeiteinstellung
cpi rmp,cKeyBlack ; Schwarze Taste?
brne keySetA1 ; Nein
cbr rFlag,(1<<bSetA)|(1<<bSetAm) ; Vorzeitiger Abbruch, loesche Flaggen
rjmp UpDateAlarm ; zeige Alarmzeit an
keySetA1:
cpi rmp,cKeyWhite ; Weisse Taste?
brne keySetA3 ; Nein
; Weisse Taste gedrueckt
sbrs rFlag,bSetAm ; Setze Weckzeit Minuten?
rjmp keySetA2 ; Nein, Setze Stunden
; Setze Weckzeit Minuten
lds rmp,sAdcP ; Lese Potentiometerwert
ldi ZL,60 ; mal 60
mul ZL,rmp
sts sAm,R1 ; Setze Weckzeit Minute
sts sSm,R1 ; Setze Snooze-Weckzeit auf gleichen Wert
cbr rFlag,(1<<bSetA)|(1<<bSetAm) ; Loesche Flaggen
sbr rFlag,1<<bArmed ; Setze Wecken-Flagge
rjmp UpDateAlarm ; zeige Weckzeit an
keySetA2: ; Setze Weckzeit Stunden
lds rmp,sAdcP ; Lese Potentiometerwert
ldi ZL,24 ; mal 24
mul ZL,rmp
sts sAh,R1 ; Setze Weckzeit Stunde
sts sSh,R1 ; Setze Snooze-Weckzeit auf gleichen Wert
sbr rFlag,1<<bSetAm ; Setze Minutenflagge
ret
keySetA3: ; keine weitere Taste waehrend Weckzeiteinstellung
ret
keyAlarm: ; Tasten bei Alarm
cpi rmp,cKeyRed ; Rote Taste?
brne keyAlarm1 ; Nein
; Rote Taste bei Alarm: Snooze-Funktion
keyAlarmSnooze:
lds rmp,sAm ; Lese Snooze-Weckzeit Minuten
subi rmp,-cSnooze ; Addiere Snooze-Zeit
sts sAm,rmp ; Und speichere
cpi rmp,60 ; Groesser oder gleich 60 Minuten?
brcs keyAlarmOff ; Nein, weiter
subi rmp,60 ; Subtrahiere 60 Minuten
sts sAm,rmp ; Und speichere
lds rmp,sAh ; Lese Snooze-Stunden
inc rmp ; Naechste Stunde
sts sAh,rmp ; Und speichern
cpi rmp,24 ; Groesser oder gleich 24?
brcs keyAlarmOff ; Nein, weiter
clr rmp ; Neustart Snnoze-Stunden
sts sAh,rmp ; Und Speichern
keyAlarmOff:
cbr rFlag,1<<bAlarm ; Loesche Alarmflagge
ldi rmp,(1<<WGM01)|(1<<COM00) ; Beende Alarm
out TCCR0,rmp ; Setze TC1 Modus und COM
cbi PORTB,3 ; Loesche Lautsprecherportbit
rjmp UpDateAlarm ; Zeige neue Weckzeit an
keyAlarm1: ; Schwarze Taste bei Alarm?
cpi rmp,cKeyBlack ; Schwarze Taste?
brne keyAlarm2 ; Nein
; Schwarze Taste bei Alarm: Setze Alarm und Alarmierung aus
lds rmp,sSm ; Lese Weckzeit Minuten Original
sts sAm,rmp ; Schreibe Snnoze Weckzeit Minuten
lds rmp,sSh ; Lese Weckzeit Stunden Original
sts sAh,rmp ; Schreibe Snooze Weckzeit Stunden
cbr rFlag,1<<bArmed ; Loesche Weckflagge
rjmp keyAlarmOff ; schalte Alarm aus
keyAlarm2:
; Alle anderen Tasten illegal
ret
keyArmed: ; Tastenauswertung bei gesetzter Weckflagge
cpi rmp,cKeyBlack ; Schwarze Taste?
brne keyArmed1 ; Nein
; Schwarze Taste be gesetzter Weckflagge
cbr rFlag,1<<bArmed ; Setze Weckflagge auf Aus
lds rmp,sSm ; Lese Weckzeit Original Minuten
sts sAm,rmp ; Schreibe in Snooze-Weckzeit Minuten
lds rmp,sSh ; Lese Weckzeit Original Minuten
sts sAh,rmp ; Schreibe in Snooze-Weckzeit Stunden
rjmp UpDateAlarm ; Zeige Weckzeit an
keyArmed1:
cpi rmp,cKeyRed ; Rote Taste?
brne keyArmed2 ; Nein
; Rote Taste bei gesetzter Weckflagge: erhoehe Snooze-Zeit
rjmp keyAlarmSnooze ; erhoehe Snooze Zeit
keyArmed2:
; ignoriere andere Taste
ret
;
; ===================================================
; E i n e S e k u n d e i s t v o r b e i
; ===================================================
;
Second:
cbr rFlag,1<<bSec ; Loesche Sekundenflagge
lds rmp,sCs ; Lese Sekunden
inc rmp ; Naechste Sekunde
sts sCs,rmp ; Speichern
; Debug Sekunden anzeigen
.if dbg == 5
rcall dispsec
.endif
; Ende Debug
cpi rmp,60 ; Naechste Minute?
brcs Second1 ; Nein
clr rmp ; Loesche Sekunden
sts sCs,rmp ; Speichern
lds rmp,sCm ; Lese Minuten
inc rmp ; Naechste Minute
sts sCm,rmp ; Speichern
cpi rmp,60 ; Naechste Stunde?
brne UpdateTime ; Nein, Zeit anzeigen
clr rmp ; Loesche Minuten
sts sCm,rmp ; Speichern
lds rmp,sCh ; Lese Stunden
inc rmp ; Naechste Stunde
sts sCh,rmp ; Speichern
cpi rmp,24 ; Naechster Tag?
brne UpdateTime ; nein, Zeit anzeigen
clr rmp ; Loesche Stunden
sts sCh,rmp ; Speichern
rjmp UpdateTime ; Zeit anzeigen
Second1: ; Blinken Doppelpunkt Zeitanzeige
sbrc rFlag,bSetC ; Zeiteinstellung aktiv?
rjmp Second4 ; ja
ror rmp ; Niedrigstes Bit ins Carry
lds rmp,sTime ; Ersten Anzeigenspeicher lesen
brcs Second2 ; Niedrigstes Bit = 1?
sbr rmp,1<<7 ; Nein, loesche LED-Bit
rjmp Second3 ; weiter
Second2:
cbr rmp,1<<7 ; Setze LED-Bit
Second3:
sts sTime,rmp ; In erste Anzeigenposition
; Blinken des Weckzeit-Doppelpunkts
Second4:
sbrs rFlag,bArmed ; Wenn Wecken aktiv
ret ; nein
lsl rmp ; Bit 7 der Sekunde in das Carry
lds rmp,sAlarm ; Lese Weckzeit Anzeigenspeicher 1
brcs Second5 ; Bit 7 ist Eins
andi rmp,0x7F ; Bit 7 ist 0, loesche Bit 7 Anzeige
rjmp Second6 ; weiter
Second5:
sbr rmp,1<<7 ; Setze Bit 7 in der Anzeige
Second6:
sts sAlarm,rmp ; Schreibe Anzeigenspeicher 1
ret
; Debug: zeige Sekunden im Display an
dispsec:
lds rmp,sCs ; Lese Sekunden
ldi XH,High(sAlarm+2) ; Zeige auf Weckzeit-Minuten
ldi XL,Low(sAlarm+2)
rcall To7Seg ; wandle rmp in Siebensegment
lds rmp,sCs ; Lese Minuten in rmp
ret
; Ende Debug
;
; ===================================================
; A n z e i g e a k t u a l i s i e r e n
; ===================================================
;
; Anzeigen der Weckzeit
;
UpDateAlarm:
ldi XH,High(sAlarm) ; Zeige auf Weckzeit-Stunden
ldi XL,Low(sAlarm)
lds rmp,sAh ; Lese Weckzeit Stunden
rcall To7Seg ; Wandle rmp in Siebensegment
lds rmp,sAm ; Lese Weckzeit Minuten
rjmp To7Seg ; Wandle rmp in Siebensegment
;
; Anzeigen der Uhrzeit
;
UpdateTime:
sbrc rFlag,bArmed ; Wenn Wecken nicht aktiviert, springe
rcall CheckAlarm ; Vergleiche Weckzeit und gib Alarm
sbrc rFlag,bSetC ; Wenn Uhrzeiteinstellung inaktiv, springe
ret
ldi XH,High(sTime) ; Zeige auf Anzeige Zeit
ldi XL,LOW(sTime)
lds rmp,sCh ; Lese Zeit Stunde
rcall To7Seg ; Wandle rmp in Siebensegment
lds rmp,sCm ; Lese Zeit Minuten
To7Seg: ; Wandle rmp in Siebensegment und schreibe in SRAM
clr R0 ; R0 ist Zaehler
To7Seg1:
subi rmp,10 ; 10 abziehen
brcs To7Seg2 ; wenn Unterlauf, fertig
inc R0 ; noch nicht, ziehe weiter 10 ab
rjmp To7Seg1 ; weiter
To7Seg2:
subi rmp,-10 ; addiere wieder 10 wegen Unterlauf
ldi ZH,HIGH(2*Tab7Seg) ; Lade Siebensegment-Tabelle
ldi ZL,LOW(2*Tab7Seg)
add ZL,R0 ; addiere Zehner
brcc To7Seg3 ; Kein Ueberlauf
inc ZH ; Ueberlauf in Tabelle
To7Seg3:
lpm ; Lese Siebensegment-Code aus Tabelle
st X+,R0 ; Speichere in Zeigerposition und erhoehe Zeiger
ldi ZH,HIGH(2*Tab7Seg) ; Lade Siebensegmenttabelle
ldi ZL,LOW(2*Tab7Seg)
add ZL,rmp ; Addiere Einerrest
brcc To7Seg4 ; kein Ueberlauf
inc ZH ; Ueberlauf in Tabelle
To7Seg4:
lpm ; Lese Siebensegmentcode aus Tabelle
st X+,R0 ; Speichere in Zeigerposition und erhoehe Zeiger
ret
; Umwandlungstabelle Dezimal/Hex in Siebensegment
Tab7Seg:
.db 0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10
; Hex Werte
.db 0x08,0x03,0x27,0x21,0x06,0x0E
;
; ===================================================
; W e c k z e i t e r r e i c h t ?
; ===================================================
;
CheckAlarm:
lds R0,sCh ; Lese Uhrzeit Stunden
lds rmp,sAh ; Lese Weckzeit Stunden
cp R0,rmp ; Gleich?
brne CheckAlarmRet ; Nein
lds R0,sCm ; Lese Uhrzeit Minuten
lds rmp,sAm ; Lese Weckzeit Minuten
cp R0,rmp ; Gleich?
brne CheckAlarmRet ; Nein
sbr rFlag,1<<bAlarm ; Setze Alarmflagge
ser rEep ; Adresse Melodie auf Ende
ldi rDurL,1 ; Melodiestart bei naechstem Interruptzyklus
clr rDurH
ldi rmp,(1<<WGM01)|(1<<COM01)|(1<<CS01) ; Starte TC0
out TCCR0,rmp ; Setze TC0-Modus, COM und Vorteiler = 8
CheckAlarmRet:
ret
;
; ===================================================
; N e u e r A D C - W e r t v o r h a n d e n
; ===================================================
;
AdcNew:
cbr rFlag,1<<bAdc ; ADC Flagge loeschen
; Debug ADC-Wert in Anzeige anzeigen
.if dbg == 6
rcall dispadc
.endif
; Ende Debug
dec rFChk ; Zaehler fuer Dimmwert auf Null?
brne AdcNew2 ; Nein
ldi rmp,0x10 ; Setze Zaehler fuer Dimmen neu
mov rFChk,rmp
lds rmp,sAdcF ; Lese Fototransistor-Wert
cpi rmp,0xFF ; Dunkel schalten?
brcs AdcNew1 ; Nein
ldi rmp,HIGH(divider/2) ; Compare Match B auf halbe Zeit
out OCR1BH,rmp
ldi rmp,LOW(divider/2)
out OCR1BL,rmp
rjmp AdcNew2 ; weiter
AdcNew1:
ldi rmp,HIGH(divider-30) ; Compare Match B auf volle Zeit
out OCR1BH,rmp
ldi rmp,LOW(divider-30)
out OCR1BL,rmp
AdcNew2:
lds rmp,sAdcP ; Lese Potentiometer-Wert
sbrs rFlag,bSetC ; Zeiteinstellung aktiv?
rjmp AdcNew4 ; Nein
; Stelle Potentiometer-Wert in Anzeige dar
sbrs rFlag,bSetCm ; Minuteneinstellung aktiv?
rjmp AdcNew3 ; Nein
; Minutendarstellung aus Poti-Wert
ldi XH,High(sTime+2) ; Setze Zeiger auf Zeit Minuten Anzeige
ldi XL,Low(sTime+2)
ldi ZL,60 ; mal 60
rjmp Mult ; Muliplizieren und in Anzeigenspeicher schreiben
AdcNew3: ; Stundendarstellung aus Poti-Wert
ldi XH,High(sTime) ; Setze Zeiger auf Zeit Stunden Anzeige
ldi XL,Low(sTime)
ldi ZL,24 ; mal 24
rjmp Mult ; Muliplizieren und in Anzeigenspeicher schreiben
AdcNew4:
sbrs rFlag,bSetA ; Weckzeiteinstellung aktiv?
ret ; Nein
sbrs rFlag,bSetAm ; Weckzeiteinstellung Minuten?
rjmp AdcNew5 ; Nein
ldi XH,High(sAlarm+2) ; Setze Zeiger Weckzeit Minuten Anzeige
ldi XL,Low(sAlarm+2)
ldi ZL,60 ; mal 60
rjmp Mult ; Muliplizieren und in Anzeigenspeicher schreiben
AdcNew5:
ldi XH,High(sAlarm) ; Setze Zeiger Weckzeit Stunden Anzeige
ldi XL,Low(sAlarm)
ldi ZL,24 ; mal 24
rjmp Mult ; Muliplizieren und in Anzeigenspeicher schreiben
;
; Muliplizieren und in Anzeigenspeicher schreiben
;
Mult:
mul ZL,rmp ; ZL * rmp in R1:R0
mov rmp,R1 ; MSB in rmp kopieren
rjmp To7Seg ; in Siebensegmentcode wandeln und in Zeigerposition
;
; ===================================================
; D e b u g : A D C - W e r t e a n z e i g e n
; ===================================================
;
dispadc:
ldi XH,HIGH(sAlarm) ; Zeiger Position Weckzeit Stunden
ldi XL,LOW(sAlarm)
lds rmp,sAdcP ; Potentiometer-Wert lesen
rcall dispadc1 ; in Hex in Display schreiben
lds rmp,sAdcF ; Fototransistor-Wert lesen
dispadc1:
push rmp ; Wert in rmp auf Stapel
swap rmp ; Oberes und unteres Nibble vertauschen
rcall dispadc2 ; Unteres Nibble in Hex schreiben
pop rmp ; rmp wieder herstellen
dispadc2:
andi rmp,0x0F ; Unteres Nibble isolieren
ldi ZH,HIGH(2*Tab7Seg) ; Zeiger auf Siebensegmenttabelle
ldi ZL,LOW(2*Tab7Seg)
add ZL,rmp ; Unteres Nibble addieren
brcc dispadc3 ; Kein Ueberlauf
inc ZH ; Ueberlauf
dispadc3:
lpm ; Code aus Tabelle lesen
st X+,R0 ; Und in SRAM schreiben
ret
;
; ===================================================
; C o p y r i g h t I n f o r m a t i o n
; ===================================================
;
.db "C(2)10 0ybh tt:p//ww.wva-rsa-mutotirlan.te"
;
; ===================================================
; M e l o d i e i m E E P R O M
; ===================================================
;
.eseg
.org $0000
;
.equ cPa = 10 ; Pausenlaenge
.equ cD = 9216 ; Tondauerkonstante
;
; Frequenztabelle
.equ cf2e = 659
.equ cf2f = 698
.equ cf2g = 784
.equ cf2a = 880
.equ cf2h = 988
.equ cf3c = 1047
.equ cf3d = 1175
.equ cf3e = 1319
.equ cf3f = 1397
.equ cf3g = 1568
.equ cf3a = 1760
.equ cf3h = 1976
.equ cf4c = 2093
.equ cf4d = 2349
.equ cf4e = 2637
.equ cf4f = 2794
.equ cf4g = 3136
.equ cf4a = 3520
.equ cf4h = 3951
.equ cf5c = 4186
; CTC Tabelle fuer diese Frequenzen
.equ cc2e = clock/16/cf2e
.equ cc2f = clock/16/cf2f
.equ cc2g = clock/16/cf2g
.equ cc2a = clock/16/cf2a
.equ cc2h = clock/16/cf2h
.equ cc3c = clock/16/cf3c
.equ cc3d = clock/16/cf3d
.equ cc3e = clock/16/cf3e
.equ cc3f = clock/16/cf3f
.equ cc3g = clock/16/cf3g
.equ cc3a = clock/16/cf3a
.equ cc3h = clock/16/cf3h
.equ cc4c = clock/16/cf4c
.equ cc4d = clock/16/cf4d
.equ cc4e = clock/16/cf4e
.equ cc4f = clock/16/cf4f
.equ cc4g = clock/16/cf4g
.equ cc4a = clock/16/cf4a
.equ cc4h = clock/16/cf4h
.equ cc5c = clock/16/cf5c
; Tondauerkonstanten
.equ cd2e = cd/cc2e
.equ cd2f = cd/cc2f
.equ cd2g = cd/cc2g
.equ cd2a = cd/cc2a
.equ cd2h = cd/cc2h
.equ cd3c = cd/cc3c
.equ cd3d = cd/cc3d
.equ cd3e = cd/cc3e
.equ cd3f = cd/cc3f
.equ cd3g = cd/cc3g
.equ cd3a = cd/cc3a
.equ cd3h = cd/cc3h
.equ cd4c = cd/cc4c
.equ cd4d = cd/cc4d
.equ cd4e = cd/cc4e
.equ cd4f = cd/cc4f
.equ cd4g = cd/cc4g
.equ cd4a = cd/cc4a
.equ cd4h = cd/cc4h
.equ cd5c = cd/cc5c
; Melodie
; 4g 4g 4f 3a
; 4e 4d 4d
; 4c 3h 4c 4f
; 4f 4g-4f
; Melodietabelle
.db cc3g,cd3g,cc3g,cd3g,cc3f,cd3f,cc2a,cd2a
.db cc3e,cd3e,cc3d,cd3d,cc3d,cd3d
.db cc3c,cd3c,cc2h,cd2h,cc3c,cd3c,cc3f,cd3f
.db cc3f,cd3f,cc3g,cd3g,cc3f,cd3f
; Ende der Tabelle
.db 0,0
;
; Ende des Quellcodes
;
©2010 by http://www.avr-asm-tutorial.net