/****************************************************************************/
/* Copyright 2009-2013 MBARI.                                               */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Written by Mike Risi for Respirometer project in 2009                    */
/* Modified by Bob Herlien for SensorNode for xFOCE, May 2013               */
/****************************************************************************/
#include "sys_timer.h"

#include "port.h"   /* enter/exit critical section code */

/* for debug purposes */
//#include "dig_io.h"

/* Timer setup  */
#define FOSC    3686400
#define FCY     (FOSC/2)
#define FS      100 /* frequency of timer */

#define TMR2PRD (FCY/FS)-1

/* milliseconds per tick */
#define MS_PER_TICK (1000 / FS)

static volatile UINT16 timerTick = 0;
static unsigned long tickCount = 0;

static void updateTickCount();

/* intialize the timer */
void tmrInit()
{
    /* clear out the timer2 control reg */
    T2CON = 0x0000;
    /* clear timer count */
    TMR2 = 0x0000;           
    /* set timer period */
    PR2 = TMR2PRD;
    
    /* set timer interrupt priority to 5 */
    _T2IP = 5;

    /* setup timer interrupts */
    _T2IF = 0;
    _T2IE = 1;

    /* start timer */
    T2CONbits.TON = 1;
    
    return;
}

/* perform any timer tasks in main loop */
void tmrTask()
{
    /* update 32 bit tick count */
    updateTickCount();
}

/* see if the timer is running */
int tmrIsRunning(Timer* timer)
{
    return timer->active;
}

/* Create the timer */
void tmrCreate(Timer* timer)
{
    tmrStop(timer);
    tmrClear(timer);
}

/* Clear the timer */
void tmrClear(Timer* timer)
{
    timer->totalTicks = 0;
    timer->startTicks = tmrGetTicks();
}

/* Read the total amount of elapsed time the timer has running */
unsigned long tmrRead(Timer* timer)
{
    unsigned long ticks = tmrGetTicks();

    if ( timer->active )
    {
        /* check for rollover */
        if ( timer->startTicks > ticks )
            timer->totalTicks += (ticks + timer->startTicks);
        else
            timer->totalTicks += (ticks - timer->startTicks);
    }

    timer->startTicks = ticks;

    return (timer->totalTicks * MS_PER_TICK);
}

/* Start the timer */
unsigned long tmrStart(Timer* timer)
{
    tmrRead(timer);
    timer->active = TRUE;
    
    return (timer->totalTicks * MS_PER_TICK);
}

/* Stop the timer */
unsigned long tmrStop(Timer* timer)
{
    tmrRead(timer);
    timer->active = FALSE;
    
    return (timer->totalTicks * MS_PER_TICK);
}

/* delay for a specified number of timer ticks */
void tmrDelayTicks(unsigned int ticks)
{
    unsigned long wait_ticks = tmrGetTicks() + ticks;

    while ( wait_ticks > tmrGetTicks() )
        ;/* wait for wait ticks to expire */
}

#define PRD_PER_MS	29
/* delay for a specified number of milliseconds */
void tmrDelayMs(unsigned int ms)
{
    if (ms >= 1000)
	tmrDelayTicks((ms+(MS_PER_TICK-1))/MS_PER_TICK);
    else if (ms > 0)
    {
	T3CON = 0x0020;			/* Set timer3 control to divide by 64*/
	TMR3 = 0x0000;           	/* clear timer count		*/
	PR3 = ms * PRD_PER_MS;		/* set timer period		*/
	_T3IE = 0;			/* Don't actually interrupt	*/
	IFS0bits.T3IF = 0;		/* clear interrupt flag		*/
	T3CONbits.TON = 1;		/* start timer			*/

	while (IFS0bits.T3IF == 0)	/* Wait for the timer to expire	*/
	    Nop();
    }
}

/* delay for a specified number of microseconds		*/
/* Note that this only works well for delays > 20us	*/
/* For shorter delays, use Nop()'s (1 Nop() == 0.54us)	*/
/* For us <= 20, gives fixed delay of approx 10us	*/
/* For us > 20, gives delay approx 9% too long		*/
void tmrDelayUs(unsigned int us)
{
    if (us >= 5000)
	tmrDelayMs((us+900)/1000);
    else if (us > 20)
    {
	T3CON = 0;			/* Set timer3 control to divide by 1*/
	TMR3 = 0;			/* clear timer count		*/
	PR3 = 2 * (us-20);		/* 8.5% too slow		*/
	IFS0bits.T3IF = 0;		/* setup timer interrupts */
	T3CONbits.TON = 1;		/* start timer */

	while (IFS0bits.T3IF == 0)	/* Wait for the timer to expire	*/
	    Nop();
    }
}

/* get internal tick counts */
unsigned long tmrGetTicks()
{
    /* update 32 bit tick count */
    updateTickCount();
    
    return tickCount;
}

void updateTickCount()
{
    UINT16 tickDelta;

    /* calculate tick delta */
    tickDelta = timerTick - (tickCount & 0x0000FFFF);
    /* add delta to tick count */
    tickCount += tickDelta;
}

/* Timer2 ISR */
void __attribute__ ((interrupt,no_auto_psv))_T2Interrupt(void)
{
    /* clear timer count */
    TMR2 = 0;

    /* increment the counter */
    ++timerTick;

    /* clear the interrupt flag */
    IFS0bits.T2IF = 0;
}

