;********************************************************************** ;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