;; vim: filetype=avr
;;
;; Timer unit control and use
;;
.equ TIMER_FLAGS = GPIOR0
;; Timer0
; Compare output mode
.set TIMER0_COMA = 0b00 ; null
.set TIMER0_COMB = 0b00 ; null
; Waveform Generation Mode (triplet low/high)
.set TIMER0_WGML = 0b10 ; CTC
.set TIMER0_WGMH = 0b0 ; CTC
; Clock Source
.set TIMER0_CS = 0b101 ; 1/1024
;; Timer1
; Waveform Generation Mode (nibble low/high)
.set TIMER1_WGML = 0b00 ; CTC
.set TIMER1_WGMH = 0b01 ; CTC
; Clock Source
.set TIMER1_CS = 0b101 ; 1/1024
; Flags
.equ TIMER1_BUSY = 1
.set SLEEP_MODE = 0b000 ; Idle
Timer_Init:
Timer0_Init:
; OC0A/B disconnected from output
; No PWM mode
ldi r16, (TIMER0_COMA << COM0A0) | (TIMER0_COMB << COM0B0) | (TIMER0_WGML << WGM00)
out TCCR0A, r16
; Clear
ldi r16, 0
out OCR0A, r16
out OCR0B, r16
out TCCR0B, r16
; Enable compare interrupt
ldi r16, (1 << OCIE0A)
sts TIMSK0, r16
Timer1_Init:
; OC1A/B disconnected from output
; No PWM mode
poke [TCCR1A, r16, (0b00 << COM1A0) | (0b00 << COM1B0) | (TIMER1_WGML << WGM10)]
; Clear
poke [TCCR1B, r16, 0]
poke [TCCR1C, r16, 0]
; Enable compare interrupt
poke [TIMSK1, r16, (1 << OCIE1A)]
Sleep_init:
; Select sleep mode
; Enable `sleep`
poke [SMCR, r16, (SLEEP_MODE << SM0) | (1 << SE)]
; Disable ADC
poke [SMCR, r16, (1 << PRTWI) | (1 << PRUSART0) | (1 << PRADC)]
ret
;; Timer0 is recurring; this starts it running, and it keeps hitting OC0A periodically
;; Input: r16 (period, in 1k-cycles)
Timer0_Start:
; Initialize timer
; set CTC trigger from r16
out OCR0A, r16
; clear counter
ldi r16, 0
out TCNT0, r16
; Start
; WGM
; Clock Source
ldi r16, (TIMER0_WGMH << WGM02) | (TIMER0_CS << CS00)
out TCCR0B, r16
ret
Timer0_Read8:
in r16, TCNT0
ret
;; Timer0 Compare A interrupt handler
Timer_OC0A:
in r0, SREG
; Run callback
rcall TIMER0_CB_A
out SREG, r0
reti
;; Timer1 is one-shot; this starts it running, and it is then stopped once it hits OC1A
Timer1_Start:
; Initialize timer
poke [TCNT1H, r16, high(0)]
poke [TCNT1L, r16, low(0)]
; Set flag
sbi TIMER_FLAGS, TIMER1_BUSY
; Start
; WGM
; Clock Source
poke [TCCR1B, r16, (TIMER1_WGMH << WGM12) | (TIMER1_CS << CS10)]
ret
Timer1_Stop:
; WGM
; Clock off
poke [TCCR1B, r16, (TIMER1_WGMH << WGM12) | (0b00 << CS10)]
; Clear flag
cbi TIMER_FLAGS, TIMER1_BUSY
ret
;; Timer1 Compare A interrupt handler
Timer_OC1A:
in r0, SREG
; Stop timer
rcall Timer1_Stop
out SREG, r0
reti
;; Prime the timer and sleep for 1s
Timer_Sleep_1s:
; Initialize counter to 16k cycles
ldi XH, high(16 * 1024)
ldi XL, low(16 * 1024)
;; Continue
;; Count to X
Timer_Sleep:
; Set TOP
sts OCR1AH, XH
sts OCR1AL, XL
; Start timer
rcall Timer1_Start
; Wait for timer to complete
_timer1_sleep:
sleep
sbic TIMER_FLAGS, TIMER1_BUSY
rjmp _timer1_sleep
; Done
ret
;; Update timer for given timeout
Timer_Update:
; Set TOP
sts OCR1AH, XH
sts OCR1AL, XL
; Check timer
lds YL, TCNT1L
lds YH, TCNT1H
cp YL, XL
cpc YH, XH
brlo timer_up_out
; Update
; XXX: figure out a better way to do this...
ldi r16, 0
subi XL, 2
sbc XH, r16
sts TCNT1L, XL
sts TCNT1H, XH
timer_up_out:
; Done
ret