/****************************************************************************/
/* Copyright 2009 MBARI.                                                    */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
#include <p24fxxxx.h>
#include "sys_timer.h"
#include "sys_defs.h"

/* Timer setup  */
#define FOSC      7372800

#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 unsigned int timerTick = 0;
static unsigned long tickCount = 0;

static void updateTickCount();

/* trigger variables */
static unsigned int triggerCount = 0;

/* debounce and trigger supression counters */
static unsigned int trigActiveCount = 0;
static unsigned int trigHoldOffCount = 0;
                        
/* user set debounce and suppression levels */
static unsigned int holdOffLevel = ((HOLDOFF_LEVEL_DEF * 1000L) / MS_PER_TICK);
static unsigned int debounceLevel = (DEBOUNCE_LEVEL_DEF / MS_PER_TICK);

/* intialize the timer */
void tmrInit()
{
    /* clear out the timer2 control reg */
    T2CON = 0x0000;
    /* clear timer count */
    TMR2 = 0x0000;           
    /* set timer period */
    PR2 = TMR2PRD;
    
	/* setup timer interrupts */
	IFS0bits.T2IF = 0;
    IEC0bits.T2IE = 1;

    /* start timer */
    T2CONbits.TON = 1;
    
    return;
}

/* perform any timer tasks in main loop */
void tmrTask()
{
    /* update 32 bit tick count */
    updateTickCount();

    /* update doze vector for one shot outputs */
}

/* 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 */
}

/* delay for a specified number of milliseconds */
void tmrDelayMs(unsigned int ms)
{
    unsigned long wait_ticks = tmrGetTicks();
     
    if ( (ms / MS_PER_TICK) > 0 )
        wait_ticks += (unsigned long)(ms / MS_PER_TICK);
    else
        wait_ticks += 1;

    while ( wait_ticks > tmrGetTicks() )
        ;/* wait for wait ticks to expire */
}

/* get internal tick counts */
unsigned long tmrGetTicks()
{
    /* update 32 bit tick count */
    updateTickCount();
    
    return tickCount;
}

/* trigger detection level variables hacked into sys_time */
unsigned int tmrGetTriggerCount()
{
    return triggerCount;
}

void tmrSetTrigHoldOff(unsigned long hold_off)
{
    unsigned long ticks = hold_off;

    /* check the bounds */
    if ( ticks > HOLDOFF_LEVEL_MAX )
        ticks = HOLDOFF_LEVEL_MAX; 

    if ( ticks < HOLDOFF_LEVEL_MIN )
        ticks = HOLDOFF_LEVEL_MIN; 
    
    /* convert to ticks */
    ticks *= FS;

    if ( ticks > 0xFFFF )
        ticks = 0xFFFF;

    holdOffLevel = ticks;
}

void tmrSetTrigDebounce(unsigned long debounce_ms)
{
    unsigned long ticks = debounce_ms;

    /* check the bounds */
    if ( ticks > DEBOUNCE_LEVEL_MAX )
        ticks = DEBOUNCE_LEVEL_MAX; 

    if ( ticks < DEBOUNCE_LEVEL_MIN )
        ticks = DEBOUNCE_LEVEL_MIN; 
    
    /* convert to ticks */
    ticks /= MS_PER_TICK;

    if ( ticks > 0xFFFF )
        ticks = 0xFFFF;

    debounceLevel = ticks;
}

unsigned long tmrGetTrigHoldOff()
{
    unsigned long time = holdOffLevel;

    time /= FS;

    return time;
}

unsigned long tmrGetTrigDebounce()
{
    unsigned long time = debounceLevel;

    time *= MS_PER_TICK;

    return time;
}

void updateTickCount()
{
    unsigned int tick_delta;

    /* calculate tick delta */
    tick_delta = timerTick - (tickCount & 0x0000FFFF);
    /* add delta to tick count */
    tickCount += tick_delta;
}

/* Timer2 ISR */
void __attribute__ ((interrupt,no_auto_psv))_T2Interrupt(void)
{
    /* clear timer count */
    TMR2 = 0;

    /* increment the counter */
    ++timerTick;

    /* if trigger hold off is set, dec it */ 
    if ( trigHoldOffCount )
    {
        --trigHoldOffCount;
    }
    else
    {

        if ( PORTAbits.RA7 == 0 )
            ++trigActiveCount;
        else
            trigActiveCount = 0;

        if ( trigActiveCount > debounceLevel )
        {
            ++triggerCount;
            trigActiveCount = 0;
            trigHoldOffCount = holdOffLevel;
        }
    }

    /* clear the interrupt flag */
    IFS0bits.T2IF = 0;
}

