/****************************************************************************/
/* Copyright 2003-2011 MBARI						    */
/****************************************************************************/
/* Summary  : I/O Support for BEDS System				    */
/* Filename : io.c							    */
/* Author   : Robert Herlien (rah)					    */
/* Project  : Benthic Event Detection System (BEDS)			    */
/* Revision : 1.0							    */
/* Created  : 10/14/2011 from Respirometer io.c				    */
/*									    */
/* 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:						    */
/* 14oct2011 rah - created from Respirometer io.c			    */
/* 10oct2002 rah - created from OASIS io.c				    */
/* $Log$
 */
/****************************************************************************/

#include <mbariTypes.h>			/* MBARI type definitions	    */
#include <mbariConst.h>			/* MBARI constants		    */
#include <cfxpico.h>			/* Persistor PicoDOS definitions    */
#include <beds.h>			/* BEDS controller definitions	    */
#include <io.h>				/* BEDS I/O definitions		    */
#include <serial.h>			/* BEDS serial I/O definitions	    */
#include <adc.h>			/* BEDS Analog Module		    */
#include <clock.h>			/* BEDS Clock module		    */

#include <stdlib.h>			/* Standard C library		    */
#include <twim.h>			/* BEDS software I2C		    */
#include <time.h>


#define WakeOnConsoleChar		/* Undef to not wake on console char*/


/********************************/
/*	External Data		*/
/********************************/

Extern volatile Nat32 accurateClkSec;	/* Num ints from DS3234		    */


/********************************/
/*	Global Data		*/
/********************************/

Global WhatWokeUs	wakeupStatus = WakeNone;
Global Int16		wdtTicker = -1;


/********************************/
/*	Module Local Data	*/
/********************************/

MLocal volatile Nat32	tick = 1L;    	/* 10 ms ticker			*/
MLocal vfptr		oldLevel4Handler, oldLevel5Handler, oldSpuriousHandler;
MLocal vfptr		oldLevel2Handler, oldLevel7Handler;

#ifdef RECIPE_PROTO
MLocal uchar	mirrorpins[] =
    { 1, 15, 16, 17, 18, 22, 23, 24, 25, 26, 27, 35, 37, 0 };

//MLocal uchar	mirrorpins[] = 
//    {1, 28, 31, 32, 35 };
#else
MLocal uchar	mirrorpins[] = 
    {1, 23, 26, 27, 31, 32, 0 };
#endif


/************************************************************************/
/* Function    : ioGetTick						*/
/* Purpose     : Return Value of system 10 ms ticker			*/
/* Inputs      : None							*/
/* Outputs     : Tick value						*/
/************************************************************************/
Nat32	ioGetTick(Void)
{
    return(tick);

} /* ioGetTick() */


/************************************************************************/
/* Function    : ioTick							*/
/* Purpose     : Handler for PIT ticker					*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
MLocal Void ioTick(Void)
{
    tick++;				/* Increment global ticker	*/

#ifdef WATCHDOG
    if (wdtTicker > 0)
	if (--wdtTicker == 0)
	{
	    TickleSWSR();		/* Keep watchdog running	*/
	    wdtTicker = TICKS_PER_SECOND;
	}
#endif

} /* ioTick() */


/************************************************************************/
/* Function    : irq4ISR						*/
/* Purpose     : Interrupt Handler for IRQ4				*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/* This single interrrupt service routine handles the IRQ4 interrupt	*/
/* and the very likely spurious interrupts that my be generated by the	*/
/* repeated asynchronous and non-acknowledged pulses of RS-232 input.	*/
/* The handler simply reverts IRQ4 back to input (to prevent further	*/
/* level sensative interrupts) and returns. It's assumed the routine	*/
/* that set this up is just looking for the side effect of breaking the	*/
/* CPU out of LPSTOP mode.						*/
/* Note that this very simple handler is defined as a normal C function	*/
/* rather than an IEV_C_PROTO/IEV_C_FUNCT. We can do this because we know*/
/* (and can verify by checking the disassembly) that is generates only	*/
/*  direct addressing instructions and will not modify any registers.	*/
/************************************************************************/
MLocal Void Irq4ISR(void)
{
    PinIO(IRQ4RXD);		// 31 // /IRQ4 (tied to Rx)
    wakeupStatus = WakeConsoleChar;
    RTE();
}


/************************************************************************/
/* Function    : irq5ISR						*/
/* Purpose     : Interrupt Handler for IRQ5				*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/* Comments    : See notes in irq4ISR					*/
/************************************************************************/
MLocal Void Irq5ISR(void)
{
    PinIO(IRQ5);
    wakeupStatus = WakeIRQ5;
    RTE();
}


/************************************************************************/
/* Function    : irq7ISR						*/
/* Purpose     : Interrupt Handler for IRQ7				*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/* Comments    : See notes in irq4ISR					*/
/************************************************************************/
MLocal Void Irq7ISR(void)
{
    PinIO(IRQ7);
    wakeupStatus = WakeIRQ7;
    RTE();
}


/************************************************************************/
/* Function    : altSerEnable						*/
/* Purpose     : Enable Tx for alt serial port (Rx always enabled)	*/
/* Inputs      : TRUE to enable alt Tx, FALSE to disable		*/
/* Outputs     : None							*/
/************************************************************************/
Void altSerEnable(MBool en)
{
    if (en)
	PIOSet(AUX_TX_OFF);
    else
	PIOClear(AUX_TX_OFF);

    PIOClear(AUX_RX_EN);
    
} /* altSerEnable() */


/************************************************************************/
/* Function    : ioNavPwr						*/
/* Purpose     : Turn on/off +3.3V to MPU6000				*/
/* Inputs      : Boolean, TRUE to turn power on				*/
/* Outputs     : None							*/
/************************************************************************/
Void	ioNavPwr(MBool on)
{
#ifndef RECIPE_PROTO
    if (on)
    {
	PIOWrite(MPU_PWR_EN, 1);
	twimSetSafe();
    }
    else
    {
	twimPwrOffState();
	PIOWrite(MPU_PWR_EN, 0);
    }
#endif
}


/************************************************************************/
/* Function    : ioSensorPwr						*/
/* Purpose     : Turn on/off +5V to system sensors			*/
/* Inputs      : Boolean, TRUE to turn power on				*/
/* Outputs     : None							*/
/************************************************************************/
Void	ioSensorPwr(MBool on)
{
#ifndef RECIPE_PROTO
    PIOWrite(SENSOR_PWR_EN, on ? 1 : 0);
#endif
}


/************************************************************************/
/* Function    : ioCheckMPUInt						*/
/* Purpose     : Return interrupt status from Invensense MPU board	*/
/* Inputs      : None							*/
/* Outputs     : Boolean, TRUE if interrupt asserted			*/
/************************************************************************/
MBool	ioCheckMPUInt(void)
{
    return(PIORead(MPU_IRQ_PIN) ? TRUE : FALSE);
}


/************************************************************************/
/* Function    : ioInit							*/
/* Purpose     : Initialize I/O system					*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
Void	ioInit(Void)
{
    TMGSetSpeed(CPU_CLOCK);		/* Set system clock speed	*/
    RTCInit(RTC_INT_LEVEL);
    PIOMirrorList(mirrorpins);		/* Set unused CPU pins as outputs*/
    PinIO(IRQ4RXD);
    PinIO(IRQ7);
    PinIO(IRQ5);

#ifdef RECIPE_PROTO
    PIOSet(LED1_PIN);			// Turn off LEDs on proto
    PIOSet(LED2_PIN);
#else
    ioSensorPwr(FALSE);
#endif

    altSerEnable(FALSE);		// Turn off TPU Tx, turn on Rx
    PIORead(MPU_IRQ_PIN);		// MPU IRQ as input
    ioNavPwr(FALSE);			// Turn off MPU6050 until I/O start
    PIOClear(UMPL_ON_PIN);
    PIOClear(SLEEP_PIN);

    /* Set up PIT and Int handlers	*/
    PITSet100usPeriod(10000/TICKS_PER_SECOND);
					/* Set our tick interval	*/
    PITAddChore(ioTick, TICK_INT_LEVEL);	/* Set tick interrupt handler	*/

    oldLevel5Handler = IEVInsertAsmFunct(Irq5ISR, level5InterruptAutovector);
    oldLevel4Handler = IEVInsertAsmFunct(Irq4ISR, level4InterruptAutovector);
    oldSpuriousHandler = IEVInsertAsmFunct(Irq4ISR, spuriousInterrupt);

} /* ioInit() */


/************************************************************************/
/* Function    : doLPStop						*/
/* Purpose     : Execute LPStop for one period, until interrupt wakes us*/
/* Inputs      : None							*/
/* Outputs     : Wakeup enum						*/
/* Comments    : IRQ7 no longer used.  Can wake from IRQ2, 4, or 5	*/
/************************************************************************/
MLocal WhatWokeUs doLPStop(Void)
{
    wakeupStatus = WakeNone;
  
    if (PinRead(IRQ2) == 0)
    {
	accurateClkSec++;
	enableClkInt();
	return(WakeIRQ2);
    }

#ifdef WakeOnConsoleChar
    if (PinRead(IRQ4RXD)==0)
	return(WakeConsoleChar);
#endif

    if (PinRead(IRQ5)==0)
	return(WakeIRQ5);

    CPUWriteInterruptMask(6);	/* See persistor.com discussion on	*/
				/* IRQ4 and spurious interrupts		*/
    PinBus(IRQ5);
#ifdef WakeOnConsoleChar
    PinBus(IRQ4RXD);
#endif
    PinBus(IRQ2);

    LPStopCSE(FastStop);

    PinIO(IRQ5);
    PinIO(IRQ4RXD);

    if (wakeupStatus == WakeNone)
	wakeupStatus = WakeTmout;

    return(wakeupStatus);

} /* doLPStop() */


/************************************************************************/
/* Function    : ioSleep						*/
/* Purpose     : Go to low-power sleep					*/
/* Inputs      : Number of 10 ms ticks to sleep				*/
/* Outputs     : Wakeup enum						*/
/************************************************************************/
WhatWokeUs ioSleep(Nat32 sleepTicks)
{
    Int32	timeToStop;
    WhatWokeUs	rtn;

    twimSetSafe();				// Set I2C pins
//    adcPowerDown();				// Never want adc pwrdown
    CIOdrain();					// let any pending UART data finish

#ifndef TEAPOT
    altSerEnable(FALSE);			// AUX 232s: Rx On, Tx Off
    EIAForceOff(TRUE);				// turn off the RS232 driver
#endif

    PIOSet(SLEEP_PIN);
    for (timeToStop = tick + sleepTicks; (Int32)(timeToStop - tick) > 0; )
	if ((rtn = doLPStop()) != WakeTmout)
	    break;

    PIOClear(SLEEP_PIN);

#ifndef TEAPOT
    setSerTxEnable();				// Re-enable Tx if in use
#endif

    return(rtn);

} /* io_pwrdown() */
