/**************************************************************************************************/
/* LOW VOLTAGE LOAD SWITCHER BOARD																  */
/* Copyright 2012 MBARI.                                                    					  */
/* MBARI Proprietary Information. All rights reserved.                    			 			  */
/* FILENAME: 		expander.c																	  */
/* DESCRIPTION:		This file contains functions used to control the LVLS 						  */
/*					board's digital I/O expanders.												  */
/* FUNCTION LIST:	expInit()																	  */
/*					expReset(int device_select)													  */
/*					expClr(int device_select, int regstr)										  */
/*					expRead(int device_select, int regstr)										  */
/*					expWrite(int device_select, int regstr, int data)							  */
/*					expSetHigh(int device_select, int regstr, int val)							  */
/*					expSetLow(int device_select, int regstr, int val)							  */
/*					expReadBit(int device_select, int regstr, int val)							  */
/*					expDigInit()																  */
/**************************************************************************************************/
#include <p24fxxxx.h>
#include <stdio.h>

#include "dig_io.h"
#include "sys_defs.h"
#include "spi2.h"
#include "expander.h"
#include "actions.h"
#include "serial.h"
#include "sys_timer.h"

////////////////////////////////////////////////////////////////////////////////////////////////////
//	NAME:			expInit()
//	DESCRIPTION:	This function initializes the LVLS board's digital I/O Expanders.
////////////////////////////////////////////////////////////////////////////////////////////////////
void expInit()
{
	expReset(DIO_01_CS);	//Reset DIO_01_CS on startup
	expReset(DIO_05_CS);	//Reset DIO_05_CS on startup

	//************ DIO_01 ONLY ************

	// SET GPPU (A)
	// use external pull ups
	expWrite(DIO_01_CS, 0x0C, 0x00);	 
			
	// SET GPPU (B)
	// use external pull ups
	expWrite(DIO_01_CS, 0x0D, 0x00);	
	
	// SET IOCON (A)
	// bank = 1, mirrored interrupts, sequential op disabled, INT is active driver output
	// int pin active high, read GPIO to clear interrupt
	expWrite(DIO_01_CS, 0x0A, 0xE2);	

	// SET IOCON (B)
	// bank = 1, mirrored interrupts, sequential op disabled, INT is active driver output
	// int pin active high, read GPIO to clear interrupt
	expWrite(DIO_01_CS, 0x15, 0xE2);

	// SET IODIR (A)
	// A7, A6, A5, A3, A2 assigned as inputs
	expWrite(DIO_01_CS, 0x00, 0xEC);

	// SET IODIR (B)
	// B7, B6, B5, B3, B2 assigned as inputs
	expWrite(DIO_01_CS, 0x10, 0xEC);
		
	// SET GPINTEN (A) on DIO_01
	// ENABLE interrupt on change for A7, A6, A5 on DIO_01
	expWrite(DIO_01_CS, 0x02, 0xE0);
	
	// SET GPINTEN (B) on DIO_01
	// ENABLE interrupt on change for B7, B6, B5 on DIO_01
	expWrite(DIO_01_CS, 0x12, 0xE0);

	// SET INTCON (A) on DIO_01
	// do not compare any pins to DEFVAL register
	expWrite(DIO_01_CS, 0x04, 0x00);
	
	// SET INTCON (B) on DIO_01
	// do not compare any pins to DEFVAL register
	expWrite(DIO_01_CS, 0x14, 0x00);

	//************ DIO_05 ONLY ************

	// SET GPPU (A)
	// use external pull ups
	expWrite(DIO_05_CS, 0x0C, 0x00);	 
			
	// SET GPPU (B)
	// use external pull ups
	expWrite(DIO_05_CS, 0x0D, 0x00);	
	
	// SET IOCON (A)
	// bank = 1, no mirror, sequential op disabled, INT is active driver output
	// int pin active high, read GPIO to clear interrupt
	expWrite(DIO_05_CS, 0x0A, 0x84);	

	// SET IOCON (B)
	// bank = 1, no mirror, sequential op disabled, INT is active driver output
	// int pin active high, read GPIO to clear interrupt
	expWrite(DIO_05_CS, 0x15, 0x84);

	// SET GPINTEN (A) on DIO_05
	// disable interrupt on change for all pins on DIO_05
	expWrite(DIO_05_CS, 0x02, 0x00);
	
	// SET GPINTEN (B) on DIO_05
	// disable interrupt on change for all pins on DIO_05
	expWrite(DIO_05_CS, 0x12, 0x00);

	// SET IODIR (A)
	// all GPIOA pins assigned as outputs
	expWrite(DIO_05_CS, 0x00, 0x00);

	// SET IODIR (B)
	// all GPIOB pins assigned as outputs
	expWrite(DIO_05_CS, 0x10, 0x00);

	// DIGITAL I/O INIT
	expDigInit();				
			
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//	NAME: 			expReset(int device_select)
//	DESCRIPTION:	This function resets the LVLS board's digital I/O Expanders.
////////////////////////////////////////////////////////////////////////////////////////////////////
void expReset(int device_select)
{
	switch(device_select)
	{
		case 0:					//RESET DIO_01
		{
			PIC_DIO_01_RST = 0;					
			tmrDelayMs(1);
			PIC_DIO_01_RST = 1;
		}
		case 1:					//RESET DIO_02
		{
			PIC_DIO_02_RST = 0;					
			tmrDelayMs(1);
			PIC_DIO_02_RST = 1;
		}
		case 2:					//RESET DIO_03
		{
			PIC_DIO_03_RST = 0;					
			tmrDelayMs(1);
			PIC_DIO_03_RST = 1;
		}
		case 3:					//RESET DIO_04
		{
			PIC_DIO_04_RST = 0;					
			tmrDelayMs(1);
			PIC_DIO_04_RST = 1;
		}
		case 4:					//RESET DIO_05
		{
			PIC_DIO_05_RST = 0;					
			tmrDelayMs(1);
			PIC_DIO_05_RST = 1;	
		}		
	}

}

////////////////////////////////////////////////////////////////////////////////////////////////////
//	NAME:			expClr(int device_select, int regstr)
//	DESCRIPTION:	This function clears the value (sets to 0x00) of a GPIO register in the
//					LVLS board's digital I/O Expanders , as well as clearing the slot in the 
//					array that holds the value of that register (if that register is a GPIO).
////////////////////////////////////////////////////////////////////////////////////////////////////
void expClr(int device_select, int regstr)
{
	expWrite(device_select, regstr, 0x00);				//CLEAR REGISTER
	
	if (regstr == 0x09)
	{
		expPrevState[device_select][0] = 0;				//SET GPIOA = 0 WITHIN ARRAY
	}
	else if (regstr == 0x19)
	{
		expPrevState[device_select][1] = 0;				//SET GPIOB = 0	WITHIN ARRAY
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//	NAME:			expRead(int device_select, int regstr)
//	DESCRIPTION:	This function performs a simple read of any register in the 
//					LVLS board's digital I/O Expanders.
////////////////////////////////////////////////////////////////////////////////////////////////////
int expRead(int device_select, int regstr)
{
	int data;

	CSAddSet(device_select);			// SELECT DEVICE
	data = ReadWriteSPI2(0x41);			// READ COMMAND
	data = ReadWriteSPI2(regstr);		// REGISTER ADDRESS(0x09 = GPIOA, 0x19 = GPIO)
	data = ReadWriteSPI2(0x00);			// CLOCK IN DATA ON THESE PULSES
//	data = ReadWriteSPI2(0x00);			// CLOCK IN DATA ON THESE PULSES
	CSAddSet(DESELECT);					// DESELECT DEVICE
	
	return data;

}

////////////////////////////////////////////////////////////////////////////////////////////////////
//	NAME:			expWrite(int device_select, int port, int data)
//	DESCRIPTION:	This function performs a simple write to any register in the 
//					LVLS board's digital I/O Expanders.
////////////////////////////////////////////////////////////////////////////////////////////////////
void expWrite(int device_select, int regstr, int data)
{
	int dummy;

	CSAddSet(device_select);			// SELECT DEVICE
	dummy = ReadWriteSPI2(0x40);		// WRITE COMMAND
	dummy = ReadWriteSPI2(regstr);		// REGISTER ADDRESS (0x09 = GPIOA, 0x19 = GPIOB)
	dummy = ReadWriteSPI2(data);		// SEND NEW VALUE OF REGISTER
	CSAddSet(DESELECT);					// DESELECT DEVICE
		
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//	NAME:			expSetHigh(int device_select, int regstr, int val)
//	DESCRIPTION:	This function sets an individual bit high in the digital I/O Expanders' 
//					GPIO registers, without changing the value of the other bits 
//					in that register.
////////////////////////////////////////////////////////////////////////////////////////////////////
void expSetHigh(int device_select, int regstr, int val)
{
	int c, j, test, reg, newVal = 0;
	int dummyArray[5][2];
	int maskArray[5][2];

	//CHECK WHICH PORT HAS BEEN SELECTED
	if(regstr == GPIOA)						
		{
			reg = 0;								//PORT A has been selected
		}
	else
		{
			reg = 1;								//PORT B has been selected
		}

	//INITIALIZE ARRAYS
	for (c = 0; c<=4; c++)							//initialize dummyArray
	{	
		for (j=0; j<=1; j++)
		{
		dummyArray[c][j] = expPrevState[c][j];		//set equal to array holding previous values
		}
	}

	for (c = 0; c<=4; c++)							//initialize maskArray
	{	
		for (j=0; j<=1; j++)
		{
		maskArray[c][j] = 0;						//make all elements in maskArray = 0
		}
	}

	//SET CORRESPONDING BIT ON maskArray FOR SELECTED DEVICE AND REGISTER
	maskArray[device_select][reg] = val;

	//SET NEW VALUE OF expPrevState BY PERFORMING LOGICAL "OR" ON dummyArray AND maskArray
	//This preserves the previous settings of the GPIO port while still allowing changes
	expPrevState[device_select][reg] = dummyArray[device_select][reg]|maskArray[device_select][reg];

	//FOR DEBUGGING PURPOSES
/*	for (c = 0; c<=4; c++)		
		{	
			for (j=0; j<=1; j++)
			{
				test = expPrevState[c][j];
				printf("%x ", test);
			}
			printf("\r\n");
		}
			printf("\r\n");
*/
	//ASSIGN SELECTED VALUE OF ARRAY TO A VARIABLE USED TO WRITE THE NEW VALUE OF THE REGISTER
	newVal = expPrevState[device_select][reg];

	//WRITE NEW VALUE OF THE REGISTER
	expWrite(device_select, regstr, newVal);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//	NAME:			expSetLow(int device_select, int regstr, int val)
//	DESCRIPTION:	This function sets an individual bit low in the digital I/O Expanders' 
//					GPIO registers, without changing the value of the other bits 
//					in that register.
////////////////////////////////////////////////////////////////////////////////////////////////////
void expSetLow(int device_select, int regstr, int val)
{
	int c, j, test, reg, newVal = 0;
	int dummyArray[5][2];
	int maskArray[5][2];

	//CHECK WHICH PORT HAS BEEN SELECTED
	if(regstr == GPIOA)						
		{
			reg = 0;								//GPIO A has been selected
		}
	else
		{
			reg = 1;								//GPIO B has been selected
		}

	//INITIALIZE ARRAYS
	for (c = 0; c<=4; c++)							//initialize dummyArray
	{	
		for (j=0; j<=1; j++)
		{
		dummyArray[c][j] = expPrevState[c][j];		//set equal to array holding previous values
		}
	}

	for (c = 0; c<=4; c++)							//initialize maskArray
	{	
		for (j=0; j<=1; j++)
		{
		maskArray[c][j] = 0;						//make all elements in maskArray = 0
		}
	}

	//SET CORRESPONDING BIT ON maskArray FOR SELECTED DEVICE AND REGISTER
	maskArray[device_select][reg] = val;

	//SET NEW VALUE OF expPrevState BY PERFORMING LOGICAL "XOR" ON dummyArray AND maskArray
	//This preserves the previous settings of the GPIO port while still allowing changes
	//If statement (with embedded logical "and") prevents setting a bit high accidentally 
	//if bit is already low

	if((dummyArray[device_select][reg] & maskArray[device_select][reg]) != 0)
	{
	expPrevState[device_select][reg] = dummyArray[device_select][reg]^ maskArray[device_select][reg];
	}

	//ASSIGN SELECTED VALUE OF ARRAY TO A VARIABLE USED TO WRITE THE NEW VALUE OF THE REGISTER
	newVal = expPrevState[device_select][reg];

	//WRITE NEW VALUE OF THE REGISTER
	expWrite(device_select, regstr, newVal);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//	NAME:			expReadBit(int device_select, int regstr, int val)
//	DESCRIPTION:	This function performs a read of an individual bit within a register in the 
//					LVLS board's digital I/O Expanders. 
//	NOTES:			Returns true if bit is set, false if bit is not set.
////////////////////////////////////////////////////////////////////////////////////////////////////
int expReadBit(int device_select, int regstr, int val)
{
	int state, result;
	state = expRead(device_select, regstr);
	result = state&val;							//performs logical "AND".  If bit is set, result will be non-zero.

	if(result != 0)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//	NAME:			expDigInit()
//	DESCRIPTION:	This function initializes the values of each of the I/Os associated 
//					with each DIO expander, in a manner analagous to that done in 
//					digInit().  
//	NOTES:			Output initialization routines will be added in later versions of the 
//					board for DIOs 02, 03, & 04.
////////////////////////////////////////////////////////////////////////////////////////////////////

void expDigInit()
{
	// DIO_01 OUTPUTS
	expSetLow(DIO_01_CS, GPIOB, GP0);				//RELAY_01_ON 		init LOW
	expSetLow(DIO_01_CS, GPIOB, GP1);				//RELAY_01_OFF 		init LOW
	expSetHigh(DIO_01_CS, GPIOB, GP4);				//CKT_BRKR_01_RST 	init HIGH

	expSetLow(DIO_01_CS, GPIOA, GP0);				//RELAY_02_OFF 		init LOW
	expSetLow(DIO_01_CS, GPIOA, GP1);				//RELAY_02_ON 		init LOW
	expSetHigh(DIO_01_CS, GPIOA, GP4);				//CKT_BRKR_02_RST 	init HIGH

	// DIO_05 OUTPUTS
	expSetHigh(DIO_05_CS, GPIOB, GP0);				//POT_01_WP			init HIGH
	expSetHigh(DIO_05_CS, GPIOB, POT_01_RST);		//POT_01_RST		init HIGH
	expSetHigh(DIO_05_CS, GPIOB, GP2);				//POT_02_WP			init HIGH
	expSetHigh(DIO_05_CS, GPIOB, POT_02_RST);		//POT_02_RST		init HIGH
	
}