/****************************************************************************/
/* Copyright 2009 - 2015 MBARI                                              */
/****************************************************************************/
/* Summary  : System timer/ticker module for OASIS5 on PIC24FJ128GA406      */
/* Filename : sys_timer.c                                                   */
/* Author   : Mike Risi (mrisi), Robert Herlien (rah)                       */
/* Project  : OASIS Mooring Replacement (OASIS5)                            */
/* Revision: 1.0                                                            */
/* Created  : 2009 by Mike Risi, Adapted for Oasis5 10/29/2015 by Bob Herlien*/
/*                                                                          */
/* MBARI provides this documentation and code "as is", with no warranty,    */
/* express or implied, of its quality or consistency. It is provided without*/
/* support and without obligation on the part of the Monterey Bay Aquarium  */
/* Research Institute to assist in its use, correction, modification, or    */
/* enhancement. This information should not be published or distributed to  */
/* third parties without specific written permission from MBARI.            */
/*                                                                          */
/****************************************************************************/
/* Modification History:                                                    */
/* 29oct2015 rah - adapted from Respirometer chamber controller             */
/****************************************************************************/

#include "Generic.h"
#include <xc.h>
#include "sys_timer.h"
#include "dig_io.h"


/* for debug purposes */
//#include "dig_io.h"

static volatile UINT16 timerTick = 0;
static unsigned long tickCount = 0;

static void updateTickCount();

/* intialize the timer */
void tmrInit()
{
#ifdef O5_DB
    T2CON = 0x0010;                     /* Prescale divide by 8 */
    PR2 = (PBCLK/(8*TICKS_PER_SEC))-1;  /* set timer period    */
#else
    T2CON = 0x0000;                     /* No prescale         */
    PR2 = (PBCLK/TICKS_PER_SEC)-1;      /* set timer period    */
#endif

    /* clear timer count */
    TMR2 = 0x0000;           
    
    /* 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();
}

/* delay for a specified number of timer ticks */
void tmrDelayTicks(unsigned int ticks)
{
    unsigned long wait_ticks = tmrGetTicks() + ticks;
//    _DOZEN = 1;

    while ( wait_ticks > tmrGetTicks() )
        ;        /* wait for wait ticks to expire */

//    _DOZEN = 0;
}

/* 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*PBCLK)/64000)-1;     /* 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 slightly 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 = (((PBCLK/400)*(us-20))/2500)-1; /* 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();
    }
}

/* 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;
}


#define DEBUG_PINS

#ifdef DEBUG_PINS
#define tickPinOn()		LATBbits.LATB3 = 1
#define tickPinOff()	LATBbits.LATB3 = 0
#define dozePinOn()		digTx2En()
#define dozePinOff()	digTx2Dis()
#else
#define tickPinOn()
#define tickPinOff()
#define dozePinOn()
#define dozePinOff()
#endif

/********************************************/
/*		Timer2 ISR							*/
/********************************************/
void __attribute__ ((interrupt,no_auto_psv))_T2Interrupt(void)
{
	tickPinOn();

    /* increment the counter */
    ++timerTick;

    /* clear the interrupt flag */
    IFS0bits.T2IF = 0;

	tickPinOff();
}

/********************************************/
/*   Support for Doze mode when idle		*/
/********************************************/

#ifndef NO_IDLE
void Doze(void)
{
	CLKDIVbits.DOZEN = 1;
	dozePinOn();
}

void NoDoze(void)
{
	CLKDIVbits.DOZEN = 0;
	dozePinOff();
}
#endif
