/**************************************************************************************************
---------------------------------------------------------------------------------------------------
	Copyright (c) 2004-2007, Jonathan Bagg
	All rights reserved.

	 Redistribution and use in source and binary forms, with or without modification, are permitted 
	 provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, this list of 
	  conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice, this list of 
	  conditions and the following disclaimer in the documentation and/or other materials provided 
	  with the distribution.
    * Neither the name of Jonathan Bagg nor the names of its contributors may be used to 
	  endorse or promote products derived from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 
  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 
  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
  POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------------------------------
   Project name : Infidigm AVR Drivers
   Processor	: ATMega64, ATMega128
   File name    : tim.c
---------------------------------------------------------------------------------------------------
   Modifications: 

   Revision 1.0  2004/06/11 	Bagg
   - Cleaned up for release
   
   Revision 1.1  2004/12/15 	Bagg
   - StopWatch Functions added
   
   Revision 1.2  2007/05/12 	Bagg
   - read_systimer() added
---------------------------------------------------------------------------------------------------
   Created      : 05 May 2004     	           Author(s) : Jonathan Bagg
---------------------------------------------------------------------------------------------------
   System Timers / Tick Timer Interrupt Based Driver
---------------------------------------------------------------------------------------------------
**************************************************************************************************/

#include <avr/io.h> 
#include <avr/signal.h>
#include <avr/interrupt.h>
#include "tim.h"

/**************************************************************************************************
*   INTERNAL GLOBAL VARIABLES - Structure to hold countdown timers
**************************************************************************************************/
static unsigned int systimer;		// 1ms timer

struct timer {
	unsigned char type;
	unsigned int length;
};
static struct timer timers[MAXTIMERS];

/**************************************************************************************************
*   INTERNAL Function declaration - See 'tim.h' Header file for Descriptions
**************************************************************************************************/
static void update_timers(void);

/**************************************************************************************************
*   start_timer(); - See 'tim.h' Header file for Description
**************************************************************************************************/
void start_timer(void)
{
 TCCR0 = _BV(WGM01)|0x05;	// div SYSCLK 128 by & CTC mode
 //OCR0 = 11;			// = 0.1mS = 10kHz 
 //OCR0 = 23;			// = 0.2mS = 5kHz
 //OCR0 = 59;			// = 0.5mS = 2kHz
 OCR0 = 119;		// = 1mS = 1kHz
 //OCR0 = 239;		// = 2mS = 500Hz
 TIMSK|= _BV(OCIE0);	//Enable OC0 IRQ
}

/**************************************************************************************************
*   TIMER0 COMPARE ISR - See 'tim.h' Header file for Description
**************************************************************************************************/
SIGNAL(SIG_OUTPUT_COMPARE0)
{
 systimer++;
 if (systimer == 0) 
	update_timers();
}

/**************************************************************************************************
*   update_timers(); - See 'tim.h' Header file for Description
**************************************************************************************************/
static void update_timers(void)
{
 unsigned char i;
 for (i=0;i < MAXTIMERS;i++) {
	if (timers[i].type != 0) 
		timers[i].type--;
 }
}

/**************************************************************************************************
*   read_systimer(); - See 'tim.h' Header file for Description
**************************************************************************************************/
unsigned int read_systimer(void)
{
 return systimer;
}

/**************************************************************************************************
*   timer_get(); - See 'tim.h' Header file for Description
**************************************************************************************************/
unsigned char timer_get(unsigned int time)
{
 unsigned char i;
 
 for (i=0;i < MAXTIMERS;i++) {
	if (timers[i].type == NOTUSED) {
		timer_reset(i,time);
		break;
	}
 }
 return i;
}

/**************************************************************************************************
*   timer_reset(); - See 'tim.h' Header file for Description
**************************************************************************************************/
void timer_reset(unsigned char timer, unsigned int time)
{
 unsigned int record;
 
 record = systimer + time;					//check for overflow
 if (time == 1) 
	TCNT0 = 0;								//Garentee full 1mS
 timers[timer].length = record;
 if (record < time) 
	timers[timer].type = ROLLOVER;			//Mark ioverflow
 else 
	timers[timer].type = NOTDONE;			//Mark if normal
}

/**************************************************************************************************
*   timer_expired(); - See 'tim.h' Header file for Description
**************************************************************************************************/
unsigned char timer_expired(unsigned char timer)
{
 char done = 0;
 TIMSK&= ~_BV(OCIE0);		//Disable OC0 IRQ
 if (timers[timer].type < NOTDONE) {
	done = 1;
 }
 else if (timers[timer].type == NOTDONE && timers[timer].length <= systimer) {
	timers[timer].type--;
	done = 1;
 }
 TIMSK|= _BV(OCIE0);		//Enable OC0 IRQ
 return done;
}

/**************************************************************************************************
*   timer_release(); - See 'tim.h' Header file for Description
**************************************************************************************************/
void timer_release(unsigned char timer)
{
 timers[timer].type = NOTUSED;
}

/**************************************************************************************************
*   stopwatch_get(); - See 'tim.h' Header file for Description
**************************************************************************************************/
unsigned char stopwatch_get(void)
{
 unsigned char i;
 
 for (i=0;i < MAXTIMERS;i++) {
	if (timers[i].type == NOTUSED) {
		stopwatch_reset(i);
		break;
	}
 }
 return i;
}

/**************************************************************************************************
*   stopwatch_reset(); - See 'tim.h' Header file for Description
**************************************************************************************************/
void stopwatch_reset(unsigned char timer)
{
 timers[timer].type = NOTDONE;
 timers[timer].length = systimer;
}

/**************************************************************************************************
*   stopwatch_check(); - See 'tim.h' Header file for Description
**************************************************************************************************/
unsigned int stopwatch_check(unsigned char timer)
{
 unsigned int time;
 
 if (timers[timer].type < AUTOEXP)				//Check for dead timer
	time = 0xFFFF;
 else if (timers[timer].type == NOTDONE)		//if systimer has not overflowed
	time = systimer - timers[timer].length;
 else {
	if (systimer < timers[timer].length)
		time = 0xFFFF - timers[timer].length + systimer;
	else
		time = 0xFFFF;
 }
 return time;
}

/**************************************************************************************************
*   stopwatch_release(); - See 'tim.h' Header file for Description
**************************************************************************************************/

/**************************************************************************************************
*   delay(); - See 'tim.h' Header file for Description
**************************************************************************************************/
void delay(unsigned int time)
{
 unsigned char wait;
 wait = timer_get(time);
 while (!timer_expired(wait));
 timer_release(wait);
}

