#ifndef __AVRTIME_H__
#define __AVRTIME_H__

#include <avr/io.h>
#include <avr/power.h>
#include "avrutil.h"

/*****************************************
 Time handling

 Main timekeeping variable is an uint32_t (in timer ticks)
 this gives up to 4294.967296 s, = 1h 11min 34s ...
 with 8MHz internal clock, no system clock prescaler and timer prescaler 2^3 = 8
*/

extern union lwb time_union;
#define time_current (time_union.t32)
#define time_low  (time_union.t16[0])
#define time_high (time_union.t16[1])

/***** Clock frequencies **
 we only consider clock frequencies which can be expressed as an uint32_t

  cpu frequency = time_sysosc / 2^time_syspsc;
  timer freq.   = cpu freq. / 2^time_tpsc
or
  timer freq. = time_sysosc >> (timesyspsc + time_tpsc) // except for truncations

  0 <= time_syspsc <=  8 (i.e. divide by 1 ..  256)
  0 <= time_tpsc   <= 10 (i.e. divide by 1 .. 1024)

  prescaler total range 0 .. 18 (1 .. 262144)

  32768 kHz (or lower) <= osc freq. <= 20MHz

  which gives us a timer tick from 50ns to 8s (or more)
*/

extern uint32_t time_sysosc;  // system clock freq., if you use a crystal, put its freq. into this, else set by time_fcpu()
extern uint8_t  time_syspsc;  // system prescaler, set by time_fcpu()
//extern uint8_t  time_tnum;    // which timer to user for our "wall clock", set by time_init()
extern uint8_t  time_tpsc;    // it's prescaler (timer freq. = cpu freq. / 2^(this value), set by time_init()

// return cpu frequency, like a dynamic version of F_CPU from avr-libc
//  0 == unknow frequency, >0 == cpu frequency in Hz
uint32_t time_fcpu(void);

// returns timer prescaling (-1 = unknown source freq. or stopped timer), i.e. the x in 2^x
int8_t time_getpsc(uint8_t which_timer, uint8_t *mask, uint8_t *mask_sz);

// 8 or 16bit timers, like atmega168 timer1, attiny861 timer0
#define TIME_CLKPS_NIL   0x00 
#define TIME_CLKPS_1     0x01  // divide by 2^0
#define TIME_CLKPS_8     0x02  // divide by 2^3
#define TIME_CLKPS_64    0x03  // divide by 2^6
#define TIME_CLKPS_256   0x04  // divide by 2^8
#define TIME_CLKPS_1024  0x05  // divide by 2^10
#define TIME_CLKPS_EXTF  0x06
#define TIME_CLKPS_EXTR  0x07

/*
  sysclk_ps
    system clock prescaler (if available)
    the current clocksource is devided with 2^sysclk_ps
    e.g. atmega168 with internal 8MHz clock and sysclk_ps = 3, the cpu will run at 1MHz

  timer_ps
    timer prescaler
    the io clock (same freq as cpu clock) is diveded by the timer prescalar
    use one of the values above (TIME_CLKPS_xx)

  both values are silently bounded to allowable values
*/

void time_init(clock_div_t sysclk_ps, uint8_t timer_ps);
void time_reset(void);
void time_get(void);

uint32_t time_clk2us(uint32_t cnt);
uint32_t time_us2clk(uint32_t us);

/*****************************************
 define which timer you'd use for time keeping, default is 1 (works for atmega168

 */

// how many timers do we have here, might be useful
#if   defined(TCCR5A)
#define AVRTIME_SZ 6
#elif defined(TCCR4A)
#define AVRTIME_SZ 5
#elif defined(TCCR3A)
#define AVRTIME_SZ 4
#elif defined(TCCR2A)
#define AVRTIME_SZ 3
#elif defined(TCCR1A)
#define AVRTIME_SZ 2
#elif defined(TCCR0A)
#define AVRTIME_SZ 1
#else
#define AVRTIME_SZ 0
#endif

#if defined(__AVR_ATxmega16A4__) \
|| defined(__AVR_ATxmega16D4__) \
|| defined(__AVR_ATxmega32A4__) \
|| defined(__AVR_ATxmega32D4__) \
|| defined(__AVR_ATxmega64A1__) \
|| defined(__AVR_ATxmega64A3__) \
|| defined(__AVR_ATxmega64D3__) \
|| defined(__AVR_ATxmega128A1__) \
|| defined(__AVR_ATxmega128A3__) \
|| defined(__AVR_ATxmega128D3__) \
|| defined(__AVR_ATxmega192A3__) \
|| defined(__AVR_ATxmega192D3__) \
|| defined(__AVR_ATxmega256D3__) \
|| defined(__AVR_ATxmega256A3__) \
|| defined(__AVR_ATxmega256A3B__)

#warning Not implemented yet

#elif defined(__AVR_ATmega48__) \
|| defined(__AVR_ATmega48A__) \
|| defined(__AVR_ATmega48P__) \
|| defined(__AVR_ATmega88__) \
|| defined(__AVR_ATmega88A__) \
|| defined(__AVR_ATmega88P__) \
|| defined(__AVR_ATmega88PA__) \
|| defined(__AVR_ATmega168__) \
|| defined(__AVR_ATmega168A__) \
|| defined(__AVR_ATmega168P__) \
|| defined(__AVR_ATmega328__) \
|| defined(__AVR_ATmega328P__) \
|| defined(__AVR_ATtiny48__) \
|| defined(__AVR_ATtiny88__)

#define AVRTIME_TIMER 1
#define AVRTIME_TIFR  TIFR1
#define AVRTIME_INIT   
#define AVRTIME_RESET GTCCR = _BV(PSRSYNC)

#elif defined(__AVR_ATtiny24__) \
|| defined(__AVR_ATtiny24A__) \
|| defined(__AVR_ATtiny44__) \
|| defined(__AVR_ATtiny44A__) \
|| defined(__AVR_ATtiny84__) \
|| defined(__AVR_ATtiny25__) \
|| defined(__AVR_ATtiny45__) \
|| defined(__AVR_ATtiny85__) \
|| defined(__AVR_ATtiny261__) \
|| defined(__AVR_ATtiny261A__) \
|| defined(__AVR_ATtiny461__) \
|| defined(__AVR_ATtiny461A__) \
|| defined(__AVR_ATtiny861__) \
|| defined(__AVR_ATtiny861A__) \
|| defined(__AVR_ATtiny43U__)

#define AVRTIME_TIMER 0
#define AVRTIME_TIFR  TIFR
#define AVRTIME_INIT  TCCR0A = _BV(TCW0)  // set to 16bit mode
#define AVRTIME_RESET TCCR0B |= _BV(PSR0)

#endif

#define glue3(a,b,c) a ## b ## c
#define xglue3(a,b,c) glue3(a,b,c)
#define glue2(a,b) a ## b
#define xglue2(a,b) glue2(a,b)

//#define TCCR_A xglue3(TCCR, AVRTIME_TIMER, A)
//#define TCCR_B xglue3(TCCR, AVRTIME_TIMER, B)
//#define TCCR_C xglue3(TCCR, AVRTIME_TIMER, C)
#define TCNT_H xglue3(TCNT, AVRTIME_TIMER, H)
#define TCNT_L xglue3(TCNT, AVRTIME_TIMER, L)
#define TOV_   xglue2(TOV , AVRTIME_TIMER)

#endif
