Files
aqhomecontrol/avr/modules/timer/main.asm

334 lines
6.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_IF_ADDR = 1
; ***************************************************************************
; data
.dseg
timerModuleData:
timerModuleTickCounter: .byte 1
timerTicksSinceLastRun: .byte 2
timerModuleCounterSecs: .byte 4
timerInterrupts: .byte 2
timerModuleData_end:
; ***************************************************************************
; code
.cseg
TIMER_BEGIN:
; ---------------------------------------------------------------------------
; Timer_Init
;
; IN:
; - nothing
; OUT:
; - nothing
; USED: r16, r17, x
Timer_Init: ; setup timer for IRQ every 100ms
; 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
rcall timerInitTimers
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, r24, r25 (more depending on called routines)
Timer_Run:
in r15, SREG
cli
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 timerRunTimers
rcall onEvery100ms
pop r25
pop r24
lds r16, timerModuleTickCounter
inc r16
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
; ---------------------------------------------------------------------------
; Set timer value.
;
; Setting a timer to 0 effectively stops the timer.
;
; IN:
; - r16: new timer value (low)
; - r17: new timer value (high)
; - X : pointer to timer value in SRAM
; OUT:
; - nothing
; REGS: X
Timer_SetValue:
push r15
in r15, SREG
cli
st X+, r16
st X+, r17
out SREG, r15
pop r15
ret
; ---------------------------------------------------------------------------
; Set timer value to 1s.
;
; IN:
; - X : pointer to timer value in SRAM
; OUT:
; - nothing
; REGS: X
Timer_SetValueTo1s:
push r16
push r17
ldi r16, 10
clr r17
rcall Timer_SetValue
pop r17
pop r16
ret
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
mov r18, r20
or r18, r21
breq timerInitTimers_writeInitial
add r20, r16 ; add counter pos in table so that not all timers elapse at the same time
adc r21, r17
timerInitTimers_writeInitial:
st X+, r20
st X, r21
inc r16
inc r16
rjmp timerInitTimers_loop
timerInitTimers_end:
ret
timerRunTimers:
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, com2Address ; 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
; ---------------------------------------------------------------------------
; read time table entry.
;
; IN:
; - Z : pointer to time list entry (suitable for LPM)
; OUT:
; - 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
timerCallR19R18:
push r18
push r19
ret
; ---------------------------------------------------------------------------
; OC0A interrupt handler
;
; Called every 100 milliseconds, increments timerTicksSinceLastRun. The rest is done outside ISR
; in Timer_Run.
timerIrqOC0A:
push r15
in r15, SREG
push r24
push r25
lds r24, timerTicksSinceLastRun
lds r25, timerTicksSinceLastRun+1
adiw r25:r24, 1
sts timerTicksSinceLastRun, r24
sts timerTicksSinceLastRun+1, r25
lds r24, timerInterrupts
lds r25, timerInterrupts+1
adiw r25:r24, 1
sts timerInterrupts, r24
sts timerInterrupts+1, r25
pop r25
pop r24
out SREG, r15
pop r15
reti
TIMER_END:
.equ MODULE_SIZE_TIMER = TIMER_END-TIMER_BEGIN