LED Clock R5: Software
Quelltext:
;**********************************************************************
;rtc - Uhrenprogramm für 8051
;
;Funktionen:
; - Auslesen und Schreiben einer RTC
; - Stellen der Uhrzeit über einen Drehencoder
; - Anzeigen der Zeit über eine 7-Segmentanzeige
; - Weckfunktion
; - Speichern der Weckzeit im internen EEprom
;
;
; Copyright (C) 2005 Thomas Elste
;
; This program is free software; you can redistribute it and/or
; modify it under the terms of the GNU General Public License
; as published by the Free Software Foundation; either version 2
; of the License, or (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
;
;**********************************************************************
;Definitionen
#cpu = 89S8252
;Makros
#def movb(x, y) {mov c, (y): mov (x), c}
#def mod(x,y){if a=#(x)h: mov a, #0h : end if:if a=#f9h: mov a, #(y)h : end if}
#def rinc {inc dptr : movx a, @dptr}
#def winc {inc dptr : movx @dptr, a : mov a, wmcon : loop while not bit acc.1 : mov a, wmcon : end loop}
;Konstanten
#const normal_address=71h
#const change_address=61h
#const dp_const=10
#const rtc_counter_const=100
#const wz_eeprom=001h, is_valid=aah
;I/O Mapping
#bit rwr=p2.6, rrd=p2.5, adr=p2.7,
#bit dreh_l=p3.0, dreh_r=p3.1, dreh_tast=p3.2
#bit alarm_led=p3.7, buzzer=p3.5
#bit cs_h10=p0.3, cs_h1=p0.2, cs_m10=p0.1, cs_m1=p0.0
;Variablen
#byte mY10=7ch, SmY10=6ch
#byte mY1=7bh, SmY1=6bh
#byte mMo10=7ah, SmMo10=6ah
#byte mMo1=79h, SmMo1=69h
#byte mD10=78h, SmD10=68h
#byte mD1=77h, SmD1=67h
#byte mW=76h, SmW=66h
#byte mH10=75h, SmH10=65h
#byte mH1=74h, SmH1=64h
#byte mMi10=73h, SmMi10=63h
#byte mMi1=72h, SmMi1=62h
#byte mS10=71h, SmS10=61h
#byte mS1=70h, SmS1=60h
#byte show_ptr
#byte dp_timer
#byte tmp, buzzer_counter, rtc_counter
#bit alt_0=2fh.7, alt_1=2fh.6, neu_0=2fh.5, neu_1=2fh.4
#bit is_hour=2fh.3, stellen=2fh.2
#bit dp_active, time_has_changed
#bit alarm_is_on, alarm_button_pressed, buzzer_is_on
#bit buzzer_skip
#byte WmH10, WmH1, WmMi10, WmMi1
ajmp Initialisierung
; Interruptvektoren
EXTI0: ; externer Interrupt 0
ajmp Externer Interrupt 0
Timer 0: ; Timer 0 Interrupt
ajmp Timer 0 Interrupt
EXTI1: ; externer Interrupt 1
ajmp Externer Interrupt 1
Timer 1: ; Timer 1 Interrupt
ajmp Timer 1 Interrupt
Initialisierung:
; Konfiguration
orl WMCON, # 18h ; Internes EEPROM zum Lesen
; und zum Schreiben verwenden.
orl TMOD, # 11h ; Timer0 und 1 sind 16-Bit Timer
mov th0, #0h
mov tl0, #0h
setb TR0 ; Timer 0 läuft.
clr TR1
setb EX0 ; externen Interrupt 1 freigeben
setb EX1
clr IT0
clr IT1
setb PX0 ; Priorität für externen Interrupt 1
setb EA
setb ET0 ; Timer 0 Interrupt freigeben
setb ET1
;Initialisierung der Variablen
mov show_ptr, #normal_address ;normalen offset für die anzeige laden
mov dp_timer, #dp_const
mov rtc_counter, #rtc_counter_const
mov p1, #00h
setb neu_0 : setb neu_1 : setb alt_1 : setb alt_0
clr rwr : clr rrd : clr adr : clr stellen
clr cs_h10 : clr cs_h1 : clr cs_m10 : clr cs_m1
clr alarm_is_on :clr alarm_led : clr alarm_button_pressed
clr buzzer : clr buzzer_is_on
; Weckzeit aus dem EEprom lesen
mov dptr, #wz_eeprom
movx a, @dptr
if a=#is_valid
else
mov a, #is_valid
movx @dptr, a
mov a, wmcon
loop while not bit acc.1
mov a, wmcon
end loop
mov a, #0
winc
mov a, #0
winc
mov a, #6
winc
mov a, #0
winc
mov a, #0
winc
end if
lcall read_eeprom
; Speicherbereich für die Anzeige während des Stellens
; mit 0f initialisieren
for r0=#6
mov a, #change_address
add a, r0
mov r1, a
mov @r1, #0fh
next
; vorm Start die RTC lesen
lcall read_rtc
;Hauptschleife
main:
;RTC in festen Intervallen abfragen
mov a, rtc_counter
if a=#0
lcall read_rtc
mov a, #rtc_counter_const
else
dec a
end if
mov rtc_counter, a
;for r0=#2h
; lcall warten
;next
; Display schreiben
lcall write_display
;for r0=#2h
lcall warten
;next
; Stellmodus aktiv
if bit stellen
clr tr0 ;Doppelpkt Timer stopp
clr dp_active ;Doppelpkt aus
lcall sub_stellen
setb ea
clr stellen
setb tr0
end if
; Alarm-Button wurde gedrückt
if bit alarm_button_pressed
lcall sub_alarm_button
end if
; ist die Weckfunktion aktiviert muss die Zeit mit der
; Weckzeit verglichen werden
if bit alarm_is_on
lcall check_alarm
end if
; Timer1 aktiviert den Buzzer, wenn der Alarm ausgelöst
;wurde
if bit buzzer_is_on
setb tr1
end if
jmp main
; aktuelle Uhrzeit in die RTC schreiben
write_rtc:
; nur Stunden, Minuten und Sekunden
for r0=#6h
mov a,#efh
add a,r0
anl a, #0fh
mov p2,a ;anlegen der Adresse
setb adr ;Adress latch enable
clr adr ;Adress latch disable
mov p2,#0fh ;bus clear
mov a, #6fh
add a, r0
mov r1, a
mov a, @r1
anl a, #0fh
mov p2, a ;daten anlegen
setb rwr ;daten schreiben
clr rwr ;wr löschen
next
ret
; Uhrzeit aus der RTC lesen
read_rtc:
for r0=#6h
mov a,#efh
add a,r0
anl a, #0fh
mov p2,a ;anlegen der Adresse
setb adr ;Adress latch enable
clr adr ;Adress latch disable
mov a, #6fh
add a, r0
mov r1, a
mov p2, #0fh ;bus clear
setb rrd ;read aktiv
mov a, p2 ;read
clr rrd ;read clear
mov @r1, a
next
ret
; Uhrzeit auf dem Display Ausgeben
write_display:
; für Muster für die 7-Segmentanzeige sind in einer
; Tabelle abgelegt
mov dptr, #7seg_tab
mov a, show_ptr ; Pointer für richtigen Bereich laden
add a, #4
mov r0, a
mov a, @r0
if a=#0fh ;bei 0f soll nichts verändert werden
elseif a=#0dh ; auch bei 0d nicht (Weckzeiteinstellung)
else
anl a, #03h
end if
movc a, @a+dptr
acall add_dp
mov p1, a
setb cs_h10
acall warten
clr cs_h10
mov a, show_ptr
add a, #3
mov r0, a
mov a, @r0
anl a, #0fh
movc a, @a+dptr
acall add_dp
mov p1, a
setb cs_h1
acall warten
clr cs_h1
mov a, show_ptr
add a, #2
mov r0, a
mov a, @r0
anl a, #0fh
movc a, @a+dptr
acall add_dp
mov p1, a
setb cs_m10
acall warten
clr cs_m10
mov a, show_ptr
add a, #1
mov r0, a
mov a, @r0
anl a, #0fh
movc a, @a+dptr
acall add_dp
mov p1, a
setb cs_m1
acall warten
clr cs_m1
ret
; vergleicht die Weckzeit mit der aktuellen Zeit
; und löst bei Übereinstimmung den Alarm aus
check_alarm:
mov a, mH10
anl a, #03h
mov tmp, a
mov a, WmH10
anl a, #03h
if a=tmp
mov a, mH1
anl a, #0fh
mov tmp, a
mov a, WmH1
anl a, #0fh
if a=tmp
mov a, mMi10
anl a, #0fh
mov tmp, a
mov a, WmMi10
anl a, #0fh
if a=tmp
mov a, mMi1
anl a, #0fh
mov tmp, a
mov a, WmMi1
anl a, #0fh
if a=tmp
setb buzzer_is_on
jmp out_check_alarm
end if
end if
end if
end if
clr tr1
clr buzzer_is_on
clr buzzer
out_check_alarm:
ret
warten:
mov r6,#01h
for r6
mov r7,#afh
for r7
next
next
ret
; wertet den Alarmbutton aus
sub_alarm_button:
loop while not bit p3.3
end loop
;wenn der Buzzer an ist, abschalten
if bit buzzer_is_on
clr tr1
clr buzzer_is_on
clr buzzer
; jmp out_sub_alarm_button
end if
; Weckfunktion toggeln
if not bit alarm_is_on
setb alarm_is_on
setb alarm_led
else
clr alarm_is_on
clr alarm_led
end if
; Alarmstatus sichern
lcall save_astat
out_sub_alarm_button:
clr alarm_button_pressed
reti
; Stellroutine
sub_stellen:
loop while not bit dreh_tast ;prellschutz
end loop
clr time_has_changed
mov show_ptr, #change_address ;zeiger für die anzeigeroutine
;auf neuen Ram bereich setzen
;stunden
setb is_hour ;bit zeigt sonderbehandlung für stunden an
mov r1, #mH10
mov r0, #mH1
lcall merge
mov r2, a
loop while bit dreh_tast ;solange taste nicht erneut
;gedrückt wurde
mov a, r2
mod(24,23) ;unter und überlaufstest
mov r2, a ;rücksichern
;(kann sich ja was geändert haben)
mov r1, #SmH10
mov r0, #SmH1
lcall split ;in den ram schreiben
;(extra bereich für stellroutine)
lcall write_display ;anzeige sub aufrufen
lcall dreh_coder ;drehcoder sub aufrufen
end loop
loop while not bit dreh_tast ;prellschutz
end loop
mov a, r2
mov r1, #mH10
mov r0, #mH1
lcall split ;in den eigentlichen RAM-Bereich
;zurückschreiben
mov SmH10, #0fh
mov SmH1, #0fh
clr is_hour ;stunden bit wieder löschen
;stunden ende
;minuten
mov r1, #mMi10
mov r0, #mMi1
lcall merge
mov r2, a
loop while bit dreh_tast
mov a, r2
mod(60,59)
mov r2, a
mov r1, #SmMi10
mov r0, #SmMi1
lcall split
lcall write_display
lcall dreh_coder
end loop
loop while not bit dreh_tast
end loop
mov a, r2
mov r1, #mMi10
mov r0, #mMi1
lcall split
mov SmMi10, #0fh
mov SmMi1, #0fh
;minuten ende
; todo: checken ob überhaupt was verändert
; nur dann in die RTC schreiben
if bit time_has_changed
mov mS10, #f0h
mov mS1, #f0h
lcall write_rtc ;neuen werte in die rtc schreiben
end if
clr time_has_changed
;Weckzeit stunden
setb is_hour ;bit zeigt sonderbehandlung für stunden an
mov r1, #WmH10
mov r0, #WmH1
; Anzeige der Weckzeitkennung
mov SmMi10, #0eh
mov SmMi1, #0eh
lcall merge
mov r2, a
loop while bit dreh_tast
mov a, r2
mod(24,23)
mov r2, a
mov r1, #SmH10
mov r0, #SmH1
lcall split
lcall write_display
lcall dreh_coder
end loop
loop while not bit dreh_tast
end loop
mov a, r2
mov r1, #WmH10
mov r0, #WmH1
lcall split
mov SmH10, #0fh
mov SmH1, #0fh
clr is_hour
;Weckzeit stunden ende
;Weckzeit minuten
mov r1, #WmMi10
mov r0, #WmMi1
mov SmH10, #0dh
mov SMH1, #0dh
lcall merge
mov r2, a
loop while bit dreh_tast
mov a, r2
mod(60,59)
mov r2, a
mov r1, #SmMi10
mov r0, #SmMi1
lcall split
lcall write_display
lcall dreh_coder
end loop
loop while not bit dreh_tast
end loop
mov a, r2
mov r1, #WmMi10
mov r0, #WmMi1
lcall split
mov SmMi10, #0fh
mov SmMi1, #0fh
mov SmH10, #0fh
mov SMH1, #0fh
;minuten ende
; wenn sich die Weckzeit geändert hat
; dann in EEprom sichern
if bit time_has_changed
lcall write_eeprom
end if
mov show_ptr, #normal_address ;zeiger für anzeige zurück setzen
lcall warten
ret
;splittet wert in a
;schreibt high nibble an addresse in r1
;schreibt low nibble an addresse in r0
split:
mov r7, a
orl a, #f0h
mov @r0, a
mov a, r7
swap a
orl a, #f0h
if bit is_hour ;sonderbehandlung stunden
orl a, #f8h
anl a, #fbh
end if
mov @r1, a
mov a, r7
ret
;holt werte von addressen in r1 und r0
;low nibble aus r1 wird high nibble in a
;low nibble aus r0 wird low nibble in a
merge:
mov a, @r0
anl a, #0fh
mov r7, a
mov a, @r1
anl a, #0fh
if bit is_hour ;sonderbehandlung stunden
anl a, #03h
end if
swap a
add a, r7
ret
;fragt den drehencoder ab
;zu verändernder wert muss in r2 stehen
dreh_coder:
movb(alt_0, neu_0) ;alten zustand sichern
movb(alt_1, neu_1)
movb(neu_0, dreh_l) ;neuen zustand vom port lesen
movb(neu_1, dreh_r)
dptr=#geber_tab ;datenpointer für wertetabelle holen
mov a, #0h
if bit alt_0 ;alter zustand codiert bytenummer aus tabelle
inc a
inc a
end if
if bit alt_1
inc a
end if
movc a, @a+dptr
if bit neu_0 ;erstes bit neuer zustand codiert halbbyte
swap a
end if
if bit neu_1 ;zweites bit neuer zustand codiert viertelbyte
rr a
rr a
end if
anl a, #03h
if a=#01h ;1 -> wert erhöhen
mov a, r2
add a, #1h
da a ;bcd korrektur
mov r2, a
mov a, #0h
setb time_has_changed
end if
if a=#02h ;2 -> wert verringern
mov a, r2
dec a
mov r2, a
anl a, #0fh
if a=#0fh ;bcd korrektur manuell
mov a, r2
anl a, #f0h
orl a, #09h
else
mov a, r2
end if
mov r2, a
setb time_has_changed
end if
ret
; fügt den Doppelpunktstatus zur Ausgabe hinzu
add_dp:
; if bit dp_active
orl a, #80h
; end if
ret
;liest die Weckzeit aus dem internen EEprom
read_eeprom:
mov dptr, #wz_eeprom
rinc
if bit acc.0
setb alarm_is_on
setb alarm_led
end if
rinc
mov WmH10, a
rinc
mov WmH1, a
rinc
mov WmMi10, a
rinc
mov WmMi1, a
ret
; schreibt die Weckzeit in den internen EEprom
write_eeprom:
mov dptr, #wz_eeprom
inc dptr
mov a, WmH10
winc
mov a, WmH1
winc
mov a, WmMi10
winc
mov a, WmMi1
winc
ret
; schreibt den Alarmstatus in den internen EEprom
save_astat:
mov dptr, #wz_eeprom
if bit alarm_is_on
mov a, #ffh
else
mov a, #0h
end if
winc
ret
Externer Interrupt 0:
clr ea
setb stellen
reti
Externer Interrupt 1:
clr ex1
setb alarm_button_pressed
reti
; toggelt den Doppelpunktstatus
; reaktiviert den Alarmbutton
Timer 0 Interrupt:
push acc
mov a, dp_timer
if a=#0
setb ex1
cpl dp_active
mov a, #dp_const
else
dec a
end if
mov dp_timer, a
pop acc
reti
; genertiert den Weckton
Timer 1 Interrupt:
push acc
mov a, buzzer_counter
if a=#0
cpl buzzer_skip
mov a, #ffh
else
dec a
end if
mov buzzer_counter, a
if bit buzzer_skip
cpl buzzer
else
clr buzzer
end if
mov th1, #feh
mov tl1, #ffh
pop acc
reti
; Hilfstabelle für den Drehgeber
geber_tab:
db 24h, 0h, 0h, 18h
; Muster für die 7-Segmentanzeige
7seg_tab:
db 3fh, 06h, 5bh, 4fh, 66h, 6dh, 7dh, 07h, 7fh, 6fh
db 00h, 00h, 00h, 4ch, 58h, 00h
LED Clock R5 Index