329 lines
8.8 KiB
NASM
329 lines
8.8 KiB
NASM
; ***************************************************************************
|
|
; copyright : (C) 2023 by Martin Preuss
|
|
; email : martin@libchipcard.de
|
|
;
|
|
; ***************************************************************************
|
|
; * This file is part of the project "AqHome". *
|
|
; * Please see toplevel file COPYING of that project for license details. *
|
|
; ***************************************************************************
|
|
|
|
|
|
|
|
|
|
|
|
; ***************************************************************************
|
|
; defs
|
|
|
|
.equ TIMER_FLAGS_100MS = 1
|
|
.equ TIMER_FLAGS_1S = 2
|
|
|
|
|
|
|
|
|
|
; ***************************************************************************
|
|
; data
|
|
|
|
.dseg
|
|
|
|
timerModuleData:
|
|
timerModuleTickCounter: .byte 1
|
|
timerModuleFlags: .byte 1
|
|
timerModuleCounterSecs: .byte 4
|
|
|
|
timerModuleDerivedCounters: .byte 10 ; must correspond to entries in timer table!
|
|
|
|
timerModuleData_end:
|
|
|
|
|
|
|
|
|
|
; ***************************************************************************
|
|
; code
|
|
|
|
.cseg
|
|
|
|
TIMER_BEGIN:
|
|
|
|
|
|
; number of entries here must be equal to bytes in timerModuleDerivedCounters!
|
|
timerTable:
|
|
.db 5, 0, LOW(onEvery5s), HIGH(onEvery5s) ; 5s : overflow after 5*1s (1)
|
|
.db 2, 0, LOW(onEvery10s), HIGH(onEvery10s) ; 10s: overflow after 2*5s (2)
|
|
.db 3, 0, LOW(onEvery30s), HIGH(onEvery30s) ; 30s: overflow after 3*10s (3)
|
|
.db 2, 0, LOW(onEvery1m), HIGH(onEvery1m) ; 1m : overflow after 2*30s (4)
|
|
.db 5, 0, LOW(onEvery5m), HIGH(onEvery5m) ; 5m : overflow after 5*1m (5)
|
|
.db 3, 0, LOW(onEvery15m), HIGH(onEvery15m) ; 15m: overflow after 3*5m (6)
|
|
.db 2, 0, LOW(onEvery30m), HIGH(onEvery30m) ; 30m: overflow after 2*15m (7)
|
|
.db 2, 0, LOW(onEvery1h), HIGH(onEvery1h) ; 1h: overflow after 2*30m (8)
|
|
.db 12, 0, LOW(onEvery12h), HIGH(onEvery12h) ; 12h: overflow after 12*1h (9)
|
|
.db 2, 0, LOW(onEvery1d), HIGH(onEvery1d) ; 1d: overflow after 2*12h (10)
|
|
.db 0, 0
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; Timer_Init
|
|
;
|
|
; IN:
|
|
; - nothing
|
|
; OUT:
|
|
; - nothing
|
|
; USED: r16, r17, x
|
|
|
|
Timer_Init: ; setup timer for 15.2588 interrupts per second (e.g. every 65.5 ms)
|
|
; reset data in SDRAM
|
|
ldi xh, HIGH(timerModuleData)
|
|
ldi xl, LOW(timerModuleData)
|
|
ldi r16, 0
|
|
ldi r17, (timerModuleData_end-timerModuleData)
|
|
rcall Utils_FillSram
|
|
|
|
; CTC mode (WGM2:0=2, OCR0A=value, OCF0A Flag =1, -> IRQ_OC0A
|
|
|
|
; CMP-A interrupt about every 100ms
|
|
ldi r16, (1<<CS02) | (1<<CS00) ; Prescaler 1024
|
|
out TCCR0B, r16
|
|
|
|
ldi r16, (1<<WGM01) ; CTC mode
|
|
out TCCR0A, r16
|
|
|
|
ldi r16, 98-1 ; (1,000,000/1024)/10 = 97.65625
|
|
out OCR0A, r16
|
|
|
|
ldi r16, (1<<OCF0A) ; clear pending interrupts
|
|
out TIFR0, r16
|
|
|
|
ldi r16, (1<<OCIE0A)
|
|
out TIMSK0, r16
|
|
|
|
ret
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; Timer_Fini
|
|
;
|
|
; IN:
|
|
; - nothing
|
|
; OUT:
|
|
; - nothing
|
|
; USED:
|
|
|
|
Timer_Fini:
|
|
ret
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; Timer_Run
|
|
;
|
|
; IN:
|
|
; - nothing
|
|
; OUT:
|
|
; - CFLAG: set if something done, cleared otherwise
|
|
; USED: r15, r16, r17, R19 (more depending on called routines)
|
|
|
|
Timer_Run:
|
|
in r15, SREG
|
|
cli
|
|
lds r17, timerModuleFlags ; get timer flags
|
|
clr r16 ; replace with 0 for next IRQ
|
|
sts timerModuleFlags, r16
|
|
mov r16, r17
|
|
andi r16, TIMER_FLAGS_100MS ; timer irq occurred?
|
|
brne Timer_Run_100ms_passed ; yes, go handle
|
|
out SREG, r15 ; restore global IRQ flag
|
|
clc ; nothing done
|
|
ret
|
|
Timer_Run_100ms_passed:
|
|
mov r16, r17
|
|
andi r16, TIMER_FLAGS_1S
|
|
brne Timer_Run_1s_passed
|
|
out SREG, r15 ; restore global IRQ flag
|
|
rcall onEvery100ms ; only call this one
|
|
sec
|
|
ret
|
|
Timer_Run_1s_passed:
|
|
rcall timerIncrementCounters ; 1s passed, handle cascading counters, get number of callable handlers
|
|
out SREG, r15 ; restore global IRQ flag
|
|
rcall timerCallFirstOnEveryHandlers
|
|
sec
|
|
ret
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; Increment cascading timer counters.
|
|
;
|
|
; This routine starts with the first cascaded counter and increments it.
|
|
; If that counter value does not overflow (according to timer table entry) then
|
|
; handling the timer table is stopped.
|
|
; Otherwise the return value is incremented and the next counter entry is inspected
|
|
; until the full table is handled.
|
|
;
|
|
; The result value has to be interpreted as the number of entries started at the first
|
|
; which have overflowed.
|
|
;
|
|
; Example: A return value of 1 means that the first entry overflowed, meaning that 10s have
|
|
; elapsed.
|
|
; A return value of 2 means that the first entry overflowed (i.e. 10s elapsed) and also the nex
|
|
; entry overflowed (i.e. also 30s elapsed).
|
|
;
|
|
; IN:
|
|
; - nothing
|
|
; OUT:
|
|
; - R16: number of timer table entries which caused an overflow
|
|
; REGS: R16, R17, R18, X, Z
|
|
|
|
timerIncrementCounters:
|
|
ldi xl, LOW(timerModuleDerivedCounters) ; points to first counter: timerModuleCounterFor10s
|
|
ldi xh, HIGH(timerModuleDerivedCounters)
|
|
ldi zl, LOW(timerTable*2)
|
|
ldi zh, HIGH(timerTable*2)
|
|
|
|
clr r18
|
|
|
|
timerIncrementCounters_loop:
|
|
lpm r17, Z ; overflow value
|
|
tst r17
|
|
breq timerIncrementCounters_done ; end of list
|
|
ld r16, X ; get current counter value
|
|
inc r16
|
|
cp r16, r17 ; compare against overflow value
|
|
brcs timerIncrementCounters_counterSmaller
|
|
clr r16 ; overflow occurred
|
|
st X+, r16
|
|
; next
|
|
inc r18
|
|
adiw zh:zl, 4
|
|
rjmp timerIncrementCounters_loop
|
|
|
|
timerIncrementCounters_counterSmaller:
|
|
st X, r16
|
|
timerIncrementCounters_done:
|
|
mov r16, r18
|
|
ret
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; Call first N handlers from the timer table.
|
|
;
|
|
; IN:
|
|
; - R16: number of handlers to call (returned by timerIncrementCounters)
|
|
; OUT:
|
|
; - nothing
|
|
; REGS: depening on handler called (expect all registers to be clobbered)
|
|
|
|
timerCallFirstOnEveryHandlers:
|
|
push r16
|
|
rcall onEvery100ms ; always call this one
|
|
rcall onEverySecond ; and this
|
|
pop r16
|
|
|
|
tst r16
|
|
breq timerCallFirstOnEveryHandlers_done
|
|
|
|
ldi zl, LOW(timerTable*2)
|
|
ldi zh, HIGH(timerTable*2)
|
|
ldi xl, LOW(timerModuleDerivedCounters) ; points to first counter: timerModuleCounterFor10s
|
|
ldi xh, HIGH(timerModuleDerivedCounters)
|
|
|
|
timerCallFirstOnEveryHandlers_loop:
|
|
lpm r17, Z ; overflow value
|
|
tst r17 ; 0?
|
|
breq timerCallFirstOnEveryHandlers_done ; yes: end of list
|
|
adiw zh:zl, 2 ; skip to handler address
|
|
lpm r20, Z+ ; read handler address
|
|
lpm r21, Z+
|
|
push r16 ; call handler
|
|
push xl
|
|
push xh
|
|
push zl
|
|
push zh
|
|
ld r16, X+ ; current counter value
|
|
rcall timerCallR21R20
|
|
pop zh
|
|
pop zl
|
|
pop xh
|
|
pop xl
|
|
pop r16
|
|
dec r16
|
|
brne timerCallFirstOnEveryHandlers_loop
|
|
|
|
timerCallFirstOnEveryHandlers_done:
|
|
ret
|
|
|
|
|
|
timerCallR21R20:
|
|
push r20
|
|
push r21
|
|
ret
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; OC0A interrupt handler
|
|
;
|
|
; Called every 100 milliseconds, sets flag TIMER_FLAGS_100MS.
|
|
; Increments an internal counter. If that counter reaches 10 the seconds counter is incremented and a the flag
|
|
; TIMER_FLAGS_1S is set.
|
|
|
|
timerIrqOC0A:
|
|
push r15
|
|
in r15, SREG
|
|
|
|
push r16
|
|
push r17
|
|
lds r17, timerModuleFlags
|
|
ori r17, TIMER_FLAGS_100MS
|
|
lds r16, timerModuleTickCounter
|
|
inc r16
|
|
cpi r16, 10
|
|
brcc timerIrq2_l1
|
|
sts timerModuleTickCounter, r16
|
|
rjmp timerIrq2_l2
|
|
timerIrq2_l1: ; increment seconds
|
|
; reset 100ms counter
|
|
clr r16
|
|
sts timerModuleTickCounter, r16
|
|
; increment seconds
|
|
push xh
|
|
push xl
|
|
push r18
|
|
ldi xh, HIGH(timerModuleCounterSecs)
|
|
ldi xl, LOW(timerModuleCounterSecs)
|
|
ldi r18, 1
|
|
ld r16, X ; byte 0
|
|
adc r16, r18 ; inc by 1
|
|
dec r18 ; doesn't affect carry flag! (r18 becomes 0 for following ADC)
|
|
st X+, r16
|
|
ld r16, X ; byte 1
|
|
adc r16, r18
|
|
st X+, r16
|
|
ld r16, X ; byte 2
|
|
adc r16, r18
|
|
st X+, r16
|
|
ld r16, X ; byte 3
|
|
adc r16, r18
|
|
st X+, r16
|
|
pop r18
|
|
pop xl
|
|
pop xh
|
|
ori r17, TIMER_FLAGS_1S
|
|
timerIrq2_l2:
|
|
sts timerModuleFlags, r17 ; store new timer flags
|
|
pop r17
|
|
pop r16
|
|
out SREG, r15
|
|
pop r15
|
|
reti
|
|
|
|
|
|
|
|
TIMER_END:
|
|
.equ MODULE_SIZE_TIMER = TIMER_END-TIMER_BEGIN
|
|
|
|
|
|
|