/******************************************************************************!
 * \file timer1.h
 * \author Sebastien Beaugrand
 * \sa http://beaugrand.chez.com/
 * \copyright CeCILL 2.1 Free Software license
 ******************************************************************************/
#ifndef AVR_TIMER1
#define AVR_TIMER1

#if defined(__arm__) || defined(__aarch64__)
# include <sys/time.h>
# include <signal.h>
# include <errno.h>

void
timer1SetPeriod(unsigned long microseconds)
{
    struct itimerval it = { { 0, microseconds }, { 0, microseconds } };
    if (setitimer(ITIMER_REAL, &it, 0) != 0) {
        perror("setitimer");
    }
}

void (* gIsr)() = NULL;

void
sigalarm_handler(int s) {
    s = s;
    if (gIsr == NULL) {
        perror("gIsr == NULL");
    }
    (*gIsr)();
}

void
timer1AttachInterrupt(void (* isr)())
{
    struct sigaction sa;

    if (gIsr != NULL) {
        perror("gIsr != NULL");
    }
    gIsr = isr;

    sa.sa_handler = sigalarm_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    if (sigaction(SIGALRM, &sa, NULL) != 0) {
        perror("sigaction");
    }
}

#else

#if defined (__AVR_ATtiny2313__)
# define TIMER1_RESOLUTION 65536UL
#else  // tinyX5
# define TIMER1_RESOLUTION 256UL
#endif

/******************************************************************************!
 * \fn timer1SetPeriod
 ******************************************************************************/
void
timer1SetPeriod(unsigned long microseconds)
{
    uint8_t prescaler;
    uint32_t ocr;

#   if defined (__AVR_ATtiny2313__)
    for (prescaler = 1; prescaler < 6; ++prescaler) {
#   else  // tinyX5
    for (prescaler = 1; prescaler < 16; ++prescaler) {
#   endif
#       if defined (__AVR_ATtiny2313__)
        // Clock div: 1 8 64 256 1024
        // Shift: 0 3 6 8 10
        ocr = prescaler << 1;
        if (ocr == 2) {
            ocr = 0;
        } else if (ocr == 4) {
            ocr = 3;
        }
        ocr = ((F_CPU / 1000000 * microseconds) >> ocr) - 1;
#       else  // tinyX5
        ocr = ((F_CPU / 1000000 * microseconds) >> (prescaler - 1)) - 1;
#       endif
        if (ocr <= TIMER1_RESOLUTION) {
            break;
        }
    }
    if (ocr >= TIMER1_RESOLUTION) {
        ocr = TIMER1_RESOLUTION - 1;
    }

    OCR1A = ocr;
#   if defined (__AVR_ATtiny2313__)
    TCCR1A = 0;
    TCCR1B = (TCCR1B & ~((1 << CS12) | (1 << CS11) | (1 << CS10))) | prescaler;
#   else  // tinyX5
    TCCR1 = (TCCR1 & ~((1 << CS13) | (1 << CS12) |
                       (1 << CS11) | (1 << CS10))) | prescaler;
#   endif

    TCNT1 = 0;
    TIMSK |= (1 << OCIE1A);
}

#endif

#endif