--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/timer.h Wed Sep 24 22:30:30 2014 +0300
@@ -0,0 +1,21 @@
+#ifndef QMSK_ARDUINO_TIMER_H
+#define QMSK_ARDUINO_TIMER_H
+
+#define TIMER_FLAGS GPIOR0
+#define TIMER1_BUSY 1
+
+/*
+ * Setup timers.
+ */
+void timer_init (void);
+
+/*
+ * 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.
+ */
+byte timer_sleep (unsigned cycles);
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/timer.c Wed Sep 24 22:30:30 2014 +0300
@@ -0,0 +1,122 @@
+#include <avr/interrupt.h>
+
+#include "stdlib.h"
+#include "timer.h"
+
+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);
+}
+
+static 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.
+ */
+byte 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;
+ }
+}