Die Uhrzeit wird beim Starten empfangen und jeden Sonntag um 4:00:50. Der Einfachheit halber zähle ich die Uhrzeit nicht weiter, während die Zeit empfangen wird. Damit man die stehende Uhr möglichst nicht sieht, empfange ich nachts um 4 Uhr die Uhrzeit. Die Quarztaktung ist so genau, dass kein täglicher Abgleich stattfinden muss. Um trotzdem die Umschaltung zwischen Sommer- und Winderzeit zu erkennen, die ja immer nachts um 2 bzw. 3 Uhr an einem Sonntag stattfindet, mache ich den Abgleich jeden Sonntag. Das Starten in der 50. Sekunde verringert die Zeit, die für den Empfang benötigt wird, da die Übertragung der Daten bei der 1. Sekunde einer Minute startet.
Das Weiterzählen der Uhrzeit um eine Sekunde findet mit den entsprechenden Überträgen statt. Beim Tag wird ein evtl. Schaltjahr am Ende des Februars ebenfalls berücksichtigt.
Die Temperatur wird einmal pro Minute gemessen. Für innen und außen wird jeweils der bisher vorgekommene Maximal- und Minimalwert abgespeichert. Für jeden dieser vier Werte wird ein ganzer Satz Informationen inkl. Datum und Uhrzeit abgespeichert, wobei der Zeitpunkt aufgezeichnet wird, zudem diese Temperatur das letzte Mal auftrat. Zum Vergleich der Temperaturen wird übrigens intern ein gemessene Zeitwert abgespeichert statt der Temperatur als Ganzzahl. In der Praxis bedeutet das, dass der tatsächliche Zeitpunkt mit der minimalen / maximalen Temperatur genauer bestimmt wird, da eine höhere Genauigkeit als 1°C benutzt wird.
Zur Anzeige der bisherigen Maximal- und Minimalwerte drückt man einen Taster, der leicht versenkt an der Seite des Gehäuses eingebaut ist. Die vier Datensätze werden dann jeweils für 3 Sekunden angezeigt, wobei eine der beiden Temperaturen jeweils ausgeblendet wird. Danach wird wieder die aktuelle Zeit und Temperatur angezeigt. Zum Zurücksetzen der gespeicherten Daten drückt man während des Anzeigezyklus' nochmals den Taster. Alle Datensätze werden dann (wie auch nach Start der Uhr) auf die aktuellen Werte gesetzt.
Zur sicheren Erkennung der einzelnen Bits beim Empfang der Uhrzeit habe ich die Signallängen gemessen:
Dadurch ergeben sich die Schwellwerte
Durch die Verwendung der Schwellwerte kann der Empfang von falschen Daten verringert werden. Zusätzlich werden die übertragenen Prüfbits verglichen.
dcf77.asm (32 kB)
; ##################################################################### ; ### Funkuhr 1.0 01/2004 (c) Uwe Freese ### ; ### http://www.uwe-freese.de mail@uwe-freese.de ### ; ### ### ; ### You can use this program freely, but please always include ### ; ### this copyright information when you share it. ### ; ### ### ; ### Diese Software kann frei verwendet werden, aber bitte immer ### ; ### diesen Copyright-Hinweis hinzufügen, wenn es weitergegeben ### ; ### wird! ### ; ##################################################################### .CSEG .ORG 0x00 .INCLUDE "2313def.inc" .EQU SIGNAL_LENGTH_SHORT_LOW = 7 ; threshold between low and a too short signal (reception error) .EQU SIGNAL_LENGTH_SHORT_HIGH = 79 ; (unused) .EQU SIGNAL_LENGTH_LONG_HIGH = 95 .EQU SIGNAL_LENGTH_LONG_LOW = 21 .EQU SIGNAL_LENGTH_HIGH = 11 ; threshold between low and high signal .EQU SIGNAL_LENGTH_SYNC_LOW = 171 ; minimum length for a sync signal (59th second) .EQU SIGNAL_LENGTH_SYNC_HIGH = 195 ; maximum length for a sync signal (59th second) .EQU SYNCTIME_H = 4 .EQU SYNCTIME_M = 0 .EQU SYNCTIME_S = 50 .EQU SYNCTIME_W = 7 ; day, Mo=1, ... So=7 .DEF R_TMP = R20 .DEF R_TMP2 = R21 .DEF R_CHECKBIT = R22 .DEF R_SIGNAL_LENGTH = R19 .DEF R_7SEG_DIGIT = R23 ; digit that should be displayed .DEF R_7SEG_DIGIT_NR = R24 ; number of the LED display that should display the number .DEF R_OVERFLOW = R25 .DEF R_DELAY1 = R16 .DEF R_DELAY2 = R17 .DEF R_DCFBIT = R18 ; Ports 'n Pins .EQU DCF_IN_PORT = PINB .EQU DCF_IN_PIN = 2 .EQU USERKEY_IN_PORT = PIND .EQU USERKEY_IN_PIN = 6 .EQU TEMP1_IN_PORT = PINB .EQU TEMP1_IN_PIN = 0 .EQU TEMP2_IN_PORT = PINB .EQU TEMP2_IN_PIN = 1 .EQU LED_PORT = PORTB .EQU LED_PIN = 3 .EQU SEVENSEG_CLCK_PORT = PORTD .EQU SEVENSEG_CLCK_PIN = 4 .EQU CLOCK_PORT = PIND .EQU CLOCK_PIN = 5 .EQU CLOCK_RESET_PIN = 0 .EQU PORTX_CHANNEL = 14 ; channel of PORTX on the 4067 ; one byte for every LED-7segment-display .EQU SRAM_DATA_START = 0xB6 .EQU portx = 0xB6; PORTX byte .EQU therm_counter = 0xB7 ; thermometer 0=show time, 1..3=show min in, 4..6=show max in, 7..9=show min out, 10..12=show max out .EQU maxout_therm_out = 0xB8 ; thermometer .EQU maxout_therm_in = 0xB9 ; thermometer .EQU maxout_d_m = 0xBA ; date .EQU maxout_d_d = 0xBB ; date .EQU maxout_t_h = 0xBC ; time .EQU maxout_t_m = 0xBD ; time .EQU minout_therm_out = 0xBE ; thermometer .EQU minout_therm_in = 0xBF ; thermometer .EQU minout_d_m = 0xC0 ; date .EQU minout_d_d = 0xC1 ; date .EQU minout_t_h = 0xC2 ; time .EQU minout_t_m = 0xC3 ; time .EQU maxin_therm_out = 0xC4 ; thermometer .EQU maxin_therm_in = 0xC5 ; thermometer .EQU maxin_d_m = 0xC6 ; date .EQU maxin_d_d = 0xC7 ; date .EQU maxin_t_h = 0xC8 ; time .EQU maxin_t_m = 0xC9 ; time .EQU minin_therm_out = 0xCA ; thermometer .EQU minin_therm_in = 0xCB ; thermometer .EQU minin_d_m = 0xCC ; date .EQU minin_d_d = 0xCD ; date .EQU minin_t_h = 0xCE ; time .EQU minin_t_m = 0xCF ; time .EQU therm_out = 0xD0 ; thermometer .EQU therm_in = 0xD1 ; thermometer .EQU d_m = 0xD2 ; date .EQU d_d = 0xD3 ; date .EQU t_h = 0xD4 ; time .EQU t_m = 0xD5 ; time .EQU t_s = 0xD6 ; time .EQU d_w = 0xD7 ; date .EQU d_y = 0xD8 ; date .EQU therm_out_shown = 0xD9 ; date .EQU therm_in_shown = 0xDA ; date .EQU d_m_shown = 0xDB ; date .EQU d_d_shown = 0xDC ; date .EQU t_h_shown = 0xDD ; time .EQU t_m_shown = 0xDE ; time .EQU t_s_shown = 0xDF ; time .EQU NOTEMP_VALUE = 235; value that's stored in SRAM if this temp shouldn't be shown .EQU TEMPZERO_VALUE = 125; a time value at 0°C ; Stack initialisieren ldi R_TMP, 0xB5; RAMEND == 0xDF out SPL, R_TMP rcall Init ; for calibrating the temp sensor, uncomment the following line ;rjmp GetTempRaw ; if user button pressed, run Demo sbic USERKEY_IN_PORT, USERKEY_IN_PIN ; Skip next inst. if bit in Port cleared rjmp Demo rjmp Start ; Temperature constants, Values for -20°C, -19°C,... +40°C ; if measured time is lower than the first value, -20°C is displayed, ; if measured time is higher or equal than the first, -19°C is displayed, ... ; (values are stored in EEPROM) ; ----- delay 100us (at 4 MHz -> 400 cycles) ----- Delay100us: ldi R_DELAY1, 98 ; 1 cycle Delay100us2: dec R_DELAY1 ; 1 cycle cpi R_DELAY1, 0 ; 1 cycle brne Delay100us2 ; 1 cycle if false, 2 if true nop ret ; 4 cycles ; ----- delay 5ms (at 4 MHz) ----- Delay5ms: ldi R_DELAY2, 49 Delay5ms2: rcall Delay100us dec R_DELAY2 cpi R_DELAY2, 0 brne Delay5ms2 ret ; ----- delay 10ms (at 4 MHz) ----- Delay10ms: ldi R_DELAY2, 99 Delay10ms2: rcall Delay100us dec R_DELAY2 cpi R_DELAY2, 0 brne Delay10ms2 ret ; ----- Delay 850ms ----- Delay890ms: ldi R_TMP, 89 Delay890ms_Loop: rcall delay10ms dec R_TMP cpi R_TMP, 1 brsh Delay890ms_Loop ret ; ----- Wait for a clock tick ----- WaitForTick: ldi R_TMP, 0 ; remember in R_TMP if user key was pressed WaitForTick_Low: ; (Low) sbic USERKEY_IN_PORT, USERKEY_IN_PIN ; Skip next inst. if bit in Port cleared ldi R_TMP, 1 sbic CLOCK_PORT, CLOCK_PIN ; Skip next inst. if bit in Port cleared rjmp WaitForTick_High rjmp WaitForTick_Low WaitForTick_High: ; (High) sbic USERKEY_IN_PORT, USERKEY_IN_PIN ; Skip next inst. if bit in Port cleared ldi R_TMP, 1 sbis CLOCK_PORT, CLOCK_PIN ; Skip next inst. if bit in Port set rjmp WaitForTick_CheckUserPressedKey rjmp WaitForTick_High WaitForTick_CheckUserPressedKey: ; if R_TMP == 1, set therm_counter to 12 cpi R_TMP, 1 breq WaitForTick_SetThermCounter ret WaitForTick_SetThermCounter: ; if 0 < therm_counter < 12, then the user pressed the key a second time ; delete all remembered temperatures then lds R_TMP, therm_counter cpi R_TMP, 12 brge WaitForTick_NoSecondKeypress cpi R_TMP, 0 breq WaitForTick_NoSecondKeypress ; Ah! Second keypress detected. Delete temperatures. rcall Reset_Temp WaitForTick_NoSecondKeypress: ldi R_TMP, 13 sts therm_counter, R_TMP ret ; ----- wait until level going low, then up ----- WaitForLow: ldi R_SIGNAL_LENGTH, 0 ; Reset signal length WaitForLow2: rcall Delay10ms cpi R_SIGNAL_LENGTH, 0xFF ; inc signal length if not at maximum breq WaitForLow2_NoInc inc R_SIGNAL_LENGTH ; count signal length WaitForLow2_NoInc: sbic DCF_IN_PORT, DCF_IN_PIN ; Skip next inst. if bit 0 in Port D set rjmp WaitForLow2 ; Bit not set ret ; ----- wait until level going high, count signal length ----- WaitForHigh: ldi R_SIGNAL_LENGTH, 0 ; Reset signal length WaitForHigh2: rcall Delay10ms cpi R_SIGNAL_LENGTH, 0xFF ; inc signal length if not at maximum breq WaitForHigh2_NoInc inc R_SIGNAL_LENGTH ; count signal length WaitForHigh2_NoInc: sbis DCF_IN_PORT, DCF_IN_PIN ; Skip next inst. if bit 0 in Port D set rjmp WaitForHigh2 ; Bit not set ret ; ----- wait for the start of a minute ----- WaitForSecond0: ; Wait for the 59th second (long period) rcall WaitForHigh rcall WaitForLow cpi R_SIGNAL_LENGTH, SIGNAL_LENGTH_SYNC_LOW brlo WaitForSecond0 cpi R_SIGNAL_LENGTH, SIGNAL_LENGTH_SYNC_HIGH brge WaitForSecond0 ret ; ----- 'PORTC' is the 74HC573 latch on the 15th output of the 4067 ----- ; ----- It stores 4 bits output. The value is stored in SRAM. ----- ; PORTX mapping: ; bit 0: real time clock counter reset (reset offset) ; bit 1: minus sign of 10th display of out temperature ; bit 2: extra minus sign OutPORTX: lds R_7SEG_DIGIT, portx ldi R_7SEG_DIGIT_NR, PORTX_CHANNEL rcall PrintDigit ret ; ----- reset max/min temperatures ----- Reset_Temp: ; clear maxout ldi R_OVERFLOW, therm_out ldi R_TMP, maxout_therm_out rcall CopyData ; clear maxin ldi R_OVERFLOW, therm_out ldi R_TMP, maxin_therm_out rcall CopyData ; clear minout ldi R_OVERFLOW, therm_out ldi R_TMP, minout_therm_out rcall CopyData ; clear minin ldi R_OVERFLOW, therm_out ldi R_TMP, minin_therm_out rcall CopyData ret Init: ; Pins on Port D used: ; Bit 0: 7segment data ; 1: 7segment data ; 2: 7segment data ; 3: 7segment data ; 4: 7segment selection clock (4067) ; 5: real time clock signal in (1 Hz) ; 6: user button in ldi R_TMP, 0b00011111 ; Port D Control out DDRD, R_TMP ; Data direction register ldi R_TMP, 0b00010000 ; Pullup resistors on DCF input on to avoid glitches out PortD, R_TMP ; on open inputs ; Pins on Port B: ; Bit 0: Input Temp. 1 ; 1: Input Temp. 2 ; 2: DCF In ; 3: DCF Sync LED + Charge Temp. Capacity ; 4: 7segment address ; 5: 7segment address ; 6: 7segment address ; 7: 7segment address ldi R_TMP, 0b11111000 ; Port B Control out DDRB, R_TMP ; Data Direction Register ldi R_TMP, 0b11110111 ; pullup resistors out PortB, R_TMP ; clear SRAM ldi R_TMP, 0 ldi R_TMP2, SRAM_DATA_START Init_ClearSRAM: ldi XH, 0 mov XL, R_TMP2 st X, R_TMP inc R_TMP2 cpi R_TMP2, 0xE0 brlo Init_ClearSRAM ret ; ----- scan one DCF bit and save it as bit 0 in R_DCFBIT ----- ; ----- on error, also set bit 1 in R_DCFBIT ----- ScanBit: rcall WaitForLow cpi R_SIGNAL_LENGTH, SIGNAL_LENGTH_LONG_HIGH brge ScanBitError ; Error if signal pause was too long rcall PrintTimeAndDate ; update display, takes about 0,3 ms, so it's not a problem for the signal length measurement rcall WaitForHigh ; inc sec. counter lds R_OVERFLOW, t_s ; misuse of R_OVERFLOW, because R_TMP and R_TMP2 are used outside inc R_OVERFLOW sts t_s, R_OVERFLOW cpi R_SIGNAL_LENGTH, SIGNAL_LENGTH_SHORT_LOW brlo ScanBitError ; Error if signal was too short cpi R_SIGNAL_LENGTH, SIGNAL_LENGTH_LONG_LOW brge ScanBitError ; Error if signal was too long cpi R_SIGNAL_LENGTH, SIGNAL_LENGTH_HIGH brlo ScanBit0 ScanBit1: ldi R_DCFBIT, 1 sbi LED_PORT, LED_PIN ret ScanBit0: ldi R_DCFBIT, 0 cbi LED_PORT, LED_PIN ret ScanBitError: ldi R_DCFBIT, 2 ret ; ----- scan one bit. If R_DCFBIT shows an error, jump (!) to ScanTime ----- ; ----- (return address is consumed from stack beforre!). ----- ; ----- If R_DCFBIT is 1 (1 received), add R_TMP2 to R_TMP. ----- ; ----- R_CHECKBIT is negated if a 1 was received. ----- ScanBitAndAdd: rcall ScanBit sbrs R_DCFBIT, 1 ; if error bit is set, restart rjmp ScanBitAndAdd_ok ; consume return address from stack, because we'll do a rjmp instead of a ret pop R_CHECKBIT ; R_CHECKBIT isn't used anymore pop R_CHECKBIT rjmp ScanTime ScanBitAndAdd_ok: sbrc R_DCFBIT, 0 add R_TMP, R_TMP2 ; add value if bit was high sbrc R_DCFBIT, 0 neg R_CHECKBIT ; calculate check bit ret ; ----- clear current time and temp values ----- ClearCurrentTimeAndTemp: ldi R_TMP, 0 sts t_s, R_TMP sts t_m, R_TMP sts t_h, R_TMP sts d_d, R_TMP sts d_m, R_TMP sts d_y, R_TMP sts d_w, R_TMP ldi R_TMP, TEMPZERO_VALUE ; store the value at temp 0°C sts therm_out, R_TMP sts therm_in, R_TMP ret ; ----- read all bits of one minute, starting at second 0 ----- ; ----- save them to time registers ----- ScanTime: rcall ClearCurrentTimeAndTemp rcall PrintTimeAndDate ; turn clock counter off lds R_TMP, portx ori R_TMP, 0b00000001 sts portx, R_TMP rcall OutPORTX sbi LED_PORT, LED_PIN rcall Delay10ms rcall Delay10ms rcall Delay10ms rcall Delay10ms cbi LED_PORT, LED_PIN rcall Delay10ms rcall Delay10ms rcall Delay10ms rcall Delay10ms sbi LED_PORT, LED_PIN rcall Delay10ms rcall Delay10ms rcall Delay10ms rcall Delay10ms cbi LED_PORT, LED_PIN rcall WaitForSecond0 ; now we are at the start of a minute ; Sekunde: Bedeutung: ; 0. Minutenbeginn (immer LOW) ; 1. - 14. Reserviert, keine Bedeutung ; 15. Reserveantenne aktiv ; 16. Umstellung von Sommer- auf Winterzeit, oder umgekehrt ; 17. Sommerzeit aktiv ; 18. Winterzeit aktiv ; 19. Ankündigung Schaltsekunde ; 20. Zeitbeginn ; 21. - 27. Minute 1, 2, 4, 8, 10, 20, 40 ; 28. Prüfbit Minute ; 29. - 34. Stunde 1, 2, 4, 8, 10, 20 ; 35. Prüfbit Stunde ; 36. - 41. Tag 1, 2, 4, 8, 10, 20 ; 42. - 44. Wochentag 1, 2, 4 ; 45. - 49. Monat 1, 2, 4, 8, 10 ; 50. - 57. Jahr 1, 2, 4, 8, 10, 20, 40, 80 ; 58. Prüfbit Datum ; 59. fehlt zur Erkennung des Minutenanfangs ; skip 21 bits ScanTime_Skip21: rcall ScanBit sbrc R_DCFBIT, 1 ; if error bit is set, restart rjmp ScanTime lds R_OVERFLOW, t_s cpi R_OVERFLOW, 21 brlo ScanTime_Skip21 ; read minutes ldi R_TMP, 0 ldi R_CHECKBIT, 0 ldi R_TMP2, 1 rcall ScanBitAndAdd ldi R_TMP2, 2 rcall ScanBitAndAdd ldi R_TMP2, 4 rcall ScanBitAndAdd ldi R_TMP2, 8 rcall ScanBitAndAdd ldi R_TMP2, 10 rcall ScanBitAndAdd ldi R_TMP2, 20 rcall ScanBitAndAdd ldi R_TMP2, 40 rcall ScanBitAndAdd ; check minutes check bit rcall ScanBit sbrc R_DCFBIT, 1 ; if error bit is set, restart rjmp ScanTime sbrc R_DCFBIT, 0 neg R_CHECKBIT ; calculate check bit ; R_TMP2 must be 0 if data is correct sbrc R_CHECKBIT, 0 rjmp ScanTime ; back to start ; save minutes to sram sts t_m, R_TMP ; read hours ldi R_TMP, 0 ldi R_CHECKBIT, 0 ldi R_TMP2, 1 rcall ScanBitAndAdd ldi R_TMP2, 2 rcall ScanBitAndAdd ldi R_TMP2, 4 rcall ScanBitAndAdd ldi R_TMP2, 8 rcall ScanBitAndAdd ldi R_TMP2, 10 rcall ScanBitAndAdd ldi R_TMP2, 20 rcall ScanBitAndAdd ; check hours check bit rcall ScanBit sbrc R_DCFBIT, 1 ; if error bit is set, restart rjmp ScanTime sbrc R_DCFBIT, 0 neg R_CHECKBIT ; calculate check bit ; R_TMP2 must be 0 if data is correct sbrc R_CHECKBIT, 0 rjmp ScanTime ; back to start ; save hours to sram sts t_h, R_TMP ; read days ldi R_TMP, 0 ldi R_CHECKBIT, 0 ldi R_TMP2, 1 rcall ScanBitAndAdd ldi R_TMP2, 2 rcall ScanBitAndAdd ldi R_TMP2, 4 rcall ScanBitAndAdd ldi R_TMP2, 8 rcall ScanBitAndAdd ldi R_TMP2, 10 rcall ScanBitAndAdd ldi R_TMP2, 20 rcall ScanBitAndAdd ; save days to sram sts d_d, R_TMP ; skip weekday ldi R_TMP, 0 ldi R_TMP2, 1 rcall ScanBitAndAdd ldi R_TMP2, 2 rcall ScanBitAndAdd ldi R_TMP2, 4 rcall ScanBitAndAdd ; save weekdays to sram sts d_w, R_TMP ; read month ldi R_TMP, 0 ldi R_TMP2, 1 rcall ScanBitAndAdd ldi R_TMP2, 2 rcall ScanBitAndAdd ldi R_TMP2, 4 rcall ScanBitAndAdd ldi R_TMP2, 8 rcall ScanBitAndAdd ldi R_TMP2, 10 rcall ScanBitAndAdd ; save month to sram sts d_m, R_TMP ; read year ldi R_TMP, 0 ldi R_TMP2, 1 rcall ScanBitAndAdd ldi R_TMP2, 2 rcall ScanBitAndAdd ldi R_TMP2, 4 rcall ScanBitAndAdd ldi R_TMP2, 8 rcall ScanBitAndAdd ldi R_TMP2, 10 rcall ScanBitAndAdd ldi R_TMP2, 20 rcall ScanBitAndAdd ldi R_TMP2, 40 rcall ScanBitAndAdd ldi R_TMP2, 80 rcall ScanBitAndAdd ; save year to sram sts d_y, R_TMP ; check date check bit rcall ScanBit sbrc R_DCFBIT, 1 ; if error bit is set, restart rjmp ScanTime sbrc R_DCFBIT, 0 neg R_CHECKBIT ; calculate check bit ; R_TMP2 must be 0 if data is correct sbrc R_CHECKBIT, 0 rjmp ScanTime ; back to start ; charge capacitor cbi LED_PORT, LED_PIN ; ExactSync loop, about 900ms rcall Delay890ms rcall PrintTimeAndDate ; show 59 sec. ldi R_TMP, 0 sts t_s, R_TMP ; turn clock counter on lds R_TMP, portx andi R_TMP, 0b11111110 sts portx, R_TMP ; now wait for low of the DCF receiver, then start the clock! rcall WaitForLow rcall OutPORTX rcall PrintTimeAndDate ; show 0 sec. ret ; ----- add one second to the current time & date of the clock ----- ClockTick: ; add one second lds R_TMP, t_s inc R_TMP sts t_s, R_TMP cpi R_TMP, 60 ; if (s >= 60) then go on, return otherwise in R_TMP2, SREG sbrc R_TMP2, 4 ret ; set second to 0 ldi R_TMP, 0 sts t_s, R_TMP ; add one minute lds R_TMP, t_m inc R_TMP sts t_m, R_TMP cpi R_TMP, 60 ; if (m >= 60) then go on, return otherwise in R_TMP2, SREG sbrc R_TMP2, 4 ret ; set minute to 0 ldi R_TMP, 0 sts t_m, R_TMP ; add one hour lds R_TMP, t_h inc R_TMP sts t_h, R_TMP cpi R_TMP, 24 ; if (m >= 24) then go on, return otherwise in R_TMP2, SREG sbrc R_TMP2, 4 ret ; set hour to 0 ldi R_TMP, 0 sts t_h, R_TMP ; calculate overflow for 1ths and 10ths for days lds R_TMP, d_m ; Jan cpi R_TMP, 1 breq ClockTick_31days ; Feb cpi R_TMP, 2 breq ClockTick_Feb ; Mar cpi R_TMP, 3 breq ClockTick_31days ; Apr cpi R_TMP, 4 breq ClockTick_30days ; May cpi R_TMP, 5 breq ClockTick_31days ; Jun cpi R_TMP, 6 breq ClockTick_30days ; Jul cpi R_TMP, 7 breq ClockTick_31days ; Aug cpi R_TMP, 8 breq ClockTick_31days ; Sep cpi R_TMP, 9 breq ClockTick_30days ; Oct cpi R_TMP, 10 breq ClockTick_31days ; Nov cpi R_TMP, 11 breq ClockTick_30days ; Dec cpi R_TMP, 12 breq ClockTick_31days ; check if 28 or 29 days in feb this year ClockTick_Feb: lds R_TMP2, d_y andi R_TMP2, 3 cpi R_TMP2, 0 ; if 0, then 29 days in feb breq ClockTick_29days ;rjmp ClockTick_28days ClockTick_28days: ldi R_OVERFLOW, 29 rjmp ClockTick_days ClockTick_29days: ldi R_OVERFLOW, 30 rjmp ClockTick_days ClockTick_30days: ldi R_OVERFLOW, 31 rjmp ClockTick_days ClockTick_31days: ldi R_OVERFLOW, 32 rjmp ClockTick_days ClockTick_days: ; add one weekday modulo 7 (1..7) lds R_TMP, d_w inc R_TMP cpi R_TMP, 8 brne ClockTick_days_ok ldi R_TMP, 1 ClockTick_days_ok: sts d_w, R_TMP ; add one day lds R_TMP, d_d inc R_TMP sts d_d, R_TMP cp R_TMP, R_OVERFLOW ; if (d >= R_OVERFLOW) then go on, return otherwise in R_TMP2, SREG sbrc R_TMP2, 4 ret ; set day to 1 ldi R_TMP, 1 sts d_d, R_TMP ; add one month lds R_TMP, d_m inc R_TMP sts d_m, R_TMP cpi R_TMP, 13 ; if (s >= 13) then go on, return otherwise in R_TMP2, SREG sbrc R_TMP2, 4 ret ; set month to 1 ldi R_TMP, 1 sts d_m, R_TMP ret ; ----- divide R_TMP (0..69) by 10 and store result in R_TMP2 (0..6) ----- DivideBy10: ; assume 0 as result ldi R_TMP2, 0 cpi R_TMP, 10 brge DivideBy10_1 ret DivideBy10_1: ; assume 1 as result ldi R_TMP2, 1 cpi R_TMP, 20 brge DivideBy10_2 ret DivideBy10_2: ; assume 2 as result ldi R_TMP2, 2 cpi R_TMP, 30 brge DivideBy10_3 ret DivideBy10_3: ; assume 3 as result ldi R_TMP2, 3 cpi R_TMP, 40 brge DivideBy10_4 ret DivideBy10_4: ; assume 4 as result ldi R_TMP2, 4 cpi R_TMP, 50 brge DivideBy10_5 ret DivideBy10_5: ; assume 5 as result ldi R_TMP2, 5 ret MultiplyBy10: mov R_TMP2, R_TMP lsl R_TMP2 ; * 2 lsl R_TMP2 ; * 4 lsl R_TMP2 ; * 8 add R_TMP2, R_TMP add R_TMP2, R_TMP ret ; ----- print out one digit to a 7 segment LED display ----- PrintDigit: push R_TMP push R_TMP2 ; set data at port d in R_TMP, PORTD andi R_TMP, 0b01110000 or R_TMP, R_7SEG_DIGIT out PORTD, R_TMP ; set address at port b mov R_TMP2, R_7SEG_DIGIT_NR lsl R_TMP2 lsl R_TMP2 lsl R_TMP2 lsl R_TMP2 in R_TMP, PORTB andi R_TMP, 0b00001111 or R_TMP, R_TMP2 out PORTB, R_TMP nop nop ; let the 4067 select the current 7segment driver cbi SEVENSEG_CLCK_PORT, SEVENSEG_CLCK_PIN nop sbi SEVENSEG_CLCK_PORT, SEVENSEG_CLCK_PIN nop ; now let the 4067 select a non existant 7segment ori R_TMP, 0b11110000 out PORTB, R_TMP nop nop cbi SEVENSEG_CLCK_PORT, SEVENSEG_CLCK_PIN nop sbi SEVENSEG_CLCK_PORT, SEVENSEG_CLCK_PIN pop R_TMP2 pop R_TMP ret ; ----- copy 6 bytes from SRAM at pos OVERFLOW to SRAM at pos TMP ----- CopyData: push R_TMP2 push R_TMP ldi XH, 0 ldi YH, 0 mov XL, R_OVERFLOW mov YL, R_TMP ldi R_TMP2, 0 CopyData_Loop: ld R_TMP, X+ st Y+, R_TMP inc R_TMP2 cpi R_TMP2, 6 brge CopyData_Ret rjmp CopyData_Loop CopyData_Ret: pop R_TMP pop R_TMP2 ret ; ----- print out the current time & date to the 7 segment LED display ----- PrintTimeAndDate: push R_TMP push R_TMP2 push R_OVERFLOW ; Set the values that should be shown. This is the current time or time and date of some temperature max/min. lds R_TMP, therm_counter cpi R_TMP, 0 breq PrintTimeAndDate_NoDec dec R_TMP sts therm_counter, R_TMP PrintTimeAndDate_NoDec: ; thermometer 0=show time, 1..3=show min in, 4..6=show max in, 7..9=show min out, 10..12=show max out ; assume to show current time lds R_TMP2, t_s ; show current sec sts t_s_shown, R_TMP2 ldi R_OVERFLOW, therm_out cpi R_TMP, 1 ; if (s >= 1) then go on, return otherwise in R_TMP2, SREG sbrc R_TMP2, 4 rjmp PrintTimeAndDate_CopyData ; assume to show min in ldi R_TMP2, 0 ; show 0 as sec for min/max times sts t_s_shown, R_TMP2 ldi R_OVERFLOW, minin_therm_out cpi R_TMP, 4 ; if (s >= 4) then go on, return otherwise in R_TMP2, SREG sbrc R_TMP2, 4 rjmp PrintTimeAndDate_CopyData ; assume to show max in ldi R_OVERFLOW, maxin_therm_out cpi R_TMP, 7 ; if (s >= 7) then go on, return otherwise in R_TMP2, SREG sbrc R_TMP2, 4 rjmp PrintTimeAndDate_CopyData ; assume to show min out ldi R_OVERFLOW, minout_therm_out cpi R_TMP, 10 ; if (s >= 10) then go on, return otherwise in R_TMP2, SREG sbrc R_TMP2, 4 rjmp PrintTimeAndDate_CopyData ; show max out ldi R_OVERFLOW, maxout_therm_out PrintTimeAndDate_CopyData: ; copy 6 Bytes from SRAM beginning at R_OVERFLOW to SRAM beginning at therm_out_shown ldi R_TMP, therm_out_shown rcall CopyData ; convert temperatures lds R_TMP, therm_in_shown rcall TempLookup sts therm_in_shown, R_TMP lds R_TMP, therm_out_shown rcall TempLookup sts therm_out_shown, R_TMP ; print second 10ths lds R_TMP, t_s_shown rcall DivideBy10 mov R_7SEG_DIGIT, R_TMP2 ldi R_7SEG_DIGIT_NR, 1 rcall PrintDigit ; print second 1ths mov R_TMP, R_7SEG_DIGIT rcall MultiplyBy10 lds R_TMP, t_s_shown sub R_TMP, R_TMP2 ; R_TMP now stores the 1ths seconds mov R_7SEG_DIGIT, R_TMP ldi R_7SEG_DIGIT_NR, 0 rcall PrintDigit ; print minute 10ths lds R_TMP, t_m_shown rcall DivideBy10 mov R_7SEG_DIGIT, R_TMP2 ldi R_7SEG_DIGIT_NR, 3 rcall PrintDigit ; print minute 1ths mov R_TMP, R_7SEG_DIGIT rcall MultiplyBy10 lds R_TMP, t_m_shown sub R_TMP, R_TMP2 ; R_TMP now stores the 1ths minutes mov R_7SEG_DIGIT, R_TMP ldi R_7SEG_DIGIT_NR, 2 rcall PrintDigit ; print hours 10ths lds R_TMP, t_h_shown rcall DivideBy10 mov R_7SEG_DIGIT, R_TMP2 ldi R_7SEG_DIGIT_NR, 5 rcall PrintDigit ; print hours 1ths mov R_TMP, R_7SEG_DIGIT rcall MultiplyBy10 lds R_TMP, t_h_shown sub R_TMP, R_TMP2 ; R_TMP now stores the 1ths hours mov R_7SEG_DIGIT, R_TMP ldi R_7SEG_DIGIT_NR, 4 rcall PrintDigit ; print days 10ths lds R_TMP, d_d_shown rcall DivideBy10 mov R_7SEG_DIGIT, R_TMP2 ldi R_7SEG_DIGIT_NR, 7 rcall PrintDigit ; print days 1ths mov R_TMP, R_7SEG_DIGIT rcall MultiplyBy10 lds R_TMP, d_d_shown sub R_TMP, R_TMP2 ; R_TMP now stores the 1ths days mov R_7SEG_DIGIT, R_TMP ldi R_7SEG_DIGIT_NR, 6 rcall PrintDigit ; print months 10ths lds R_TMP, d_m_shown rcall DivideBy10 mov R_7SEG_DIGIT, R_TMP2 ldi R_7SEG_DIGIT_NR, 9 rcall PrintDigit ; print months 1ths mov R_TMP, R_7SEG_DIGIT rcall MultiplyBy10 lds R_TMP, d_m_shown sub R_TMP, R_TMP2 ; R_TMP now stores the 1ths months mov R_7SEG_DIGIT, R_TMP ldi R_7SEG_DIGIT_NR, 8 rcall PrintDigit PrintTimeAndDate_TempIn: ; print temp in 10ths lds R_TMP, therm_in_shown cpi R_TMP, NOTEMP_VALUE breq PrintTimeAndDate_TempInHide rcall DivideBy10 mov R_7SEG_DIGIT, R_TMP2 mov R_OVERFLOW, R_TMP2 ; misuse R_OVERFLOW ; if zero, print out a 10, which turns off this digit because of the CMOS4543 cpi R_7SEG_DIGIT, 1 brge PrintTimeAndDate_ok1 ldi R_7SEG_DIGIT, 10 PrintTimeAndDate_ok1: ldi R_7SEG_DIGIT_NR, 11 rcall PrintDigit ; print temp in 1ths mov R_TMP, R_OVERFLOW rcall MultiplyBy10 lds R_TMP, therm_in_shown sub R_TMP, R_TMP2 ; R_TMP now stores the 1ths months mov R_7SEG_DIGIT, R_TMP ldi R_7SEG_DIGIT_NR, 10 rcall PrintDigit rjmp PrintTimeAndDate_TempOut PrintTimeAndDate_TempInHide: ; show nothing ldi R_7SEG_DIGIT, 10 ; 4543 makes it display nothing ldi R_7SEG_DIGIT_NR, 11 rcall PrintDigit ldi R_7SEG_DIGIT_NR, 10 rcall PrintDigit PrintTimeAndDate_TempOut: ; print temp out 10ths ; assume minus sign is off lds R_DELAY1, portx ; use R_DELAY1 as portx value andi R_DELAY1, 0b11111001 ; temp is positive? lds R_TMP, therm_out_shown cpi R_TMP, NOTEMP_VALUE breq PrintTimeAndDate_TempOutHide cpi R_TMP, 128 brlo PrintTimeAndDate_TempIsPositive ; negative, switch minus sign on ori R_DELAY1, 0b00000110 neg R_TMP sts therm_out_shown, R_TMP ; save the temp as positive value PrintTimeAndDate_TempIsPositive: rcall DivideBy10 mov R_7SEG_DIGIT, R_TMP2 ; if zero, print out a 10, which turns off this digit because of the CMOS4543 cpi R_7SEG_DIGIT, 1 brge PrintTimeAndDate_10thExist ; no 10th ldi R_7SEG_DIGIT, 10 andi R_DELAY1, 0b11111011 ; switch extra minus sign off rjmp PrintTimeAndDate_ok2 PrintTimeAndDate_10thExist: ori R_DELAY1, 0b00000010 ; switch minus sign of 10th display off, if R_7SEG_DIGIT is in {1, 7} cpi R_7SEG_DIGIT, 1 breq PrintTimeAndDate_10thMinusOff cpi R_7SEG_DIGIT, 7 breq PrintTimeAndDate_10thMinusOff rjmp PrintTimeAndDate_ok2 PrintTimeAndDate_10thMinusOff: andi R_DELAY1, 0b11111101 ; switch 10th minus sign off PrintTimeAndDate_ok2: ldi R_7SEG_DIGIT_NR, 13 rcall PrintDigit ; print temp out 1ths mov R_TMP, R_TMP2 rcall MultiplyBy10 lds R_TMP, therm_out_shown sub R_TMP, R_TMP2 ; R_TMP now stores the 1ths months mov R_7SEG_DIGIT, R_TMP ldi R_7SEG_DIGIT_NR, 12 rcall PrintDigit rjmp PrintTimeAndDate_PrintMinusSign PrintTimeAndDate_TempOutHide: ; show nothing ldi R_7SEG_DIGIT, 10 ; 4543 makes it display nothing ldi R_7SEG_DIGIT_NR, 13 rcall PrintDigit ldi R_7SEG_DIGIT_NR, 12 rcall PrintDigit PrintTimeAndDate_PrintMinusSign: sts portx, R_DELAY1 rcall OutPORTX pop R_OVERFLOW pop R_TMP2 pop R_TMP ret ; ----- lookup the temp for the measured time in EEPROM ----- ; IN: R_TMP (measured time) ; OUT: R_TMP (temperature, signed) ; REGISTER USAGE: none TempLookup: cpi R_TMP, NOTEMP_VALUE breq TempLookup_Ret push R_TMP2 push R_OVERFLOW ; set EEPROM read addres low byte (high byte is always 0, because eeprom not used somewhere else) ldi R_TMP2, 0 TempLookup_Loop: out EEARL, R_TMP2 sbi EECR, EERE ;set EEPROM Read strobe ;This instruction takes 4 clock cycles since ;it halts the CPU for two clock cycles in R_OVERFLOW, EEDR ;get data cp R_OVERFLOW, R_TMP brlo TempLookup_Found inc R_TMP2 rjmp TempLookup_Loop ; repeat until value found TempLookup_Found: ; R_TMP2 is the address in EEPROM last read ; let's substract 20, then we have the temperature in °C (because value for -20°C is stored in EEPROM location 0) subi R_TMP2, 20 mov R_TMP, R_TMP2 pop R_OVERFLOW pop R_TMP2 TempLookup_Ret: ret ; ----- measure the temperature ----- GetTempRaw: rcall Delay890ms ; disable charging capacitor sbi LED_PORT, LED_PIN ldi R_TMP, 60 sts therm_in, R_TMP sts therm_out, R_TMP ; now count till the temp input is high ldi R_TMP, 0 GetTempRaw_Count: rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us inc R_TMP sbic TEMP1_IN_PORT, TEMP1_IN_PIN ; Skip next inst. if bit 0 in Port D set rjmp GetTempRaw_Count ; charge capacitor cbi LED_PORT, LED_PIN ; lower 4 bit mov R_TMP2, R_TMP andi R_TMP2, 0b00001111 sts t_s, R_TMP2 ; higher 4 bit mov R_TMP2, R_TMP lsr R_TMP2 lsr R_TMP2 lsr R_TMP2 lsr R_TMP2 sts t_m, R_TMP2 rcall PrintTimeAndDate rjmp GetTempRaw ; ----- measure the temperature ----- GetTemp: ; disable charging capacitor sbi LED_PORT, LED_PIN ; now count till the temp input is high ldi R_TMP, 0 ldi R_TMP2, 0 GetTemp_Count: sbis TEMP1_IN_PORT, TEMP1_IN_PIN ; Skip next inst. if bit in Port cleared rjmp GetTemp_1Done sbis TEMP2_IN_PORT, TEMP2_IN_PIN ; Skip next inst. if bit in Port cleared rjmp GetTemp_2Done rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us inc R_TMP inc R_TMP2 rjmp GetTemp_Count GetTemp_1Done: sbis TEMP2_IN_PORT, TEMP2_IN_PIN ; Skip next inst. if bit in Port cleared rjmp GetTemp_BothDone rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us inc R_TMP2 rjmp GetTemp_1Done GetTemp_2Done: sbis TEMP1_IN_PORT, TEMP1_IN_PIN ; Skip next inst. if bit in Port cleared rjmp GetTemp_BothDone rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us rcall Delay100us inc R_TMP rjmp GetTemp_2Done GetTemp_BothDone: ; charge capacitor cbi LED_PORT, LED_PIN sts therm_in, R_TMP sts therm_out, R_TMP2 GetTemp_CheckMaxIn: ; is temp > max in temp? lds R_TMP2, therm_in lds R_TMP, maxin_therm_in ; if TMP <= TMP2 then save, if TMP2 < TMP, then branch cp R_TMP, R_TMP2 brlo GetTemp_CheckMinIn ; overwrite max in temp ldi R_OVERFLOW, therm_out ldi R_TMP, maxin_therm_out rcall CopyData ldi R_TMP, NOTEMP_VALUE ; store NOTEMP_VALUE in SRAM so that PrintTimeAndDate doesn't show this temp sts maxin_therm_out, R_TMP GetTemp_CheckMinIn: ; is temp < min in temp? lds R_TMP, minin_therm_in cp R_TMP2, R_TMP brlo GetTemp_CheckMaxOut ; overwrite max in temp ldi R_OVERFLOW, therm_out ldi R_TMP, minin_therm_out rcall CopyData ldi R_TMP, NOTEMP_VALUE ; store NOTEMP_VALUE in SRAM so that PrintTimeAndDate doesn't show this temp sts minin_therm_out, R_TMP GetTemp_CheckMaxOut: ; is temp > max in temp? lds R_TMP2, therm_out lds R_TMP, maxout_therm_out cp R_TMP, R_TMP2 brlo GetTemp_CheckMinOut ; overwrite max out temp ldi R_OVERFLOW, therm_out ldi R_TMP, maxout_therm_out rcall CopyData ldi R_TMP, NOTEMP_VALUE ; store NOTEMP_VALUE in SRAM so that PrintTimeAndDate doesn't show this temp sts maxout_therm_in, R_TMP GetTemp_CheckMinOut: ; is temp < min in temp? lds R_TMP, minout_therm_out cp R_TMP2, R_TMP brlo GetTemp_Ret ; overwrite max in temp ldi R_OVERFLOW, therm_out ldi R_TMP, minout_therm_out rcall CopyData ldi R_TMP, NOTEMP_VALUE ; store NOTEMP_VALUE in SRAM so that PrintTimeAndDate doesn't show this temp sts minout_therm_in, R_TMP GetTemp_Ret: ret ; ----- check if it's sync time (So. 4:00:50 h), result in R_TMP (1 if sync time, 0 otherwiese) ----- CheckSyncTime: lds R_TMP2, t_s cpi R_TMP2, SYNCTIME_S brne CheckSyncTime_False lds R_TMP2, t_m cpi R_TMP2, SYNCTIME_M brne CheckSyncTime_False lds R_TMP2, t_h cpi R_TMP2, SYNCTIME_H brne CheckSyncTime_False lds R_TMP2, d_w cpi R_TMP2, SYNCTIME_W brne CheckSyncTime_False CheckSyncTime_True: ldi R_TMP, 1 ret CheckSyncTime_False: ldi R_TMP, 0 ret ; ----- start of main program ----- Demo: rcall ClearCurrentTimeAndTemp rcall Reset_Temp DemoLoop: rcall ClockTick rcall PrintTimeAndDate rcall Delay5ms rjmp DemoLoop Start: rcall ScanTime rcall GetTemp rcall Reset_Temp MainLoop: rcall ClockTick rcall WaitForTick rcall PrintTimeAndDate ; check if 04:00:50 sun rcall CheckSyncTime sbrc R_TMP, 0 rcall ScanTime ; check if ??:??:00 lds R_TMP, t_s cpi R_TMP, 0 brne MainLoop rcall GetTemp rjmp MainLoop