//----------------------------------------------------------------------------------
// <copyright file="timer.c" company="LiquidRobotics">
//	Copyright (c) Liquid Robotics Corporation.  All rights reserved.
// </copyright>
//
// <summary>
// 	Implmentation of the timer tick ISR and for reading accumulated time (uses
//	timer/counters 1 and 2)
// </summary>
//
// <owner>Mike Cookson</owner>
//---------------------------------------------------------------------------------

#include "includes.h"
#include <util/atomic.h>
#include <avr/power.h>
#include <avr/interrupt.h>
#include "timer.h"
#include "uart.h"

time_t	timeTick = 0;
uint32_t secondTick = 0;
int8_t	rtcTick = 0;
const int _ytab[2][12] = { {31,28,31,30,31,30,31,31,30,31,30,31}, {31,29,31,30,31,30,31,31,30,31,30,31} };

/*PAGE*/
/*
 *******************************************************************************
 *                  SETS THE DEFAULT CONFIGURATION FOR TIMERS
 *
 * Description:			Places the timers used in default state (powered off)
 *
 * Arguments:			n/a
 *
 * Returns:				n/a	
 *
 * Notes:				This function should be called by the most generic app
 *						initialization at startup; later, OStmrInit() will be
 *						called, which will enable timers and start the task
 *						switching.
 *******************************************************************************
 */
void OStmrDefault(void)
{
	power_timer1_disable();			// turn off power to timer/counter 1 (used as a general purpose counter)
	power_timer2_disable();			// turn off power to timer/counter 2 (used for the timer ISR)
}

/*PAGE*/
/*
 *******************************************************************************
 *                 POWERS THE TIMERS ON AND STARTS TASK SWITCHING
 *
 * Description:			Powers timers on and starts task switching
 *
 * Arguments:			n/a
 *
 * Returns:				n/a	
 *
 * Notes:				This function should be called by the first task to
 *						execute ** prior ** to relying on delay functions or
 *						the general-purpose timer/counter
 *******************************************************************************
 */
void OStmrInit(void)
{
	// using timer1 for general purpose counter/timer
	// using timer2 for counter/timer interrupts

	OS_CPU_SR cpu_sr;
	OS_ENTER_CRITICAL();

//JGK	power_timer1_enable();			// turn on power to timer 1 (used as general purpose counter)
	power_timer3_enable();			// turn on power to timer 1 (used as general purpose counter)
    power_timer2_enable();			// turn on power to timer 2 (used for timer ISR)

//JGK	TCCR1A = 0;						// normal mode counter
//JGK	TCCR1B = _BV(CS12);				// clock divisor is 256 (calculated not to overflow in 2 seconds)
//JGK	TCCR1C = 0;
//JGK	TCNT1H = 0;
//JGK	TCNT1L = 0;

	TCCR3A = 0;						// normal mode counter
	TCCR3B = _BV(CS12);				// clock divisor is 256 (calculated not to overflow in 2 seconds)
	TCCR3C = 0;
	TCNT3H = 0;
	TCNT3L = 0;

	ASSR = _BV(AS2);				// enable asynchronous mode with external input
	TCCR2A = _BV(WGM21);			// WGM wave form mode 2 (counter/timer) with
	TCCR2B = _BV(CS20);				//   no additional prescalar 32768/256/1 for 128/sec
	TCNT2 = 0;						// inits the actual counter to 0
	OCR2A = 32768/OSTMR_TICKS_PER_SECOND - 1; // sets the comparison value
	while (ASSR & 0x1F) {};			// wait for changes to be applied
	TIFR2 = 7;						// this clears the interrupts?  it seems like it would cause one
	TIMSK2 = _BV(OCIE2A);			// enable timer2 interrupt A (overflow interrupt and B match interrupt are disabled

	OS_EXIT_CRITICAL();
}

/*PAGE*/
/*
 *******************************************************************************
 *                   ENABLE/DISABLE TIMER TICK INTERRUPTS
 *
 * Description:			Reads the current value of the timer tick interrupt mask
 *						and then updates the interrupt mask according to the
 *						parameter.  This is useful for temporarily disabling the
 *						timer tick, such as:
 *
 *							void somefunc()
 *							{
 *								...
 *								uint8_t tickintsave = OStmrToggleMask(0);
 *								... do some actions with timer tick off ...
 *								OStmrToggleMask(tickintsave);
 *								... continue with timer tick in original state ...
 *							}
 *
 * Arguments:			newVal is the new state of the timer tick mask; this can
 *						be 0 if timers are to be turned off, or it can be a
 *						previous return value from OStmrToggleMask to restore
 *						a previous state
 *
 * Returns:				The state of the timer tick mask on entry to the
 *						function, which can be supplied as "newVal" to restore
 *						the state.
 *
 * Notes:
 *******************************************************************************
 */
uint8_t OStmrToggleMask(uint8_t newVal)
{
	OS_CPU_SR cpu_sr;
	OS_ENTER_CRITICAL();

	uint8_t savedVal = TIMSK2;
	TIMSK2 = newVal;

	OS_EXIT_CRITICAL();
	return savedVal;
}

/*PAGE*/
/*
 *******************************************************************************
 *                   TIMER TICK INTERRUPT SERVICE ROUITINE
 *
 * Description:			This is the timer tick interrupt service routine (for
 *						timer/counter 2)  This ISR is required for preemptive
 *						task switching; it also rescues any tasks pending on
 *						UART I/O
 *
 * Arguments:			n/a
 *
 * Returns:				n/a
 *
 * Notes:				1) This is a "naked" function, meaning that special
 *						   macros OSINTPUSH and OSINTPOP take care of the
 *						   assmbler-based ISR prolgue and epilogue
 *
 *						2) A portion of this function can execute with
 *						   the global interrupts enabled, but with the timer
 *						   tick interrupt specifically disabled.  This
 *						   represents an improvment in non-tick interrupt
 *						   latency at the cost of some stack space.
 *******************************************************************************
 */
ISR(TIMER2_COMPA_vect, ISR_NAKED)
{
    OSINTPUSH();						// custom register save routine
	
	// hand-optimized OSIntEnter(), also saves SP in TCB in preparation for 
	// context switch
	OSINTENTER_AND_SAVE_SP();

	// disable the timer tick overflow interrupt and re-enable the global interrupt mask
#if OSTMR_NESTED_ISR != 0
	TIMSK2 &= ~_BV(OCIE2A);
	sei();
#endif

    OSTimeTick();						// handle the timers (MicroC/OS)

	// wake up any sleeping tasks where the intercharacter gap is sufficient
	OSuartTimerTickHandler();			

	(rtcTick < 127)?(rtcTick++):({rtcTick=0; secondTick++; if(timeTick)timeTick++;});
	// re-enable the timer tick overflow interrupt and disable the global interrupt mask
#if OSTMR_NESTED_ISR != 0
	cli();
	TIMSK2 |= _BV(OCIE2A);
#endif

	// notify MicroC/OS that ISR is complete (may perform a task switch)
    OSIntExit();

	// note that control will not return from OSIntExit unless no context switch has occurred

    OSINTPOP();							// custom register restore routine
}


uint32_t getsecondtick()
{
	uint32_t ticks;
	OS_CPU_SR cpu_sr;
	OS_ENTER_CRITICAL();
		ticks = secondTick;
	OS_EXIT_CRITICAL();
	return ticks;
}	

uint8_t settime(time_t time)
{
	OS_CPU_SR cpu_sr;
	OS_ENTER_CRITICAL();
		timeTick = time;
	OS_EXIT_CRITICAL();
	return 0;
}	

time_t gettime()
{
	OS_CPU_SR cpu_sr;
	time_t temp;

	OS_ENTER_CRITICAL();
		temp = timeTick;
	OS_EXIT_CRITICAL();

	return temp;
}	


time_t mktime(tm time)
{
	time_t result = 0;
	uint32_t jdn = 0;
	uint8_t month = time.tm_mon;
	int16_t year = time.tm_year;
	int16_t c = 0;


	
	if(time.tm_sec != -1)
	{
		if(month < 3)
		{
			month+=12;
			year--;
		}

		c = 2 - year/100 + year/400;

	    jdn = 1461L*(year + 4716)/4 + 153*(month + 1)/5 + time.tm_mday + c - 2442112L;

		result = 60*(60*(24*jdn + time.tm_hour) + time.tm_min) + time.tm_sec;
	}

	return result;
}


	
tm*	gmtime(time_t *timer)
	{
        static tm br_time;
        tm *timep = &br_time;
        time_t time = *timer;
        uint32_t dayclock, dayno;
        int16_t year = EPOCH_YR;

        dayclock = (uint32_t)time % SECS_DAY;
        dayno = (uint32_t)time / SECS_DAY;

        timep->tm_sec = dayclock % 60;
        timep->tm_min = (dayclock % 3600) / 60;
        timep->tm_hour = dayclock / 3600;
        timep->tm_wday = (dayno + 4) % 7;       // day 0 was a thursday 
        while (dayno >= YEARSIZE(year)) 
		{
        	dayno -= YEARSIZE(year);
        	year++;
        }
        timep->tm_year = year - YEAR0;
        timep->tm_yday = dayno;
        timep->tm_mon = 0;
        while (dayno >= _ytab[LEAPYEAR(year)][timep->tm_mon]) 
		{
        	dayno -= _ytab[LEAPYEAR(year)][timep->tm_mon];
			timep->tm_mon++;
        }
        timep->tm_mday = dayno + 1;
        timep->tm_isdst = 0;

        return timep;
}
