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