//#define TESTING_TIMER

/*
This module contains a software PIC timer based on Timer0 overflows.  It requires the following:
(1) Global interrupts enabled
(2) Timer0 not used for anything else
(3) Running off a 20MHz oscillator
(3) Copy pasting the following into the interrupt response routine in the main C file:
if (TMR0IE && TMR0IF)
		TimerPIC_ISR();
(4) Including TimerPIC.h

Originally, this module updated all the timer flags inside the interrupt response.  However, this took
over 100 us and caused problems with other precise timing interrupts (OC).  Instead the timers have been
rewritten to update outside of an interrupt.

To use a timer:
(1) Initialize the module using TimerPIC_Init
(2) Start a timer using TimerPIC_InitTimer(Num,NewTime), where Num is 0-7 and NewTime is in ms
(3) Check if the timer has expired using if(TimerPIC_IsTimerExpired(Num) == TimerPIC_EXPIRED)
*/

//#include "pic16f777.h"
#include <stdio.h>
#define _LEGACY_HEADERS
#include <htc.h>
#include "BITDEFS.h"
#include "TimerPIC.h"

#ifdef TESTING_TIMER
//__CONFIG(UNPROTECT & WDTDIS & PWRTDIS & HS);
__CONFIG (UNPROTECT & WDTDIS & PWRTDIS & HS);
#endif TESTING_TIMER

typedef unsigned char Tflag_t;

typedef struct timer_struc{
   	unsigned int TimerTime;
	unsigned int LastTime;
}Timer_t;

/*---------------------------- Module Functions ---------------------------*/

/*---------------------------- Module Variables ---------------------------*/
static Timer_t TMR_TimerArray[sizeof(Tflag_t)*8]; // Store times for all timers
static unsigned int time;  /* this is used by the default RTI routine */

void TimerPIC_Init(void) {

	// Set up overflows every 0.2048 ms: 2^8*((20MHz/4)/4)
	T0CS = 0;	// Internal instruction clock (fosc/4)
	PSA = 0;	// Assign prescaler to Timer0
	PS2 = 0;
	PS1 = 0;
	PS0 = 1;	// Prescaler = 1:4;
	TMR0IF = 0;	// Clear flag
	TMR0IE = 1;	// Turn on interrupt
}

TimerPICReturn_t TimerPIC_InitTimer(unsigned char Num, unsigned int NewTime) {

	// Schedule when timer expires
	TMR_TimerArray[Num].TimerTime = NewTime;
	TMR_TimerArray[Num].LastTime = TimerPIC_GetTime();
	return TimerPIC_OK;
}

TimerPICReturn_t TimerPIC_IsTimerExpired(unsigned char Num) {

	if( (TimerPIC_GetTime() - TMR_TimerArray[Num].LastTime) > TMR_TimerArray[Num].TimerTime) {
		return TimerPIC_EXPIRED;
	}
	else
		return TimerPIC_NOT_EXPIRED;
}

unsigned int TimerPIC_GetTime(void) {
	return (time);
}

void TimerPIC_ISR(void) {
	static unsigned char counter = 0;

	// Clear the source of the interrupt
	TMR0IF = 0;
	// We're interrupted every 0.2048ms, so only update every 5th time
	if (++counter == 5) {
		// keep the GetTime() timer running
		time++;
		counter = 0;
	}
}

#ifdef TESTING_TIMER

void interrupt TimerExpired(void) {
	if (TMR0IE && TMR0IF)
		TimerPIC_ISR();
}

void main(void) {

	// Turn off analog inputs
	PCFG0 = 1;
	PCFG1 = 1;
	PCFG2 = 1;
	PCFG3 = 1;

	TimerPIC_Init();
	TimerPIC_InitTimer(0,100);
	TRISB3 = 0;
	TRISB4 = 0;
	RB4 = 1;
	RB5 = 1;
	GIE = 1;

	while(1) {
		if(TimerPIC_IsTimerExpired(0) == TimerPIC_EXPIRED){
			TimerPIC_InitTimer(0,100);
			RB4 ^= 1;
		}
	}
}

#endif