/*◄■■■■■■■■■■■■■■■■■■■■■■■■■■■■ MCP23017_IOExpndr.c ■■■■■■■■■■■■■■■■■■■■■■■■■►*/

// <editor-fold defaultstate="collapsed" desc="Title">
/******************************************************************************/
/*     MCP23xxx 8 & 16 bit I/O Expander API using interrupt driven I²C        */
/*                                                                            */
/* For Microchip MCP23008 & MCP23017 8 & 16-Bit I/O Expander with I²C         */
/* Provisions for handling upto 8 chips on the bus                            */
/*                                                                            */
/*                                                                            */
/* Created on Thursday, March 2, 2017                                         */
/* © John William Darsow                                                      */
/* Current Version Date: (see File Date)                                      */
/* Compiled using: MPLAB X IDE v3.45 & XC8 v1.38                              */
/*                                                                            */
/* Modifications,UpDates and ToDo:                                            */
/*                                                                            */
/******************************************************************************/
// </editor-fold>

/*═════════════════════════════════ INCLUDEs ═════════════════════════════════*/
#include <stdlib.h>
#include "MCP23017_IOExpndr.h"

/*═══════════════════════════ VARIABLE DEFINITIONS ═══════════════════════════*/
// <editor-fold defaultstate="collapsed" desc="Variable Definitions">

/* Do to the versatility of these I/O chips and the multiple use on a bus the following are only a suggested start.
   Weather to
   * Copy this library to project/Peripherals and modify it
   * Move Variables to the application program
   * Make an interface file in project/Peripherals 
   it's up to you the programmer.
 * It is important to take care when changing the BANK bit as the address mapping changes after the byte is
   clocked into the device.
 */

//Byte            ioDIR;                 // I/O DIRECTION REGISTER                     (ADDR 0x00)
//ioDIRbits_t     ioDIRbits;             //? extern volatile
//Byte            ioIPOL;                // INPUT POLARITY PORT REGISTER               (ADDR 0x01) 
//ioIPOLbits_t    ioIPOLbits;            //? extern volatile
//Byte            ioGPINTEN;             // INTERRUPT-ON-CHANGE PINS                   (ADDR 0x02)
//ioGPINTENbits_t ioGPINTENbits;         //? extern volatile
//Byte            ioDEFVAL;              // DEFAULT VALUE REGISTER                     (ADDR 0x03)
//ioDEFVALbits_t  ioDEFVALbits;          //? extern volatile
//Byte            ioINTCON;              // INTERRUPT-ON-CHANGE CONTROL REGISTER       (ADDR 0x04)
//ioINTCONbits_t  ioINTCONbits;          //? extern volatile
//Byte            ioCON;                 // I/O EXPANDER CONFIGURATION REGISTER        (ADDR 0x05)
//ioCONbits_t     ioCONbits;             //? extern volatile
//Byte            ioGPPU;                // GPIO PULL-UP RESISTOR REGISTER             (ADDR 0x06)
//ioGPPUbits_t    ioGPPUbits;            //? extern volatile
//Byte            ioINTF;                // INTERRUPT FLAG REGISTER                    (ADDR 0x07)
//ioINTFbits_t    ioINTFbits;            //? extern volatile
//Byte            ioINTCAP;              // INTERRUPT CAPTURED VALUE FOR PORT REGISTER (ADDR 0x08)
//ioINTCAPbits_t  ioINTCAPbits;          //? extern volatile
//Byte            ioGPIO;                // GENERAL PURPOSE I/O PORT REGISTER          (ADDR 0x09)
//ioGPIObits_t    ioGPIObits;            //? extern volatile
//Byte            ioOLAT;                // OUTPUT LATCH REGISTER 0                    (ADDR 0x0A)
//ioOLATbits_t    ioOLATbits;            //? extern volatile

// </editor-fold>// Variable Definitions

/*═══════════════════════════ FUNCTIONS DEFINITIONS ══════════════════════════*/
// <editor-fold defaultstate="collapsed" desc="Functions Definitions">

/*──────────────────────── Write IOExpndr Register ───────────────────────────*/
void setIOExpndr(uInt8 IOchip, uInt8 regAddr, Byte data)
{
   Byte busAddr = (IO_EXPNDR | IOchip) << 1;// Combine fixed and user chip addr & Make room for R/nW bit, shift in a 0
   
   i2c_start();                             // generate Start COndition
   i2c_byte_out(busAddr);                   // Send IOExpndr I²C bus address & R/nW=0
   i2c_byte_out(regAddr);                   // Send IOExpndr register address
   i2c_byte_out(data);                      // send data
   i2c_stop();                              // initiate Stop Condition
}

/*───────────────────────── Read IOExpndr Register ───────────────────────────*/
Byte getIOExpndr(uInt8 IOchip, uInt8 regAddr)
{
   Byte data;                               // Return data
   uInt8 busAddr = (IO_EXPNDR | IOchip) << 1; // Combine fixed and user chip addr & Make room for R/nW bit, shift in a 0

   i2c_start();                             // generate Start COndition
   i2c_byte_out(busAddr);                   // Send IOExpndr I²C address & R/nW=0
   i2c_byte_out(regAddr);                   // Send IOExpndr register address
   i2c_reStart();                           // generate 2nd Start Condition
   i2c_byte_out(busAddr | 1u);              // Send IOExpndr I²C address & R/nW=1
   data = i2c_byte_in();                    // read data
   i2c_stop();                              // send Stop Condition

   return data;                             // return data from I2C buffer
}

/*──────────────────────── Write IOExpndr Registers ──────────────────────────*/
void writeIOExpndr(uInt8 IOchip, Byte * data, uInt8 regAddr, uInt8 numberBytes)
{
   Byte busAddr = (IO_EXPNDR | IOchip) << 1;       // Combine fixed and user chip addr & Make room for R/nW bit, shift in a 0
   uInt8 n;                                      // Data buffer count

   i2c_start();                                  // generate Start COndition
   i2c_byte_out(busAddr);                        // Send IOExpndr I²C bus address & R/nW=0
   i2c_byte_out(regAddr);                        // Send IOExpndr start register address
   for (n=0; n < numberBytes; n++)               // loop through the number of bytes
      i2c_byte_out(data[n]);                     // Write bytes from data array
   i2c_stop();                                   // send Stop Condition
}

/*───────────────────────── Read IOExpndr Registers ──────────────────────────*/
void readIOExpndr(uInt8 IOchip, Byte * data, uInt8 regAddr, uInt8 numberBytes)
{
   Byte busAddr = (IO_EXPNDR | IOchip) << 1;     // Combine fixed and chip addr & make room for R/nW bit, shift in a 0
   uInt8 n;                                      // Data buffer count

   i2c_start();                                  // generate Start COndition
   i2c_byte_out(busAddr);                        // Send IOExpndr I²C address & R/nW=0
   i2c_byte_out(regAddr);                        // Send IOExpndr start register address
   i2c_reStart();                                // generate 2nd Start Condition
   i2c_byte_out(busAddr | 1u);                   // Send IOExpndr I²C address & R/nW=1
   for (n = 0; n < numberBytes - 1u; n++)        // loop through the number of bytes
   {
      data[n] = i2c_byte_in();                   // Read byte into array
      i2c_GiveAck(ACK);                          // Send ACK bit
   }
   data[n] = i2c_byte_in();                      // Read last byte into array
   i2c_stop();                                   // send Stop Condition

}

// </editor-fold>// Functions Definitions
