avr: reworked timer code.

Allow for more tailored timers.
This commit is contained in:
Martin Preuss
2023-04-10 01:00:58 +02:00
parent d26f500bba
commit 84f11398b8
3 changed files with 179 additions and 285 deletions

View File

@@ -14,9 +14,7 @@
; ***************************************************************************
; defs
.equ TIMER_FLAGS_100MS = 1
.equ TIMER_FLAGS_1S = 2
.equ TIMER_FLAGS_IF_ADDR = 1
@@ -27,11 +25,9 @@
timerModuleData:
timerModuleTickCounter: .byte 1
timerModuleFlags: .byte 1
timerTicksSinceLastRun: .byte 2
timerModuleCounterSecs: .byte 4
timerModuleDerivedCounters: .byte 10 ; size must be number of entries in timer table
timerModuleData_end:
@@ -45,21 +41,6 @@ timerModuleData_end:
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
@@ -96,6 +77,8 @@ Timer_Init: ; setup timer for IRQ every 100ms
ldi r16, (1<<OCIE0A)
out TIMSK0, r16
rcall timerInitTimers
ret
@@ -121,136 +104,140 @@ Timer_Fini:
; - nothing
; OUT:
; - CFLAG: set if something done, cleared otherwise
; USED: r15, r16, r17, R19 (more depending on called routines)
; USED: r15, r16, r24, r25 (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. Called once every second.
;
; 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
lds r24, timerTicksSinceLastRun
lds r25, timerTicksSinceLastRun+1
clr r16 ; replace with 0 for next IRQ
sts timerTicksSinceLastRun, r16
sts timerTicksSinceLastRun+1, r16
out SREG, r15 ; restore global IRQ flag
sbiw r25:r24, 0
clc ; flag "nothing done"
breq Timer_Run_End
Timer_Run_loop: ; for every occurred 100ms irq
push r24
push r25
rcall onEvery100ms
pop r25
pop r24
lds r16, timerModuleTickCounter
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
cpi r16, 10
brcc Timer_Run_SecondElapsed
sts timerModuleTickCounter, r16
rjmp Timer_Run_loop_end
Timer_Run_SecondElapsed:
clr r16
sts timerModuleTickCounter, r16
push r24
push r25
rcall timerRunTimers
pop r25
pop r24
Timer_Run_loop_end:
sbiw r25:r24, 1
brne Timer_Run_loop
sec
Timer_Run_End:
ret
timerIncrementCounters_counterSmaller:
st X, r16
timerIncrementCounters_done:
mov r16, r18
timerInitTimers:
ldi zl, LOW(timerList*2)
ldi zh, HIGH(timerList*2)
clr r16 ; run var for pos in time table
clr r17 ; 0 for adc
timerInitTimers_loop:
rcall timerReadTableEntry
mov r18, xl
or r18, xh
breq timerInitTimers_end
add r20, r16 ; add counter pos in table so that not all timers elapse at the same time
adc r21, r17
st X+, r20
st X, r21
inc r16
rjmp timerInitTimers_loop
timerInitTimers_end:
ret
timerRunTimers:
; TODO: incremenent uptime counter
ldi xl, LOW(timerModuleCounterSecs)
ldi xh, HIGH(timerModuleCounterSecs)
rcall Utils_IncrementCounter32
ldi zl, LOW(timerList*2)
ldi zh, HIGH(timerList*2)
timerRunTimers_loop:
rcall timerReadTableEntry
mov r16, xl
or r16, xh
breq timerRunTimers_end
mov r16, r22
andi r16, TIMER_FLAGS_IF_ADDR
breq timerRunTimers_l1 ; no need to check address
lds r16, comAddress ; check address
tst r16
breq timerRunTimers_loop ; no address, ignore counter
timerRunTimers_l1:
ld r24, X+
ld r25, X
sbiw r25:r24, 1
brcs timerRunTimers_loop ; overflow, so already was zero, ignore entry
breq timerRunTimers_reachedZero ; reached zero
st X, r25
st -X, r24
rjmp timerRunTimers_loop
timerRunTimers_reachedZero:
st X, r21 ; reset initial value
st -X, r20
push zl
push zh
rcall timerCallR19R18
pop zh
pop zl
rjmp timerRunTimers_loop
timerRunTimers_end:
ret
; ---------------------------------------------------------------------------
; Call first N handlers from the timer table.
; read time table entry.
;
; IN:
; - R16: number of handlers to call (returned by timerIncrementCounters)
; - Z : pointer to time list entry (suitable for LPM)
; 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)
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 zl
push zh
rcall timerCallR21R20
pop zh
pop zl
pop r16
dec r16
brne timerCallFirstOnEveryHandlers_loop
timerCallFirstOnEveryHandlers_done:
; - r19:r18: handler routine
; - X : SRAM address for counter
; - r21:r20: initial value
; - r22 : flags
; - r23 : reserved
timerReadTableEntry:
lpm xl, Z+ ; SRAM address of counter
lpm xh, Z+
lpm r18, Z+ ; routine (low)
lpm r19, Z+ ; routine (high)
lpm r22, Z+ ; flags
lpm r23, Z+ ; reserved
lpm r20, Z+ ; initial value
lpm r21, Z+
ret
timerCallR21R20:
push r20
push r21
timerCallR19R18:
push r18
push r19
ret
@@ -258,56 +245,22 @@ timerCallR21R20:
; ---------------------------------------------------------------------------
; 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.
; Called every 100 milliseconds, increments timerTicksSinceLastRun. The rest is done outside ISR
; in Timer_Run.
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
push r24
push r25
lds r24, timerTicksSinceLastRun
lds r25, timerTicksSinceLastRun+1
adiw r25:r24, 1
sts timerTicksSinceLastRun, r24
sts timerTicksSinceLastRun+1, r25
pop r25
pop r24
out SREG, r15
pop r15
reti