/****************************************************************************/
/* Copyright 2003-2011 MBARI												*/
/****************************************************************************/
/* Summary	: SPI I/O Support for BEDS2 controller							*/
/* Filename : spi.c															*/
/* Author	: Robert Herlien (rah)											*/
/* Project	: Benthic Event Detection System (BEDS)							*/
/* Revision: 1.0															*/
/* Created	: 10/17/2011 from Respirometer spi.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:													*/
/* 10jan2003 rah - created for OASIS3										*/ 
/* 08dec2008 rah - modified for respirometer controller						*/ 
/* 17oct2011 rah - modified for BEDS										*/
/* 20feb2018 rah - modified for BEDS2 on PIC32MX470							*/
/****************************************************************************/

#include <mbariTypes.h>					/* MBARI type definitions			*/
#include <mbariConst.h>					/* MBARI constants					*/
#include <xc.h>							/* PIC32 register definitions		*/
#include <beds.h>						/* BEDS controller definitions		*/
#include <dig_io.h>						/* BEDS I/O definitions				*/
#include <spi.h>						/* BEDS SPI I/O definitions			*/
#include <string.h>						/* For memset						*/


/********************************/
/*		Global Data				*/
/********************************/

SpiSelect	spi1InUse=SPI_IDLE, spi2InUse=SPI_IDLE;


/********************************/
/*		Module Local Data		*/
/********************************/

MLocal unsigned int sdBRG = 15;


/************************************************************************/
/* Function	   : spiInit												*/
/* Purpose	   : Initialize SPI I/O										*/
/* Inputs	   : None													*/
/* Outputs	   : None													*/
/************************************************************************/
void spiInit(void)
{
    /* Map the pins             */
    SDI1R = 0x03;                       /* MISO1 = RPD11, pin 71        */
    RPD9R = 0x08;                       /* MOSI1 = RPD9, pin 69         */
    SDI2R = 0x0a;                       /* MISO2 = RPC4, pin 9          */
    RPC1R = 0x06;                       /* MOSI2 = RPC1, pin 6          */

    /* Setup SPI (local) Control Registers */
    SPI1CON = 0x10069;                  /* 8 bit, master, enh buf		*/
    SPI1BRG = 1;						/* 3 MHz						*/

    SPI2CON = 0x10129;                  /* 8 bit, master, enh buf       */
    SPI2BRG = 15;                       /* 400 KHz to start             */

    IPC7bits.SPI1IP = 5;                /* Set SPI1 Interrupt priority	*/
    IPC7bits.SPI1IS = 0;

    IPC8bits.SPI2IP = 5;                /* Set SPI2 Interrupt priority	*/
    IPC8bits.SPI2IS = 0;

} /* spiInit() */


/************************************************************************/
/* Function	   : spi1WriteRead											*/
/* Purpose	   : Write and Read n bytes from SPI1 bus					*/
/* Inputs	   : Ptr to write data, read data, num bytes to write/read	*/
/* Outputs	   : None													*/
/* Comments    : Calling routine must set baud rate and	assert			*/
/*				 appropriate /CS line before calling this				*/
/************************************************************************/
void spi1WriteRead(Byte *wp, Byte *rp, int nBytes)
{
	int		nSent, nRcvd;

	SPI1CONSET = _SPI1CON_ON_MASK;

	while (!SPI1STATbits.SPITBE)	/* First clear any bytes in FIFOs	*/
		;
	while (!SPI1STATbits.SPIRBE)
		*rp = SPI1BUF;

	SPI1STATCLR = _SPI1STAT_SPIROV_MASK;

	for (nSent = nRcvd = 0; nRcvd < nBytes; )
	{
		if (!SPI1STATbits.SPITBF && (nSent < nBytes))
			SPI1BUF = wp[nSent++];

		if (!SPI1STATbits.SPIRBE)
			rp[nRcvd++] = SPI1BUF;
	}
}


/************************************************************************/
/* Function	   : spi2WriteRead											*/
/* Purpose	   : Write and Read n bytes from SPI2 bus					*/
/* Inputs	   : Ptr to write data, read data, num bytes to write/read	*/
/* Outputs	   : None													*/
/* Comments    : Calling routine must set baud rate and assert			*/
/*				 appropriate /CS line before calling this				*/
/************************************************************************/
void spi2WriteRead(Byte *wp, Byte *rp, int nBytes)
{
	int		nSent, nRcvd;

	SPI2CONSET = _SPI2CON_ON_MASK;

	while (!SPI2STATbits.SPITBE)	/* First clear any bytes in FIFOs	*/
		;
	while (!SPI2STATbits.SPIRBE)
		*rp = SPI2BUF;

	SPI2STATCLR = _SPI2STAT_SPIROV_MASK;

	for (nSent = nRcvd = 0; nRcvd < nBytes; )
	{
		if (!SPI2STATbits.SPITBF && (nSent < nBytes))
			SPI2BUF = wp[nSent++];

		if (!SPI2STATbits.SPIRBE)
			rp[nRcvd++] = SPI2BUF;
	}
}

/************************************************************************/
/* Routines to set up and tear down SPI buses for various SPI peripherals*/
/************************************************************************/

/************************************************************************/
/* Function	   : spiSelectSD											*/
/* Purpose	   : Set up SPI for talking to SD card						*/
/* Inputs	   : None													*/
/* Outputs	   : None													*/
/************************************************************************/
void spiSelectSD(void)
{
	spi2InUse = SPI_SD;					/* Show SPI2 in use by SD		*/
	digADC_CUnsel();
	/* SD and ADC have same setup (for now), so don't change SPI2CON	*/
//	SPI2CONCLR = _SPI2CON_ON_MASK;		/* Must be off to set other bits*/
//	SPI2CON = 0x10129;					/* Set up SPI2CON for SD		*/
//	SPI2CONSET = _SPI2CON_ON_MASK;		/* Turn it on					*/
	SPI2BRG = sdBRG;					/* Set SPI2 baud				*/
	digSD_CSel();						/* Assert /CS for SD card		*/
}

/************************************************************************/
/* Function	   : spiSetSDBRG											*/
/* Purpose	   : Set SPI baud for talking to the SD Card				*/
/* Inputs	   : None													*/
/* Outputs	   : None													*/
/* Comment     : SD driver changes baud rates, so we remember last value*/
/************************************************************************/
void spiSetSDBRG(unsigned int brg)
{
	sdBRG = brg;						/* Remember SD baud rate		*/
	SPI2BRG = brg;						/*  and set it in SPI2			*/
}

/************************************************************************/
/* Function	   : spiSelectADC											*/
/* Purpose	   : Set up SPI for talking to A/D converter				*/
/* Inputs	   : None													*/
/* Outputs	   : None													*/
/************************************************************************/
void spiSelectADC(void)
{
	spi2InUse = SPI_ADC;				/* Show SPI2 in use by ADC		*/
	digADCEnbl();
	digSD_CUnsel();						/* Assert /CS for SD card		*/
	/* SD and ADC have same setup (for now), so don't change SPI2CON	*/
//	SPI2CONCLR = _SPI2CON_ON_MASK;		/* Must be off to set other bits*/
//	SPI2CON = 0x10129;					/* Set up SPI2CON for ADC		*/
//	SPI2CONSET = _SPI2CON_ON_MASK;		/* Turn it on					*/
	SPI2BRG = 59;						/* SPI2 baud = 100KHz			*/
	digADC_CSel();						/* Assert /CS for ADC			*/
}

/************************************************************************/
/* Function	   : spiSelectRTC											*/
/* Purpose	   : Set up SPI for talking to Real-Time Clock chip			*/
/* Inputs	   : None													*/
/* Outputs	   : None													*/
/************************************************************************/
void spiSelectRTC(void)
{
	spi1InUse = SPI_RTC;				/* Show SPI1 in use by RTC		*/
	digIMU_CUnsel();
	/* RTC and IMU have same setup (for now), so don't change SPI1CON	*/
//	SPI1CONCLR = _SPI1CON_ON_MASK;		/* Must be off to set other bits*/
//	SPI1CON = 0x10069;					/* Set up SPI1CON for RTC		*/
//	SPI1CONSET = _SPI1CON_ON_MASK;		/* Turn it on					*/
	SPI1BRG = 2;						/* SPI1 baud = 2MHz				*/
	digRTC_CSel();						/* Assert /CS for RTC			*/
}

/************************************************************************/
/* Function	   : spiSelectIMU											*/
/* Purpose	   : Set up SPI for talking to IMU							*/
/* Inputs	   : None													*/
/* Outputs	   : None													*/
/************************************************************************/
void spiSelectIMU(void)
{
	spi1InUse = SPI_IMU;				/* Show SPI1 in use by IMU		*/
	digRTC_CUnsel();					/* De-assert both /CS pins		*/
	/* RTC and IMU have same setup (for now), so don't change SPI1CON	*/
//	SPI1CONCLR = _SPI1CON_ON_MASK;		/* Must be off to set other bits*/
//	SPI1CON = 0x10069;					/* Set up SPI1CON for IMU		*/
//	SPI1CONSET = _SPI1CON_ON_MASK;		/* Turn it on					*/
	SPI1BRG = 2;						/* SPI1 baud = 2MHz				*/
	digIMU_CSel();						/* Assert /CS for IMU			*/
}

/************************************************************************/
/* Function	   : spi1Unselect											*/
/* Purpose	   : Turn off all SPI1 chip selects and SPI1 bus			*/
/* Inputs	   : None													*/
/* Outputs	   : None													*/
/************************************************************************/
void spi1Unselect(void)
{
	digRTC_CUnsel();					/* De-assert both /CS pins		*/
	digIMU_CUnsel();
	spi1InUse = SPI_IDLE;				/* Show nobody is using SPI1	*/
}

/************************************************************************/
/* Function	   : spi2Unselect											*/
/* Purpose	   : Turn off all SPI2 chip selects and SPI2 bus			*/
/* Inputs	   : None													*/
/* Outputs	   : None													*/
/************************************************************************/
void spi2Unselect(void)
{
	digSD_CUnsel();						/* De-assert both /CS pins		*/
	digADC_CUnsel();
	spi2InUse = SPI_IDLE;				/* Show nobody is using SPI2	*/
}
