#include <avr/interrupt.h>
#define TIMER_FLAGS GPIOR0
#define TIMER1_BUSY 1
/*
* Setup timers.
*/
void timer_init (void)
{
TCCR1A = (
// OC1A output pin disconnected
0b00 << COM1A0
// OC1B output pin disconnected
| 0b00 << COM1B0
// no PWM
| 0b00 << WGM10
);
TCCR1B = (
// CTC mode
0b01 << WGM12
);
TCCR1C = 0;
}
static void timer1_set ()
{
sbi(&TIMER_FLAGS, TIMER1_BUSY);
}
void timer1_start (short cycles)
{
// count up from zero...
TCNT1 = 0;
// ...to the given number of timer cycles
OCR1A = cycles;
// start!
timer1_set();
TIMSK1 = (
// OCA interrupt
0b1 << OCIE1A // enable
);
TCCR1B = (
// WGM
0b01 << WGM12 // CTC
// clocksource
| 0b101 << CS10 // 1024'th
);
}
/*
* Keep the timer running, but clear the flag..
*/
static void timer1_clear ()
{
cbi(&TIMER_FLAGS, TIMER1_BUSY);
}
/*
* Stop the timer at its current value.
*/
static void timer1_stop ()
{
// WGM: normal
// clocksource: stop
TCCR1B = 0;
timer1_clear();
}
ISR(TIMER1_COMPA_vect)
{
timer1_clear();
// cpu will wake up from sleep()
}
/*
* Sleep on timer1 interrupt.
*
* Starts fresh timer that sleeps given cycles if given, or continues on the running timer.
*
* Returns 1 on timeout, 0 on other interrupt.
*/
char timer_sleep (unsigned cycles)
{
if (cycles) {
// set fresh timer
timer1_start(cycles);
} else {
// wait for next cycle...
timer1_set();
}
// sleep while timer is running
// XXX: atomic?
if (tbi(&TIMER_FLAGS, TIMER1_BUSY)) {
// TODO: PRR
SMCR = (
// idle sleep
(0b00 << SM0)
// enable sleep
| (0b1 << SE)
);
// sleep
__asm__ ( "sleep" :: );
// cleanup
SMCR = 0;
}
if (tbi(&TIMER_FLAGS, TIMER1_BUSY)) {
// interrupt
return 0;
} else {
// timeout
return 1;
}
}