Assembler-ProgrammDie 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