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