src/timer.c
author Tero Marttila <terom@paivola.fi>
Sun, 20 Apr 2014 22:47:41 +0300
changeset 78 504c2310cddb
parent 63 e9b478c817df
permissions -rw-r--r--
timer: continuous interval-sleep operation
#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;
    }
}