/*************************************************************
23X17Eval.C

This program is used for the MCP23X17EV board.

Written for MPLAB HiTech C (PICC)



**************************************************************/
#include	<pic.h>
#include "I2C.H"
#include "SPI.H"
#include "MCP23X17.H"

//Function prototypes
void InitPIC(void);
void InitMCP(void);
void CheckButtonCapture(void);
void SetLEDPattern(void);
void SwitchDevices(void);
void Write23X17(unsigned char reg, unsigned char data);
unsigned char Read23X17(unsigned char reg);
unsigned char I2CReadByte(unsigned char reg);
void I2CWriteByte(unsigned char reg, unsigned char data);
void SetSSP(unsigned char mode);
unsigned char SPIReadByte(unsigned char reg);
void SPIWriteByte(unsigned char reg, unsigned char data);
void Delay_ms(long num_ms);

#define I2CMODE 0
#define SPIMODE 1

#define WrtCmd  0
#define RdCmd   1

#define Mode00  0
#define Mode11  1

#define CSL RB3 = 0
#define CSH RB3 = 1

#define MUX_IN1	RA0			//HIGH routes to I2C
#define MUX_IN3	RA1			//HIGH routes to SPI
#define SEL_SWITCH RA2	//Selector switch input

#define	POR_DEFAULT	0x0F
#define	KR					0x0E
#define	SCROLL			0x0D
#define	ALT					0x0B
#define	BLINK				0x07
//*********MACROS******************************

#define SetI2C { \
  MUX_IN3 = 0; \
  MUX_IN1 = 1; \
  }

#define SetSPI { \
  MUX_IN1 = 0; \
  MUX_IN3 = 1; \
  }

//*********GLOBALS******************************
unsigned char	ButtonFlag = 0x00;	//Set by ISR if interrupted by 
unsigned char	ButtonLatch = 0x00;	//1 = KR, 2 = Scroll, 4 = Alt, 8 = Blink, 0xFF = (default)
unsigned int 	LEDPattern = 0x000;		//This is the pattern which gets manipulated via algorithms
unsigned char gAddrPins = 0;
unsigned char I2CBaudGenerator = 125;
unsigned char SerialMode = I2CMODE;
unsigned char SPIClkMode = Mode00;
unsigned char gControlByte = 0x40;
unsigned char OLDSwitch = 0xFF;
unsigned char Direction = 0x00;
unsigned int  Count = 0x00;

__CONFIG(INTIO & WDTDIS & MCLREN & LVPDIS & PWRTEN);

/****************************************
	ISR()
****************************************/
void interrupt isr(void)
{
  if(INTF)
  {
  	ButtonFlag = 1;
  	INTF = 0;
  }
}

/****************************************
	main()
Start - 
InitPIC
InitMCP
	Set Mode to I2C
	Configure 23017
	Set Mode to SPI
	Configure 23S17
Check toggle switch
  Set proper serial mode
  Make other device all inputs
Read GP inputs
Drive outputs to match inputs
goto -> Check Toggle Switch
****************************************/
void main(void)
{
	InitPIC();
	InitMCP();

	while(1)
	{
		if(SEL_SWITCH != OLDSwitch)	//Precondition MCP23X17's only if switch is changed
			SwitchDevices();
	  
		if(ButtonFlag)
		  CheckButtonCapture();

	  SetLEDPattern();
	}
}

/*************************************************************
   Function Name:  InitPIC                                         
   Return Value:   void                                           
   Parameters:     void                     
   Description:    Initializes the PIC       
**************************************************************/
void InitPIC( void )
{
  OSCCON = 0x70;	// Set internal RC frequency
  T0CS = 0;				// Timer 0 source is CLK0
  PS2 = 1;				// Prescaler bit for Timer 0
  PS1 = 1;				// Prescaler bit for Timer 0
  PS0 = 1;				// Prescaler bit for Timer 0

	ADCON1 = 0x06;  // Digital pins
	TRISA0 = 0;			// Set up the 4066 pins
	TRISA1 = 0;			// Set up the 4066 pins
	TRISA2 = 1;			// Selector switch input
	
	TRISB0 = 1;     // INT pin
	TRISB3 = 0;			// Chip Select
	INTEDG = 0;     // INT on falling edge
	INTE = 1;       // Enable INT pin
	GIE = 1;        // Global interrupt enable
}

/*************************************************************
   Function Name:  InitMCP                                         
   Return Value:   void                                           
   Parameters:     void                     
   Description:    Initializes the MCP23X17 devices       
**************************************************************/
void InitMCP( void )
{
//Set Mode to I2C
	SetSSP(I2CMODE);

//Configure 23017
  Write23X17(GPPUA,  0x0F);   // Pullups
  Write23X17(IOCONA, 0x40); 	//
  Write23X17(IODIRA, 0xFF);   //All inputs by default (this is the POR default)
  Write23X17(IODIRB, 0xFF);   //All inputs by default (this is the POR default)
//Set Mode to SPI
	SetSSP(SPIMODE);
//Configure 23S17
  Write23X17(GPPUA,  0x0F);   // Pullups
  Write23X17(IOCONB, 0x40);   //
  Write23X17(IODIRA, 0xFF);   //All inputs by default (this is the POR default)
  Write23X17(IODIRB, 0xFF);   //All inputs by default (this is the POR default)
}

/*************************************************************
   Function Name:	CheckButtonCapture                                         
   Return Value:  void                                           
   Parameters:    void                     
   Description:   Reads the INTCAPA register and places a value
   								in a global (LEDPattern). If no button, do
   								algorithm on global (LEDPattern). Then calls
   								SetLEDPattern.
		Sets LEDPattern variable:
		POR_DEFAULT	
		KR					
		SCROLL			
		ALT					
		BLINK				
		
		Sends to SetLEDPattern:
		POR_DEFAULT	
		KR					
		SCROLL			
		ALT					
		BLINK				
**************************************************************/
void CheckButtonCapture(void)
{
	unsigned char n;
	n = Read23X17(INTCAPA) & 0x0F;
	  switch (n)
	  {
	   case KR: //
	   	LEDPattern = 0xC00;
  		ButtonLatch = n;
    	Direction = 0;
     	Count = 0;
	   	break;

	   case SCROLL: //
	   	LEDPattern = 0xFFE;
  		ButtonLatch = n;
	   	break;

	   case ALT: //
	   	LEDPattern = 0xAAA;
  		ButtonLatch = n;
	   	break;

	   case BLINK: //
	   	LEDPattern = 0xFC0;
  		ButtonLatch = n;
	   	break;

	   default:
	   //No Change
	   	break;
	  }
  	ButtonFlag = 0;		//Clear the flag
}

/*************************************************************
   Function Name:  SetLEDPattern                                         
   Return Value:   void                                           
   Parameters:     void                    
   Description:    Sets the pattern value for the LED      
		ButtonLatch: KR, Scroll, ALT, Blink
		
**************************************************************/
void SetLEDPattern()
{
  Write23X17(GPIOA, LEDPattern << 4);//
 	Write23X17(GPIOB, (LEDPattern & 0xFF0) >> 4); //
  
  switch (ButtonLatch)
  {
      case KR: //
     	  if(Count >= 0x0A)
 	      {  
      		Direction = ~Direction;
      		Count = 0;
 	      }
 	      if (Direction)
 	      LEDPattern = LEDPattern << 1;	//Shift the bits by 1 bit
 	      else
 	      LEDPattern = LEDPattern >> 1;	//Shift the bits by 1 bit
 	      Count++;
       	Delay_ms(75);
      	break;

      case SCROLL: //
   	    LEDPattern = LEDPattern << 1;	//Shift the bits by 1 bit
       	if(LEDPattern == 0xC000)
 	        LEDPattern = 0xFFE;
       	Delay_ms(75);
      	break;

      case ALT: //
        LEDPattern = ~LEDPattern;
			 	Delay_ms(200);
      	break;

      case BLINK: //
        LEDPattern = ~LEDPattern;
			 	Delay_ms(200);
      	break;

      default:
        LEDPattern = ~LEDPattern;
			 	Delay_ms(500);
      	break;
  }
}

/*************************************************************
   Function Name:  SwitchDevices                                         
   Return Value:   void                                           
   Parameters:     void                    
   Description:    Switches between the two MCP23X17 devices     
**************************************************************/
void SwitchDevices()
{
  unsigned char n;
	OLDSwitch = SEL_SWITCH;
	ButtonLatch = 0x00;
	LEDPattern = 0x000;   //Reset the LED Pattern for visual purposes
	
	 if(SEL_SWITCH)   //Select I2C device (MCP23017)
	 {
	 	//First configure 23S17 (SPI) to all inputs so it does not interfere
	 	SetSSP(SPIMODE);
	  Write23X17(GPINTENA, 0x00); // Disable Int-on-Change
	 	n = Read23X17(GPIOA);				// Read GPIO to clear INT 'Just in Case'
	  Write23X17(IODIRA, 0xFF);   // Make all inputs
	 	Write23X17(IODIRB, 0xFF);   // Make all inputs
	 	
	 	//Now configure the I/O on the selected device
	 	SetSSP(I2CMODE);
	  Write23X17(GPINTENA, 0x0F); // Enable Int-on-Change
	  Write23X17(IODIRA, 0x0F);   //GPA<7:4> = outputs; GPA<3:0> = inputs;
	 	Write23X17(IODIRB, 0x00);   //GPB<7:0> = outputs
	 }
	 else						//Select SPI device (MCP23S17)
	 {
	 	//First configure 23017 (I2C) to all inputs so it does not interfere
	 	SetSSP(I2CMODE);	
	  Write23X17(GPINTENA, 0x00); // Disable Int-on-Change
	 	n = Read23X17(GPIOA);				// Read GPIO to clear INT 'Just in Case'
	  Write23X17(IODIRA, 0xFF);   //Make all inputs
	 	Write23X17(IODIRB, 0xFF);   //Make all inputs
	
	 	//Now configure the I/O on the selected device
	 	SetSSP(SPIMODE);
	  Write23X17(GPINTENA, 0x0F); // Enable Int-on-Change
	  Write23X17(IODIRA, 0x0F);   // GPA<7:4> = outputs; GPA<3:0> = inputs;
	 	Write23X17(IODIRB, 0x00);   // GPB<7:0> = outputs
	 }
}

/*************************************************************
   Function Name:  Delay_ms                                         
   Return Value:   void                                           
   Parameters:     Number of milliseconds                    
   Description:    Delay function
                   TMR0 times out every ~100 us     
**************************************************************/
void Delay_ms(long num_ms)
{
  long n,m;
  
  m = num_ms*8;

	TMR0 = 0x50;
	TMR0IF = 0;
  
  for(n = 0; n < m; n++)
  {
    while(!TMR0IF);  //Wait for timer flag
    TMR0IF = 0;      //Clear flag
  }
}

/*************************************************************
   Function Name:  Write23X17                                         
   Return Value:   void                                           
   Parameters:     Register address, Data                    
   Description:    Writes a 23X17 register. I2C or SPI is in
                   global byte     
**************************************************************/
void Write23X17(unsigned char reg, unsigned char data)
{
	if(SerialMode == I2CMODE)	//If 23017 selected
 	  I2CWriteByte(reg, data); // 
	else											//Else 23S17 is selected
	  SPIWriteByte(reg, data); //
}

/*************************************************************
   Function Name:  Read23X17                                         
   Return Value:   void                                           
   Parameters:     Register address                    
   Description:    Reads a 23X17 register. I2C or SPI is in
                   global byte     
**************************************************************/
unsigned char Read23X17(unsigned char reg)
{
  unsigned char num;
	if(SerialMode == I2CMODE)	//If 23017 selected
		num = I2CReadByte(reg);
	else											//Else 23S17 is selected
		num = SPIReadByte(reg);
	return(num);
}

//****************************************
//	SetSSP(mode)
//****************************************
void SetSSP(unsigned char mode)
{  
	SSPEN = 0;				      // disable other peripheral

  //Setup I2C
	if (mode == I2CMODE)
	{  
    OpenI2C(MASTER, SLEW_OFF);

		CKP = 1;
		SSPADD = I2CBaudGenerator;
		RB1 = 0;		//Clear latch
		RB4 = 0;		//Clear latch
		TRISB1 = 1;	//SDA
		TRISB4 = 1;	//SCL
		
		//Sets I2C mode
		SetI2C;							  //Configures the MUX
		SerialMode = I2CMODE;	//Global variable
  }
  //Setup SPI
  else if (mode == SPIMODE)
  {

    if(SPIClkMode == Mode11)        //Mode 11
  		OpenSPI( SPI_FOSC_16, MODE_11, SMPMID);
    else if (SPIClkMode == Mode00)  //Mode 00
  		OpenSPI( SPI_FOSC_16, MODE_00, SMPMID );

		TRISB3 = 0;		// CS
		TRISB2 = 0;		// SDO
		TRISB1 = 1;		// SDI
		TRISB4 = 0;		// SCK
		CSH;
		
		//Set SPI
		SetSPI;								//Configures MUX
		SerialMode = SPIMODE;		//Global variable
  }
}

//****************************************
//	I2CWriteByte(unsigned char addr, unsigned char byte)
//	Writes a byte to the 23017
//****************************************
void I2CWriteByte(unsigned char reg, unsigned char data)
{
	I2CStart();
	WriteI2C( gControlByte | WrtCmd | gAddrPins );
	WaitForACK();
	WriteI2C( reg );
	WaitForACK();
	WriteI2C( data );
	WaitForACK();
	I2CStop();
}

//****************************************
//	int I2CReadByte(unsigned char addr))
//	Reads a byte from the 23008	
//****************************************
unsigned char I2CReadByte(unsigned char reg)
{
	unsigned char num;

	I2CStart();
	WriteI2C( gControlByte | WrtCmd | gAddrPins );
	WaitForACK();
	WriteI2C( reg );
	WaitForACK();
	I2CReStart();;
	WriteI2C( gControlByte | RdCmd | gAddrPins );
	WaitForACK();
	num = ReadI2C();
	nACK();
	I2CStop();
	return(num);
}

//*****************************************************************
//     Function Name:    SPIWriteByte                                
//     Return Value:     None                                        
//     Parameters:       Addr and data.               
//     Description:      This routine performs a byte write.         
//*******************************************************************
void SPIWriteByte( unsigned char reg, unsigned char data )
{
  CSL;                        // Enable SPI Communication to MCP2515
  while( WriteSPI(gControlByte | WrtCmd | gAddrPins) );
  while( WriteSPI(reg) );
  while( WriteSPI(data) );
  CSH;                       // Disable SPI Communication to MCP2515
}

//*****************************************************************
//     Function Name:    SPIReadByte                                
//     Return Value:     Data at register                                        
//     Parameters:       Register
//     Description:      This routine performs a sequential write.         
//*******************************************************************
unsigned char SPIReadByte(unsigned char reg)
{
  unsigned char n;
  
  CSL;                        // Enable SPI Communication to MCP2515
  while( WriteSPI(gControlByte | RdCmd | gAddrPins) );
  while( WriteSPI(reg) );
  n = ReadSPI();
  CSH;                       // Disable SPI Communication to MCP2515
  
  return n;
}
