#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;
}
void timer1_start (short cycles)
{
// count up from zero...
TCNT1 = 0;
// ...to the given number of timer cycles
OCR1A = cycles;
// start!
sbi(&TIMER_FLAGS, TIMER1_BUSY);
TIMSK1 = (
// OCA interrupt
0b1 << OCIE1A // enable
);
TCCR1B = (
// WGM
0b01 << WGM12 // CTC
// clocksource
| 0b101 << CS10 // 1024'th
);
}
static void timer1_stop ()
{
// WGM: normal
// clocksource: stop
TCCR1B = 0;
cbi(&TIMER_FLAGS, TIMER1_BUSY);
}
ISR(TIMER1_COMPA_vect)
{
timer1_stop();
// XXX: cpu will automatically wake up from sleep()
}
/*
* Sleep on timer1 interrupt.
*
* Returns 0 on interrupt, 1 on timeout.
*/
char timer_sleep (int cycles)
{
if (cycles) {
// set timer
timer1_start(cycles);
}
// sleep
// TODO: PRR
SMCR = (
// idle sleep
(0b00 << SM0)
// enable sleep
| (0b1 << SE)
);
// sleep
__asm__ ( "sleep" :: );
// cleanup
SMCR = 0;
if (cycles && !tbi(&TIMER_FLAGS, TIMER1_BUSY)) {
// timeout
return 1;
} else {
timer1_stop();
// interrupt
return 0;
}
}