/*◄■■■■■■■■■■■■■■■■■■■■■■■■■■■ MCP23017_IOExpndr.h ■■■■■■■■■■■■■■■■■■■■■■■■■■►*/

/* 
 * File       : MCP23xxx_IOExpndr.h
 * Author     : JWDarsow
 * See        : MCP23xxx_IOExpndr.c for title information
 * Created on : Thursday, March 2, 2017
 */

#ifndef MCP23017_IOEXPNDR_H
#define	MCP23017_IOEXPNDR_H

/*═════════════════════════════════ INCLUDEs ═════════════════════════════════*/
#include <xc.h>
#include"JWD-NHR_Common.h"       //t Standard Defines & Generic Type Defs
#include "I2CM.h"                 //t MSSP I²C routines

/*═════════════════════════════ TYPE DECLARATIONS ════════════════════════════*/
// <editor-fold defaultstate="collapsed" desc="Type Declarations">

/*─────────────────────────── bit-field definitions ──────────────────────────*/
// <editor-fold defaultstate="collapsed" desc="bit-field definitions">
typedef union {
   /* These bits control the direction of data I/O
      1 = Pin is configured as an input.
      0 = Pin is configured as an output.
    */
   struct {
      unsigned IO0      : 1;
      unsigned IO1      : 1;
      unsigned IO2      : 1;
      unsigned IO3      : 1;
      unsigned IO4      : 1;
      unsigned IO5      : 1;
      unsigned IO6      : 1;
      unsigned IO7      : 1;
   };
   Byte byte;
} ioDIRbits_t;       // I/O DIRECTION REGISTER

typedef union {
   /* These bits control the polarity inversion of the input pins
      1 = GPIO register bit will reflect the opposite logic state of the input pin.
      0 = GPIO register bit will reflect the same logic state of the input pin.
    */
   struct {
      unsigned IP0      : 1;
      unsigned IP1      : 1;
      unsigned IP2      : 1;
      unsigned IP3      : 1;
      unsigned IP4      : 1;
      unsigned IP5      : 1;
      unsigned IP6      : 1;
      unsigned IP7      : 1;
   };
   Byte byte;
} ioIPOLbits_t;      // INPUT POLARITY REGISTER

typedef union {
   /* General purpose I/O interrupt-on-change bits
      1 = Enable GPIO input pin for interrupt-on-change event.
      0 = Disable GPIO input pin for interrupt-on-change event.
    */
   struct {
      unsigned GPINT0   : 1;
      unsigned GPINT1   : 1;
      unsigned GPINT2   : 1;
      unsigned GPINT3   : 1;
      unsigned GPINT4   : 1;
      unsigned GPINT5   : 1;
      unsigned GPINT6   : 1;
      unsigned GPINT7   : 1;
   };
   Byte byte;
} ioGPINTENbits_t;   // INTERRUPT-ON-CHANGE CONTROL REGISTER

typedef union {    // DEFAULT COMPARE REGISTER FOR INTERRUPT-ON-CHANGE
   /* These bits set the compare value for pins configured for interrupt-on-change from defaults
      If the associated pin level is the opposite from the register bit, an interrupt occurs.
    */
   struct {
      unsigned DEF0     : 1;
      unsigned DEF1     : 1;
      unsigned DEF2     : 1;
      unsigned DEF3     : 1;
      unsigned DEF4     : 1;
      unsigned DEF5     : 1;
      unsigned DEF6     : 1;
      unsigned DEF7     : 1;
   };
   Byte byte;
} ioDEFVALbits_t;    // DEFAULT COMPARE REGISTER FOR INTERRUPT-ON-CHANGE

typedef union {
   /* These bits control how the associated pin value is compared for interrupt-on-change
      1 = Controls how the associated pin value is compared for interrupt-on-change.
      0 = Pin value is compared against the previous pin value.
    */
   struct {
      unsigned IOC0     : 1;
      unsigned IOC1     : 1;
      unsigned IOC2     : 1;
      unsigned IOC3     : 1;
      unsigned IOC4     : 1;
      unsigned IOC5     : 1;
      unsigned IOC6     : 1;
      unsigned IOC7     : 1;
   };
   Byte byte;
} ioINTCONbits_t;    // INTERRUPT CONTROL REGISTER

typedef union {
   struct {
      unsigned          : 1; // Unimplemented: Read as ‘0’
      unsigned INTPOL   : 1; // This bit sets the polarity of the INT output pin
      /*
         1 = Active-high.
         0 = Active-low.
       */
      unsigned ODR      : 1; // This bit configures the INT pin as an open-drain output
      /*
         1 = Open-drain output (overrides the INTPOL bit).
         0 = Active driver output (INTPOL bit sets the polarity).
       */
      unsigned HAEN     : 1; // Hardware Address Enable bit (MCP23S17 & MCP23S08 only)
      /*
         1 = Enables the MCP23S17 address pins.
         0 = Disables the MCP23S17 address pins.
       */
      unsigned DISSLW   : 1; // Slew Rate control bit for SDA output
      /*
         1 = Slew rate disabled.
         0 = Slew rate enabled.
       */
      unsigned SEQOP    : 1; // Sequential Operation mode bit (Not used on the MCP23008)
      /*
         1 = Sequential operation disabled, address pointer does not increment.
         0 = Sequential operation enabled, address pointer increments.
       */
      unsigned MIRROR   : 1; // INT Pins Mirror bit (Not used on the MCP23008)
      /*
         1 = The INT pins are internally connected
         0 = The INT pins are not connected. INTA is associated with PortA and INTB is associated with PortB
       */
      unsigned BANK     : 1; // Controls how the registers are addressed
   };
   Byte byte;
} ioCONbits_t;       // CONFIGURATION REGISTER

typedef union {
   /* These bits control the weak pull-up resistors on each pin (when configured as an input)
      1 = Pull-up enabled.
      0 = Pull-up disabled.
    */
   struct {
      unsigned PU0      : 1;
      unsigned PU1      : 1;
      unsigned PU2      : 1;
      unsigned PU3      : 1;
      unsigned PU4      : 1;
      unsigned PU5      : 1;
      unsigned PU6      : 1;
      unsigned PU7      : 1;
   };
   Byte byte;
} ioGPPUbits_t;      // PULL-UP RESISTOR CONFIGURATION REGISTER

typedef union {
   /* These bits reflect the interrupt condition on the port. Will reflect the change only if interrupts are enabled
      1 = Pin caused interrupt.
      0 = Interrupt not pending.
    */
   struct {
      unsigned INT0     : 1;
      unsigned INT1     : 1;
      unsigned INT2     : 1;
      unsigned INT3     : 1;
      unsigned INT4     : 1;
      unsigned INT5     : 1;
      unsigned INT6     : 1;
      unsigned INT7     : 1;
   };
   Byte byte;
} ioINTFbits_t;      // INTERRUPT FLAG REGISTER

typedef union {
   /* These bits reflect the logic level on the port pins at the time of interrupt due to pin change
      1 = Logic-high.
      0 = Logic-low.
    */
   struct {
      unsigned ICP0     : 1;
      unsigned ICP1     : 1;
      unsigned ICP2     : 1;
      unsigned ICP3     : 1;
      unsigned ICP4     : 1;
      unsigned ICP5     : 1;
      unsigned ICP6     : 1;
      unsigned ICP7     : 1;
   };
   Byte byte;
} ioINTCAPbits_t;    // INTERRUPT CAPTURE REGISTER

typedef union {
   /* These bits reflect the logic level on the pins
      1 = Logic-high.
      0 = Logic-low.
    */
   struct {
      unsigned GP0      : 1;
      unsigned GP1      : 1;
      unsigned GP2      : 1;
      unsigned GP3      : 1;
      unsigned GP4      : 1;
      unsigned GP5      : 1;
      unsigned GP6      : 1;
      unsigned GP7      : 1;
   };
   Byte byte;
} ioGPIObits_t;      // PORT REGISTER

typedef union {
   /* These bits reflect the logic level on the output latch
      1 = Logic-high.
      0 = Logic-low.
    */
   struct {
      unsigned OL0      : 1;
      unsigned OL1      : 1;
      unsigned OL2      : 1;
      unsigned OL3      : 1;
      unsigned OL4      : 1;
      unsigned OL5      : 1;
      unsigned OL6      : 1;
      unsigned OL7      : 1;
   };
   Byte byte;
} ioOLATbits_t;      // OUTPUT LATCH REGISTER (OLAT)

//bit-field definitions </editor-fold>

/*────────────────────────────── XI/O data Array ─────────────────────────────*/
typedef union 
{         // MCP23017 IO chip data type
   struct {
      ioDIRbits_t       IODIR_A;    // I/O DIRECTION REGISTER A
      ioDIRbits_t       IODIR_B;    // I/O DIRECTION REGISTER B
      ioIPOLbits_t      IPOL_A;     // INPUT POLARITY REGISTER A
      ioIPOLbits_t      IPOL_B;     // INPUT POLARITY REGISTER B
      ioGPINTENbits_t   GPINTEN_A;  // INTERRUPT-ON-CHANGE CONTROL REGISTER A
      ioGPINTENbits_t   GPINTEN_B;  // INTERRUPT-ON-CHANGE CONTROL REGISTER B
      ioDEFVALbits_t    DEFVAL_A;   // DEFAULT COMPARE REGISTER FOR INTERRUPT-ON-CHANGE A
      ioDEFVALbits_t    DEFVAL_B;   // DEFAULT COMPARE REGISTER FOR INTERRUPT-ON-CHANGE B
      ioINTCONbits_t    INTCON_A;   // INTERRUPT CONTROL REGISTER A
      ioINTCONbits_t    INTCON_B;   // INTERRUPT CONTROL REGISTER B
      ioCONbits_t       IOCON_A;    // CONFIGURATION REGISTER A
      ioCONbits_t       IOCON_B;    //      "           "     B
      ioGPPUbits_t      GPPU_A;     // PULL-UP RESISTOR CONFIGURATION REGISTER A
      ioGPPUbits_t      GPPU_B;     // PULL-UP RESISTOR CONFIGURATION REGISTER B
      ioINTFbits_t      INTF_A;     // INTERRUPT FLAG REGISTER A
      ioINTFbits_t      INTF_B;     // INTERRUPT FLAG REGISTER B
      ioINTCAPbits_t    INTCAP_A;   // INTERRUPT CAPTURE REGISTER A
      ioINTCAPbits_t    INTCAP_B;   // INTERRUPT CAPTURE REGISTER B
      ioGPIObits_t      GPIO_A;     // PORT REGISTER A
      ioGPIObits_t      GPIO_B;     // PORT REGISTER B
      ioOLATbits_t      OLAT_A;     // OUTPUT LATCH REGISTER (OLAT) A
      ioOLATbits_t      OLAT_B;     // OUTPUT LATCH REGISTER (OLAT) B
   } Reg;

   Byte asArray[22];    // Setup data
} IOchip_t;

//Type Declarations </editor-fold>

/* 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.
 */

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

/*────────────────────────────────── DEFINES ─────────────────────────────────*/
// <editor-fold defaultstate="collapsed" desc="DEFINES">
#define IO_EXPNDR            0b00100000u    // IOExpander 7 bit I²C address sans chip select bits

// MCP23017 CONTROL REGISTER SUMMARY (IOCON.BANK = 0)
#define b0DIRAaddr       0x00           // I/O Direction Register
#define b0DIRBaddr       0x01           // I/O Direction Register
#define b0IPOLAaddr      0x02           // Input Polarity Port Register
#define b0IPOLBaddr      0x03           // Input Polarity Port Register
#define b0GPINTENAaddr   0x04           // Interrupt-On-Change Pins
#define b0GPINTENBaddr   0x05           // Interrupt-On-Change Pins
#define b0DEFVALAaddr    0x06           // Default Value Register
#define b0DEFVALBaddr    0x07           // Default Value Register
#define b0INTCONAaddr    0x08           // Interrupt-On-Change Control Register
#define b0INTCONBaddr    0x09           // Interrupt-On-Change Control Register
#define b0CONAaddr       0x0A           // I/O Expander Configuration Register
#define b0CONBaddr       0x0B           // I/O Expander Configuration Register
#define b0GPPUAaddr      0x0C           // GPIO Pull-Up Resistor Register
#define b0GPPUBaddr      0x0D           // GPIO Pull-Up Resistor Register
#define b0INTFAaddr      0x0E           // Interrupt Flag Register
#define b0INTFBaddr      0x0F           // Interrupt Flag Register
#define b0INTCAPAaddr    0x10           // Interrupt Captured Value For Port Register
#define b0INTCAPBaddr    0x11           // Interrupt Captured Value For Port Register
#define b0GPIOAaddr      0x12           // General Purpose I/O Port Register
#define b0GPIOBaddr      0x13           // General Purpose I/O Port Register
#define b0OLATAaddr      0x14           // Output Latch Register 0
#define b0OLATBaddr      0x15           // Output Latch Register 0

// MCP23017 CONTROL REGISTER SUMMARY (IOCON.Bank = 1)
#define b1DIRAaddr       0x00           // I/O Direction Register
#define b1DIRBaddr       0x10           // I/O Direction Register
#define b1IPOLAaddr      0x01           // Input Polarity Port Register
#define b1IPOLBaddr      0x11           // Input Polarity Port Register
#define b1GPINTENAaddr   0x02           // Interrupt-On-Change Pins
#define b1GPINTENBaddr   0x12           // Interrupt-On-Change Pins
#define b1DEFVALAaddr    0x03           // Default Value Register
#define b1DEFVALBaddr    0x13           // Default Value Register
#define b1INTCONAaddr    0x04           // Interrupt-On-Change Control Register
#define b1INTCONBaddr    0x14           // Interrupt-On-Change Control Register
#define b1CONAaddr       0x05           // I/O Expander Configuration Register
#define b1CONBaddr       0x15           // I/O Expander Configuration Register
#define b1GPPUAaddr      0x06           // GPIO Pull-Up Resistor Register
#define b1GPPUBaddr      0x16           // GPIO Pull-Up Resistor Register
#define b1INTFAaddr      0x07           // Interrupt Flag Register
#define b1INTFBaddr      0x17           // Interrupt Flag Register
#define b1INTCAPAaddr    0x08           // Interrupt Captured Value For Port Register
#define b1INTCAPBaddr    0x18           // Interrupt Captured Value For Port Register
#define b1GPIOAaddr      0x09           // General Purpose I/O Port Register
#define b1GPIOBaddr      0x19           // General Purpose I/O Port Register
#define b1OLATAaddr      0x0A           // Output Latch Register 0
#define b1OLATBaddr      0x1A           // Output Latch Register 0

//DEFINES </editor-fold>

/*═══════════════════════════ FUNCTION DECLARATIONS ══════════════════════════*/
// <editor-fold defaultstate="collapsed" desc="Function Declarations">
/*──────────────────────── Write IOExpndr Register ───────────────────────────*/
void setIOExpndr(uInt8 IOchip, uInt8 regAddr, Byte data);

/*──────────────────────── Read IOExpndr Register ────────────────────────────*/
Byte getIOExpndr(uInt8 IOchip, uInt8 regAddr);

/*──────────────────────── Write IOExpndr Registers ──────────────────────────*/
void writeIOExpndr(uInt8 IOchip, Byte * data, uInt8 regAddr, uInt8 numberBytes);

/*───────────────────────── Read IOExpndr Registers ──────────────────────────*/
void readIOExpndr(uInt8 IOchip, Byte * data, uInt8 regAddr, uInt8 numberBytes);

//Function Declarations </editor-fold>

/*══════════════════════════════════ MACROs ══════════════════════════════════*/

#endif	/* MCP23017_IOEXPNDR_H */

