// File Name	: timer.c

/*! \file timer.c \brief System Timer function library. */
//*****************************************************************************
//
// File Name	: 'timer.c'
// Title		: System Timer function library
// Author		: Pascal Stang - Copyright (C) 2000-2002
// Created		: 11/22/2000
// Revised		: 5/6/2002
// Version		: 1.1
// Target MCU	: Atmel AVR Series
// Editor Tabs	: 4
//
// This code is distributed under the GNU Public License
//		which can be found at http://www.gnu.org/licenses/gpl.txt
//
//*****************************************************************************

#ifndef WIN32
	#include 
	#include 
	#include 
	#include 
#endif

#include "global.h"
#include "timer.h"
#include "lcd.h"

extern RtcTimeType	gRtcTime;
extern PumpInfoType	gPumpInfo;
extern RtcTimeType	gAlarmTime;
extern SysStateType	gSysState;
extern void timer0OutForButtonClick(void);
extern void Timer1CmpA_Isr(void);
extern u08 NeedSpeakerWork(void);


// Program ROM constants
// the prescale division values stored in 2^n format
// STOP, CLK, CLK/8, CLK/64, CLK/256, CLK/1024
short __attribute__ ((progmem)) TimerPrescaleFactor[] = {0,0,3,6,8,10};
// Program ROM constants
static char __attribute__ ((progmem)) MonthDayTable[] = {31,28,31,30,31,30,31,31,30,31,30,31};
// Note every 4 years, Feb has 29 days, as in 2004, 2008, 2012, ...

// Global variables
// time registers
volatile unsigned long Timer0PauseReg;
volatile unsigned long Timer0Reg0;
volatile unsigned long Timer0Reg1;
volatile unsigned long Timer2Reg0;
volatile unsigned long Timer2Reg1;

typedef void (*voidFuncPtr)(void);
volatile static voidFuncPtr TimerIntFunc[TIMER_NUM_INTERRUPTS];

// delay for a minimum of  microseconds 
// the time resolution is dependent on the time the loop takes 
// e.g. with 4Mhz and 5 cycles per loop, the resolution is 1.25 us 
void delay(unsigned short us) 
{
	unsigned short delay_loops;
	register unsigned short i;

	delay_loops = (us+3)/5*CYCLES_PER_US; // +3 for rounding up (dirty) 

	// one loop takes 5 cpu cycles 
	for (i=0; i < delay_loops; i++) {};
} 

void timerInit(void)
{
	u08 intNum;
	// detach all user functions from interrupts
	for(intNum=0; intNum number of milliseconds

	// calculate delay for [pause_ms] milliseconds
	u16 prescaleDiv = 1<<(PRG_RDB(TimerPrescaleFactor+inp(TCCR0)));
	u32 pause = (pause_ms*(F_CPU/(prescaleDiv*256)))/1000;
	
	Timer0PauseReg = 0;
	while(Timer0PauseReg < pause);
}

void timer0ClearOverflowCount(void)
{
	// clear the timer overflow counter registers
	Timer0Reg0 = 0;	// initialize time registers
	Timer0Reg1 = 0;	// initialize time registers
}

long timer0GetOverflowCount(void)
{
	// return the current timer overflow count
	// (this is since the last timer0ClearOverflowCount() command was called)
	return Timer0Reg0;
}

void timer2ClearOverflowCount(void)
{
	// clear the timer overflow counter registers
	Timer2Reg0 = 0;	// initialize time registers
	Timer2Reg1 = 0;	// initialize time registers
}

long timer2GetOverflowCount(void)
{
	// return the current timer overflow count
	// (this is since the last timer2ClearOverflowCount() command was called)
	return Timer2Reg0;
}

void timer1PWMInit(u08 bitRes)
{
	// configures timer1 for use with PWM output
	// on OC1A and OC1B pins

	// enable timer1 as 8,9,10bit PWM
	if(bitRes == 9)
	{	// 9bit mode
		sbi(TCCR1A,PWM11);
		cbi(TCCR1A,PWM10);
	}
	else if( bitRes == 10 )
	{	// 10bit mode
		sbi(TCCR1A,PWM11);
		sbi(TCCR1A,PWM10);
	}
	else
	{	// default 8bit mode
		cbi(TCCR1A,PWM11);
		sbi(TCCR1A,PWM10);
	}

	// set clear-timer-on-compare-match
	sbi(TCCR1B,CTC1);
	// clear output compare value A
	outp(0, OCR1AH);
	outp(0, OCR1AL);
	// clear output compare value B
	outp(0, OCR1BH);
	outp(0, OCR1BL);
}

void timer1PWMOff(void)
{
	// turn off timer1 PWM mode
	cbi(TCCR1A,PWM11);
	cbi(TCCR1A,PWM10);
	// clear (disable) clear-timer-on-compare-match
	cbi(TCCR1B,CTC1);
	// set PWM1A/B (OutputCompare action) to none
	timer1PWMAOff();
	timer1PWMBOff();
}

void timer1PWMAOn(void)
{
	// turn on channel A (OC1A) PWM output
	// set PWM1A as non-inverted PWM
	sbi(TCCR1A,COM1A1);
	cbi(TCCR1A,COM1A0);
}

void timer1PWMBOn(void)
{
	// turn on channel B (OC1B) PWM output
	// set PWM1B as non-inverted PWM
	sbi(TCCR1A,COM1B1);
	cbi(TCCR1A,COM1B0);
}

void timer1PWMAOff(void)
{
	// turn off channel A (OC1A) PWM output
	// set PWM1A (OutputCompare action) to none
	cbi(TCCR1A,COM1A1);
	cbi(TCCR1A,COM1A0);
}

void timer1PWMBOff(void)
{
	// turn off channel B (OC1B) PWM output
	// set PWM1B (OutputCompare action) to none
	cbi(TCCR1A,COM1B1);
	cbi(TCCR1A,COM1B0);
}

void timer1PWMASet(u16 pwmDuty)
{
	// set PWM (output compare) duty for channel A
	// this PWM output is generated on OC1A pin
	// NOTE:	pwmDuty should be in the range 0-255 for 8bit PWM
	//			pwmDuty should be in the range 0-511 for 9bit PWM
	//			pwmDuty should be in the range 0-1023 for 10bit PWM
	outp( (pwmDuty>>8), OCR1AH);		// set the high 8bits of OCR1A
	outp( (pwmDuty&0x00FF), OCR1AL);	// set the low 8bits of OCR1A
}

void timer1PWMBSet(u16 pwmDuty)
{
	// set PWM (output compare) duty for channel B
	// this PWM output is generated on OC1B pin
	// NOTE:	pwmDuty should be in the range 0-255 for 8bit PWM
	//			pwmDuty should be in the range 0-511 for 9bit PWM
	//			pwmDuty should be in the range 0-1023 for 10bit PWM
	outp( (pwmDuty>>8), OCR1BH);		// set the high 8bits of OCR1B
	outp( (pwmDuty&0x00FF), OCR1BL); // set the low 8bits of OCR1B
}

//! Interrupt handler for tcnt0 overflow interrupt
SIGNAL(SIG_OVERFLOW0)
{
	Timer0Reg0++;			// increment low-order counter
	if(!Timer0Reg0)		// if low-order counter rollover
		Timer0Reg1++;		// increment high-order counter	

	// increment pause counter
	Timer0PauseReg++;

	// if a user function is defined, execute it too
	if(TimerIntFunc[TIMER0OVERFLOW_INT])
		TimerIntFunc[TIMER0OVERFLOW_INT]();
}

//! Interrupt handler for tcnt1 overflow interrupt
SIGNAL(SIG_OVERFLOW1)
{
	// if a user function is defined, execute it
	if(TimerIntFunc[TIMER1OVERFLOW_INT])
		TimerIntFunc[TIMER1OVERFLOW_INT]();
}

//! Interrupt handler for tcnt2 overflow interrupt
SIGNAL(SIG_OVERFLOW2)
{
	Timer2Reg0++;			// increment low-order counter
	if(!Timer2Reg0)		// if low-order counter rollover
		Timer2Reg1++;		// increment high-order counter	

	// if a user function is defined, execute it
	if(TimerIntFunc[TIMER2OVERFLOW_INT])
		TimerIntFunc[TIMER2OVERFLOW_INT]();
}

//! Interrupt handler for CutputCompare1A match (OC1A) interrupt
SIGNAL(SIG_OUTPUT_COMPARE1A)
{
	// if a user function is defined, execute it
	if(TimerIntFunc[TIMER1OUTCOMPAREA_INT])
		TimerIntFunc[TIMER1OUTCOMPAREA_INT]();
}

//! Interrupt handler for OutputCompare1B match (OC1B) interrupt
SIGNAL(SIG_OUTPUT_COMPARE1B)
{
	// if a user function is defined, execute it
	if(TimerIntFunc[TIMER1OUTCOMPAREB_INT])
		TimerIntFunc[TIMER1OUTCOMPAREB_INT]();
}

//! Interrupt handler for InputCapture1 (IC1) interrupt
SIGNAL(SIG_INPUT_CAPTURE1)
{
	// if a user function is defined, execute it
	if(TimerIntFunc[TIMER1INPUTCAPTURE_INT])
		TimerIntFunc[TIMER1INPUTCAPTURE_INT]();
}

//! Interrupt handler for OutputCompare2 match (OC2) interrupt
SIGNAL(SIG_OUTPUT_COMPARE2)
{
	// if a user function is defined, execute it
	if(TimerIntFunc[TIMER2OUTCOMPARE_INT])
		TimerIntFunc[TIMER2OUTCOMPARE_INT]();
}

//=================================================================================
// The following functions are written by Gang Xie for the project.
//
//		Courtesy of Pascal Stang: some segments/functions are directly from Pascal.
//=================================================================================
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void InitializeRtc(void)
{
	// initializae the real-time clock Timer2: 32768/128/2^8 = 1Hz
	outp((1< 59)					// check seconds overflow
	{
		gRtcTime.second = 0;
		gRtcTime.minute++;						// increment minutes
		gSysState.update_rtc_minute = 1;
		if(gRtcTime.minute > 59)				// check minutes overflow
		{
			gRtcTime.minute = 0;
			gRtcTime.hour++;					// increment hours
			gSysState.update_rtc_hour = 1;
			if(gRtcTime.hour > 23)				// check hours overflow
			{
				gRtcTime.hour = 0;
				gRtcTime.day++;					// increment days
				gRtcTime.sunsat++;
				if (gRtcTime.sunsat>6)
					gRtcTime.sunsat = 0;
				gSysState.update_rtc_day = 1;
				gSysState.update_rtc_sunsat = 1;
				days = CalcDaysInMonth(gRtcTime.month, gRtcTime.year);
				if(gRtcTime.day > days)			// check days overflow
				{
					gRtcTime.day = 1;
					gRtcTime.month++;			// increment months
					gSysState.update_rtc_month = 1;
					if(gRtcTime.month >= 13)	// check months overflow
					{
						gRtcTime.month = 1;
						gRtcTime.year++;		// increment years
						if (gRtcTime.year>=10000)
							gRtcTime.year = 0;
						gSysState.update_rtc_year = 1;
					}
				}
			}
		}
	}

	//alarm
	if (gSysState.alarmOn)
	{
		if ((gRtcTime.hour==gAlarmTime.hour) &&
			(gRtcTime.minute==gAlarmTime.minute) &&
			(gRtcTime.second==gAlarmTime.second))
			gSysState.alarmCount = kAlarmTimeout + 1;
	}
	if (gSysState.alarmCount > 0)				//safer to be here
		gSysState.alarmCount--;

	// change frequency
	if (NeedSpeakerWork())
	{
		gSysState.spkNoteNum++;
		if (gSysState.spkNoteNum >= 8)
			gSysState.spkNoteNum = 0;
	}

	//pump
	if (gSysState.pumpActivated)
	{
		if (CompareTwoTimes(gRtcTime, gPumpInfo.next)==1)
		{
			gPumpInfo.duration.year = ((u16)gPumpInfo.duration.minute)*60;
			gPumpInfo.duration.year += ((u16)gPumpInfo.duration.second);
			gPumpInfo.totDurInSeconds = gPumpInfo.duration.year;
			gPumpInfo.duration.year ++;
		}
	}
	if (gPumpInfo.duration.year > 0)
	{
		if (gSysState.pumpActivated)
			sbi(SPK_PORTO, PUMP_POSPIN);		//set 1=>begin bumping

		gPumpInfo.duration.year--;

		if (gPumpInfo.duration.year==0)
		{
			cbi(SPK_PORTO, PUMP_POSPIN);		//clear 0=>stop bumping
			CalcNextPumpTime();
		}
		gSysState.update_info = 1;
	}

	//switch to normal LCD page
	if (gSysState.displayTimeout > 0)
		gSysState.displayTimeout--;

	//info
	if (gSysState.displayPage == kPage0_Norm)
	{
		gSysState.update_info = 1;
		gSysState.update_info_num++;
		if (gSysState.update_info_num>21)
			gSysState.update_info_num = 0;
	}
}

//---------------------------------------------------------------------------------
// return	
//	0: one < two, i.e., one is slower than two 
//	1: = equal
//	2: one > two, i.e., one is faster than two
//---------------------------------------------------------------------------------
u08 CompareTwoTimes(RtcTimeType one, RtcTimeType two)
{
	if (one.year>two.year)
		return 2;
	if (one.yeartwo.month)
		return 2;
	if (one.monthtwo.day)
		return 2;
	if (one.daytwo.hour)
		return 2;
	if (one.hourtwo.minute)
		return 2;
	if (one.minutetwo.second)
		return 2;
	if (one.secondminute + gap->minute;
	while (tmp > 59)
	{
		tmp -= 60;
		aid ++;
	}
	t->minute = tmp;

	// hour
	tmp = t->hour + gap->hour + aid;
	aid = 0;
	while (tmp > 23)
	{
		tmp -= 24;
		aid ++;
	}
	t->hour = tmp;

	// day
	tmp = t->day + gap->day + aid;
	yeas = t->year;
	mons = t->month;
	days = CalcDaysInMonth(mons, yeas);
	while (tmp > days)
	{
		tmp -= days;
		mons ++;
		if (mons == 13)
		{
			mons = 1;
			yeas ++;
		}
		days = CalcDaysInMonth(mons, yeas);
	}
	t->day = tmp;
	t->month = mons;
	t->year = yeas;
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void CalcNextPumpTime(void)
{
	u08 rlt;
	
	rlt = CompareTwoTimes(gPumpInfo.next, gRtcTime);
	while (rlt<2)
	{
		AddTimeGap(&(gPumpInfo.next), &(gPumpInfo.frequency));
		rlt = CompareTwoTimes(gPumpInfo.next, gRtcTime);
	}
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
u16 AdjustYear(u16 val, u08 isInc)
{
	u16 rlt;

	if (isInc)
	{
		rlt = val + 1;
		if (rlt>9999)
			rlt = 0;
	}
	else
	{
		if (val == 0)
			val = 10000;
		rlt = val - 1;
	}
	return rlt;
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
u08 Adjust2DgtNumber(u08 val, u08 isInc, u08 minv, u08 maxv)
{
	u08 rlt;

	// In the middle of computations, there should not be negative
	if (isInc)
	{
		if (val>=maxv)
			rlt = minv;
		else
			rlt = val + 1;
	}
	else
	{
		if (val <= minv)
			rlt = maxv;
		else
			rlt = val - 1;
	}
	return rlt;
}