This library provides simplified I2C access to a Microchip MCP23x17 GPIO expender device, including a general interface for any GPIO expender

Dependents:   MCP23017App

Files at this revision

API Documentation at this revision

Comitter:
Yann
Date:
Fri Jan 09 14:37:42 2015 +0000
Child:
1:ec9e770173d5
Commit message:
Add support of interruptions

Changed in this revision

AbstractGpioExpender.h Show annotated file Show diff for this revision Revisions of this file
MCP23017_I2C.cpp Show annotated file Show diff for this revision Revisions of this file
MCP23017_I2C.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AbstractGpioExpender.h	Fri Jan 09 14:37:42 2015 +0000
@@ -0,0 +1,82 @@
+#pragma once
+
+#include <list>
+
+#include <mbed.h>
+
+#define GPA0        0
+#define GPA1        1
+#define GPA2        2
+#define GPA3        3
+#define GPA4        4
+#define GPA5        5
+#define GPA6        6
+#define GPA7        7
+#define GPB0        8
+#define GPB1        9
+#define GPB2        10
+#define GPB3        11
+#define GPB4        12
+#define GPB5        13
+#define GPB6        14
+#define GPB7        15
+#define GPIO_MAX    GPB7
+#define GPIO_MED    GPB0
+#define GPIO_SIZE   8
+
+class AbstractGpioExpender {
+public:
+    /** Interrupt modes
+     */
+    enum InterruptModes {
+        OnChange = 0x00,
+        OnRising = 0x01,
+        OnFalling = 0x03
+    };
+    
+    /** Reset the device
+     */
+    virtual void reset() = 0;
+
+    /** Setup interrupt mechanism for all GPIO ports
+     * @param p_mirroring Set to 0x00 to disable INTA/B mirroring, 0x01 otherwise. Default: 0x00
+     * @param p_openDrain Set to 0x00 for active driver output, 0x01 for opn drain output. Default: 0x00
+     * @param p_polarity Set to 0x00 for interrupt active low, 0x01 otherwise. Default: 0x00
+     */
+    virtual void setupInterrupts(const unsigned char p_mirroring = 0x00, const unsigned char p_openDrain = 0x00, const unsigned char p_polarity = 0x00) = 0;
+    
+    /** Setup interrupt for a specific IO port
+     * @param p_gpioId The IO port identifier
+     * @param p_mode  The interrupt mode
+     * @return 0 on success, negative value otherwise
+     */
+    virtual int setupInterruptPin(const unsigned char p_gpioId, const InterruptModes p_mode = OnRising) = 0;
+    
+    /** Get interrupt information and clear it
+     * @param p_gpioId The IO port identifier where the interrupt occured
+     * @param p_value  The logic value on the pin port where the interrupt occured
+     * @return 0 on success, negative value otherwise
+     */
+    virtual int getLastInterruptPin(unsigned char * p_gpioId, unsigned char * p_value) = 0;
+    
+    /** Attach a function to call when a interrupt occurs on the GPIOA input ports
+     */
+    virtual void setIntrACallback(void (* p_fptr)(void)) = 0;
+    
+    /** Attach a function to call when a rising edge occurs on the input
+     */
+    virtual void setIntrBCallback(void (* p_fptr)(void)) = 0;
+    
+    virtual int read(const unsigned char p_gpioId, unsigned char * p_value) = 0;
+    virtual int write(const unsigned char p_gpioId, const unsigned char p_value) = 0;
+    
+    virtual unsigned char createBus(const std::list<unsigned char> p_lines, const PinMode p_mode = PullNone) = 0;
+    virtual void deleteBus(const unsigned char p_busId) = 0;
+    virtual int busRead(const unsigned char p_busId) = 0;
+    virtual int busWrite(const unsigned char p_busId, const unsigned char p_value) = 0;
+    
+protected:
+    virtual bool writeRegister(const unsigned char p_registerId, const unsigned char p_value) = 0;
+    virtual bool readRegister(const unsigned char p_registerId, unsigned char * p_value) = 0;
+
+}; // End of class AbstractGpioExpender 
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MCP23017_I2C.cpp	Fri Jan 09 14:37:42 2015 +0000
@@ -0,0 +1,551 @@
+/* mbed simplified access to Microchip MCP28x17 GPIO expender devices (I2C)
+ * Copyright (c) 2010-2012 ygarcia, MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
+ * and associated documentation files (the "Software"), to deal in the Software without restriction, 
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
+ * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or 
+ * substantial pinions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 
+ * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <iostream>
+#include <sstream>
+
+#include "MCP23017_I2C.h"
+
+#define IODIRA      0x00
+#define IODIRB      0x01
+#define IPOLA       0x02
+#define IPOLB       0x03
+#define GPINTENA    0x04
+#define GPINTENB    0x05
+#define DEFVALA     0x06
+#define DEFVALB     0x07
+#define INTCONA     0x08
+#define INTCONB     0x09
+#define IOCONA      0x0a
+#define IOCONB      0x0b
+#define GPPUA       0x0c
+#define GPPUB       0x0d
+#define INTFA       0x0e
+#define INTFB       0x0f
+#define INTCAPA     0x10
+#define INTCAPB     0x11
+#define GPIOA       0x12
+#define GPIOB       0x13
+#define OLATA       0x14
+#define OLATB       0x15
+
+namespace MCP23017_I2C {
+
+    unsigned char CMCP23017_I2C::I2CModuleRefCounter = 0;
+
+    CMCP23017_I2C::CMCP23017_I2C(const PinName p_sda, const PinName p_scl, const unsigned char p_address, const PinName p_intA, const PinName p_intB, const PinName p_reset, const bool p_internalPullUp, const unsigned int p_frequency) : _gpioAFlags(0x00), _gpioBFlags(0x00), _buses(), _busesIndex(0x00), _internalId("") {
+        DEBUG_ENTER("CMCP23017_I2C")
+
+        if (CMCP23017_I2C::I2CModuleRefCounter != 0) {
+            error("CMCP23017_I2C: Wrong params");
+        }
+#ifdef __DEBUG
+        std::ostringstream out(std::ostringstream::out);
+        out << "CMCP23017_I2C #" << CMCP23017_I2C::I2CModuleRefCounter;
+        _internalId.assign(out.str());
+        DEBUG("CMCP23017_I2C: _internalId='%s'", _internalId.c_str())
+#endif // __DEBUG
+        _i2cInstance = new I2C(p_sda, p_scl);
+        CMCP23017_I2C::I2CModuleRefCounter += 1;
+        DEBUG_ENTER("CMCP23017_I2C: refCounter=%d", CMCP23017_I2C::I2CModuleRefCounter)
+
+        _slaveAddress = (p_address << 1) | 0x40; // Slave address format is: 0 0 1 0 A3 A2 A1 R/W
+        DEBUG("CMCP23017_I2C: I2C slave adress: 0x%02x", _slaveAddress)
+        _i2cInstance->frequency(p_frequency); // Set the frequency of the I2C interface
+        
+        if (p_intA != NC) {
+            DEBUG("CMCP23017_I2C: INTA managed");
+            _intA = new InterruptIn(p_intA);
+            if (p_internalPullUp) _intA->mode(PullDown);
+            _intA->enable_irq(); // Enable interrupt
+        } else {
+            DEBUG("CMCP23017_I2C: INTA not managed");
+            _intA = NULL; // Not used
+        }
+        if (p_intB != NC) {
+            DEBUG("CMCP23017_I2C: INTB managed");
+            _intB = new InterruptIn(p_intB);
+            if (p_internalPullUp) _intB->mode(PullDown);
+            _intB->enable_irq(); // Enable interrupt
+        } else {
+            DEBUG("CMCP23017_I2C: INTB not managed");
+            _intB = NULL; // Not used
+        }
+        if (p_reset != NC) {
+            DEBUG("CMCP23017_I2C: RESET managed");
+            _reset = new DigitalOut(p_reset);
+            _reset->write(1); // Disable reset
+        } else {
+            DEBUG("CMCP23017_I2C: RESET not managed");
+            _reset = NULL; // Not used
+        }
+    
+        DEBUG_LEAVE("CMCP23017_I2C")
+    }
+
+    CMCP23017_I2C::~CMCP23017_I2C() {
+        DEBUG_ENTER("~CMCP23017_I2C")
+    
+        // Release I2C instance
+        DEBUG_ENTER("~CMCP23017_I2C: refCounter=%d", CMCP23017_I2C::I2CModuleRefCounter)
+        CMCP23017_I2C::I2CModuleRefCounter -= 1;
+        if (CMCP23017_I2C::I2CModuleRefCounter == 0) {
+            delete _i2cInstance;
+            _i2cInstance = NULL;
+        }
+        // Release _wp if required
+        if (_intA != NULL) {
+            delete _intA;
+        }
+        if (_intB != NULL) {
+            delete _intB;
+        }
+        if (_reset != NULL) {
+            delete _reset;
+        }
+    
+        DEBUG_LEAVE("~CMCP23017_I2C")
+    }
+    
+    bool CMCP23017_I2C::Initialize(const unsigned char p_gpioAFlags, const unsigned char p_gpioBFlags) {
+        
+        // Configure default behavior
+        _gpioAFlags = p_gpioAFlags;
+        _gpioBFlags = p_gpioBFlags;
+        configure(_gpioAFlags, _gpioBFlags);
+        
+        return true;
+    }
+    
+    void CMCP23017_I2C::configure(const unsigned char p_gpioAFlags, const unsigned char p_gpioBFlags) { // TODO Optimization with sequential access
+//        DEBUG_ENTER("CMCP23017_I2C::configure: 0x%02x 0x%02x", p_gpioAFlags, p_gpioBFlags)
+        
+        // Setup IOCON - See REGISTER 1-6: IOCON – I/O EXPANDER CONFIGURATION REGISTER
+        writeRegister(IOCONA, 0x10); // Sequential operation disabled
+        writeRegister(IOCONB, 0x10); // Sequential operation disabled
+        // Setup IODIR - See REGISTER 1-1: IODIR – I/O DIRECTION REGISTER (ADDR 0x00)
+        writeRegister(IODIRA, p_gpioAFlags);
+        writeRegister(IODIRB, p_gpioBFlags);
+        // Setup IPOL - See REGISTER 1-2: IPOL – INPUT POLARITY PORT REGISTER
+        writeRegister(IPOLA, 0x00); // GPIO register bit will reflect the same logic state of the input pin
+        writeRegister(IPOLB, 0x00);
+        // Setup GPPU - See REGISTER 1-7: GPPU – GPIO PULL-UP RESISTOR REGISTER
+        writeRegister(GPPUA, 0x00);
+        writeRegister(GPPUB, 0x00);
+        
+        // Setup interrupt       
+        if (_intA != NULL) {
+            DEBUG("CMCP23017_I2C::configure: Setup INTA")
+            // Setup GPINTEN - See GPINTEN – INTERRUPT-ON-CHANGE PINS
+            writeRegister(GPINTENA, 0x00); // Disable GPIO interrupt-on-change events
+            // Setup DEFVAL - See REGISTER 1-4: DEFVAL – DEFAULT VALUE REGISTER
+            writeRegister(DEFVALA, 0x00); // Pin level change from 0 to 1 raises an interrupt
+            // Setup INTCON - REGISTER 1-5: INTCON – INTERRUPT-ON-CHANGE CONTROL REGISTER
+            writeRegister(INTCONA, 0xff); // Pin level change from 0 to 1 raises an interrupt
+        }
+        if (_intB != NULL) {
+            DEBUG("CMCP23017_I2C::configure: Setup INTB")
+            // Setup GPINTEN - See GPINTEN – INTERRUPT-ON-CHANGE PINS
+            writeRegister(GPINTENB, 0x00); // Disable GPIO interrupt-on-change events
+            // Setup DEFVAL - See REGISTER 1-4: DEFVAL – DEFAULT VALUE REGISTER
+            writeRegister(DEFVALB, 0x00); // Pin level change from 0 to 1 raises an interrupt
+            // Setup INTCON - REGISTER 1-5: INTCON – INTERRUPT-ON-CHANGE CONTROL REGISTER
+            writeRegister(INTCONB, 0xff); // Pin level change from 0 to 1 raises an interrupt
+        }
+        
+//        DumpRegisters();
+        
+//        DEBUG_LEAVE("CMCP23017_I2C::configure")
+    }
+    
+    void CMCP23017_I2C::reset() {
+//        DEBUG_ENTER("CMCP23017_I2C::reset")
+        if (_reset != NULL) {
+            _reset->write(0);
+            wait_us(1);
+            _reset->write(1);
+            wait_us(1);
+            if ((_gpioAFlags != 0x00) || (_gpioBFlags != 0x00)) { // Apply configuration
+                configure(_gpioAFlags, _gpioBFlags);
+            } // else, POR reset values, see DS21952B-page 10 Clause 1.6 Configuration and Control Registers
+        }
+//        DEBUG_LEAVE("CMCP23017_I2C::reset")
+    }
+    
+    void CMCP23017_I2C::setIntrACallback(void (* p_fptr)(void)) {
+        if (_intA != NULL) {
+            unsigned char vregister;
+            if (!readRegister(IOCONA, &vregister)) {
+                return;    
+            }
+            if (isBitSet(vregister, 1)) {
+                _intA->rise(p_fptr);
+            } else {
+                _intA->fall(p_fptr);
+            }
+        }
+    }
+    
+    void CMCP23017_I2C::setIntrBCallback(void (* p_fptr)(void)) {
+        if (_intB != NULL) {
+            unsigned char vregister;
+            if (!readRegister(IOCONB, &vregister)) {
+                return;    
+            }
+            if (isBitSet(vregister, 1)) {
+                _intB->rise(p_fptr);
+            } else {
+                _intB->fall(p_fptr);
+            }
+        }
+    }
+        
+    template<typename T> 
+    void CMCP23017_I2C::setIntrACallback (const T * p_tptr, void(T::* p_mptr)(void)) {
+        if (_intA != NULL) {
+            unsigned char vregister;
+            if (!readRegister(IOCONA, &vregister)) {
+                return;    
+            }
+            if (isBitSet(vregister, 1)) {
+                _intA->rise(p_tptr, p_mptr);
+            } else {
+                _intA->fall(p_tptr, p_mptr);
+            }
+        }
+    }
+    
+    template<typename T> 
+    void CMCP23017_I2C::setIntrBCallback (const T * p_tptr, void(T::* p_mptr)(void)) {
+            unsigned char vregister;
+            if (!readRegister(IOCONB, &vregister)) {
+                return;    
+            }
+            if (isBitSet(vregister, 1)) {
+                _intB->rise(p_tptr, p_mptr);
+            } else {
+                _intB->fall(p_tptr, p_mptr);
+            }
+    }
+    
+    void CMCP23017_I2C::setupInterrupts(const unsigned char p_mirroring, const unsigned char p_openDrain, const unsigned char p_polarity) {
+        DEBUG_ENTER("CMCP23017_I2C::setupInterrupts: %02x, %02x, %02x", p_mirroring, p_openDrain, p_polarity)
+        
+        // Setup IOCONA
+        unsigned char vregister;
+        if (!readRegister(INTCONA, &vregister)) {
+            return;    
+        }
+        // Setup INTCON - REGISTER 1-5: INTCON – INTERRUPT-ON-CHANGE CONTROL REGISTER
+        vregister = setBit(vregister, 6, p_mirroring);
+        vregister = setBit(vregister, 2, p_openDrain);
+        vregister = setBit(vregister, 1, p_polarity);        
+        writeRegister(INTCONA, vregister);
+        
+        // Setup IOCONB
+        if (!readRegister(INTCONB, &vregister)) {
+            return;    
+        }
+        // Setup INTCON - REGISTER 1-5: INTCON – INTERRUPT-ON-CHANGE CONTROL REGISTER
+        vregister = setBit(vregister, 6, p_mirroring);
+        vregister = setBit(vregister, 2, p_openDrain);
+        vregister = setBit(vregister, 1, p_polarity);        
+        writeRegister(INTCONB, vregister);
+        
+    }
+    
+    int CMCP23017_I2C::setupInterruptPin(const unsigned char p_gpioId, const InterruptModes p_mode) {
+        DEBUG_ENTER("CMCP23017_I2C::setupInterruptPin: %02x, %02x", p_gpioId, p_mode)
+        
+        // Retrive the register address
+        unsigned char gpioIntconId, gpioDefvalId, gpioGpintenId;
+        if (!registerIdFromGpioId(p_gpioId, &gpioIntconId)) {
+            DEBUG_LEAVE("CMCP23017_I2C::setupInterruptPin: -1")
+            return -1;
+        }
+        if (gpioIntconId == GPIOA) {
+            gpioIntconId = INTCONA;
+            gpioDefvalId = DEFVALA; 
+            gpioGpintenId = GPINTENA;
+        } else {
+            gpioIntconId = INTCONB;
+            gpioDefvalId = DEFVALB; 
+            gpioGpintenId = GPINTENB;
+        }
+        DEBUG("CMCP23017_I2C::setupInterruptPin: gpioIntconId=%02x gpioDefvalId=%02x gpioGpintenId=%02x", gpioIntconId, gpioDefvalId, gpioGpintenId)
+        
+        // Retrive the GPIO pin number
+        unsigned char gpioBit = gpioBitFromGpioId(p_gpioId);
+        DEBUG("CMCP23017_I2C::setupInterruptPin: gpioBit=%02x", gpioBit)
+        
+        // Read it
+        unsigned char gpioIntconValue, gpioDefvalValue, gpioGpintenValue;
+        if (!readRegister(gpioIntconId, &gpioIntconValue)) {
+            DEBUG_LEAVE("CMCP23017_I2C::setupInterruptPin: -2")
+            return -2;
+        }
+        if (!readRegister(gpioDefvalId, &gpioDefvalValue)) {
+            DEBUG_LEAVE("CMCP23017_I2C::setupInterruptPin: -2")
+            return -2;
+        }
+        if (!readRegister(gpioGpintenId, &gpioGpintenValue)) {
+            DEBUG_LEAVE("CMCP23017_I2C::setupInterruptPin: -2")
+            return -2;
+        }
+        DEBUG("CMCP23017_I2C::setupInterruptPin: gpioIntconValue=%02x gpioDefvalValue=%02x gpioGpintenValue=%02x", gpioIntconValue, gpioDefvalValue, gpioGpintenValue)
+        
+        //         
+        switch (static_cast<unsigned char>(p_mode)) {
+            case static_cast<unsigned char>(OnChange):
+                gpioIntconValue = setBit(gpioIntconValue, gpioBit, 0x00);
+                gpioDefvalValue = setBit(gpioDefvalValue, gpioBit, 0x00);
+                break;
+            case static_cast<unsigned char>(OnRising):
+                gpioIntconValue = setBit(gpioIntconValue, gpioBit, 0x01);
+                gpioDefvalValue = setBit(gpioDefvalValue, gpioBit, 0x00);
+                break;
+            case static_cast<unsigned char>(OnFalling):
+                gpioIntconValue = setBit(gpioIntconValue, gpioBit, 0x01);
+                gpioDefvalValue = setBit(gpioDefvalValue, gpioBit, 0x01);
+                break;
+        } // End of 'switch' statement
+        
+        // Enable interrupt
+        gpioGpintenValue = setBit(gpioGpintenValue, gpioBit, 0x01); 
+        
+        // Write register
+        DEBUG("CMCP23017_I2C::setupInterruptPin: gpioIntconValue=%02x gpioDefvalValue=%02x gpioGpintenValue=%02x", gpioIntconValue, gpioDefvalValue, gpioGpintenValue)
+        writeRegister(gpioDefvalId, gpioDefvalValue);
+        writeRegister(gpioIntconId, gpioIntconValue);
+        writeRegister(gpioGpintenId, gpioGpintenValue);
+        
+        DEBUG_LEAVE("CMCP23017_I2C::setupInterruptPin: 0")
+        return 0;
+    }
+    
+    int CMCP23017_I2C::getLastInterruptPin(unsigned char * p_gpioId, unsigned char * p_value) {
+        DEBUG_ENTER("CMCP23017_I2C::getLastInterruptPin")
+        
+        // Read first INTFA if required
+        unsigned char vregister;
+        if (_gpioAFlags != 0x00) {
+            if (!readRegister(INTFA, &vregister)) {
+                DEBUG_LEAVE("CMCP23017_I2C::setupInterruptPin: -1")
+                return -1;    
+            }
+            vregister &= _gpioAFlags; // Cannot have interrupt on output port
+            for (unsigned char bit = 0; bit < GPIO_SIZE; bit++) {
+                if (isBitSet(vregister, bit)) {
+                    *p_gpioId = bit;
+                    readRegister(INTCAPA, p_value);
+                    *p_value = (*p_value >> bit) & 0x01;
+                    
+                    DEBUG_LEAVE("CMCP23017_I2C::getLastInterruptPin (A): %02x %02x", *p_gpioId, *p_value)
+                    return 0;
+                }
+            } // End of 'for' statement
+        }
+        
+        // If not interrupt on GPIOA, try with GPIOB
+        if (_gpioBFlags != 0x00) {
+            if (!readRegister(INTFB, &vregister)) {
+                DEBUG_LEAVE("CMCP23017_I2C::setupInterruptPin: -2")
+                return -2;    
+            }
+            vregister &= _gpioBFlags; // Cannot have interrupt on output port
+            for (unsigned char bit = 0; bit < GPIO_SIZE; bit++) {
+                if (isBitSet(vregister, bit)) {
+                    *p_gpioId = bit + GPIO_SIZE;
+                    readRegister(INTCAPB, p_value);
+                    *p_value = (*p_value >> bit) & 0x01;
+                    
+                    DEBUG_LEAVE("CMCP23017_I2C::getLastInterruptPin (B): %02x %02x", *p_gpioId, *p_value)
+                    return 0;
+                }
+            } // End of 'for' statement
+        }
+        
+        DEBUG_LEAVE("CMCP23017_I2C::getLastInterruptPin: 0")
+        return 0;
+    }
+    
+    int CMCP23017_I2C::read(const unsigned char p_gpioId, unsigned char * p_value) {
+        DEBUG_ENTER("CMCP23017_I2C::read: 0x%02x", p_gpioId)
+        
+        // Retrive the register address
+        unsigned char gpioRegisterId;
+        if (!registerIdFromGpioId(p_gpioId, &gpioRegisterId)) {
+            DEBUG_LEAVE("CMCP23017_I2C::read: -1")
+            return -1;
+        }
+        DEBUG("CMCP23017_I2C::read: gpioRegisterId=%02x", gpioRegisterId)
+        unsigned char gpioBit = gpioBitFromGpioId(p_gpioId);
+        DEBUG("CMCP23017_I2C::read: gpioBit=%02x", gpioBit)
+        
+        // Read it
+        unsigned char gpioRegisterValue;
+        if (!readRegister(gpioRegisterId, &gpioRegisterValue)) {
+            DEBUG_LEAVE("CMCP23017_I2C::read: -2")
+            return -2;
+        }
+        DEBUG("CMCP23017_I2C::read: gpioRegisterValue=%02x", gpioRegisterValue)
+        
+        DEBUG_LEAVE("CMCP23017_I2C::read: 0")
+        return 0;
+    }
+    
+    bool CMCP23017_I2C::registerIdFromGpioId(const unsigned char p_gpioId, unsigned char * p_gpioRegisterId) {
+        DEBUG_ENTER("CMCP23017_I2C::registerIdFromGpioId: 0x%02x", p_gpioId)
+        
+        // Sanity check
+        if (p_gpioId > GPIO_MAX) {
+            DEBUG_ENTER("CMCP23017_I2C::registerIdFromGpioId: false")
+            return false;
+        }
+        
+        *p_gpioRegisterId = (p_gpioId < GPIO_MED) ? GPIOA : GPIOB;
+        
+        DEBUG_ENTER("CMCP23017_I2C::registerIdFromGpioId: true")
+        return true;
+    }
+    
+    int CMCP23017_I2C::write(const unsigned char p_gpioId, const unsigned char p_value) {
+        DEBUG_ENTER("CMCP23017_I2C::write: 0x%02x 0x%02x", p_gpioId, p_value)
+        
+        // Retrive the register address
+        unsigned char gpioRegisterId;
+        if (!registerIdFromGpioId(p_gpioId, &gpioRegisterId)) {
+            DEBUG_LEAVE("CMCP23017_I2C::write: -1")
+            return -1;
+        }
+        DEBUG("CMCP23017_I2C::write: gpioRegisterId=%02x", gpioRegisterId)
+        
+        // Retrive the GPIO pin number
+        unsigned char gpioBit = gpioBitFromGpioId(p_gpioId);
+        DEBUG("CMCP23017_I2C::write: gpioBit=%02x", gpioBit)
+        
+        // Read it
+        unsigned char gpioRegisterValue;
+        if (!readRegister(gpioRegisterId, &gpioRegisterValue)) {
+            DEBUG_LEAVE("CMCP23017_I2C::write: -2")
+            return -2;
+        }
+        DEBUG("CMCP23017_I2C::write: gpioRegisterValue=%02x", gpioRegisterValue)
+        
+        // Update GPIO bit
+        if (!isBitEqual(gpioRegisterValue, gpioBit, p_value)) {
+            // Write it if required
+            gpioRegisterValue = setBit(gpioRegisterValue, gpioBit, p_value);
+            DEBUG("CMCP23017_I2C::write: New gpioRegisterValue=%02x", gpioRegisterValue)
+            if (!writeRegister(gpioRegisterId, gpioRegisterValue)) {
+                DEBUG_LEAVE("CMCP23017_I2C::write: -3")
+                return -3;
+            }
+        }
+        
+        DEBUG_LEAVE("CMCP23017_I2C::write: 0")
+        return 0;
+    }
+    
+    unsigned char CMCP23017_I2C::createBus(const std::list<unsigned char> p_lines, const PinMode p_mode) {
+        unsigned char busId = _busesIndex++;
+        
+        _buses.insert(_buses.end(), std::pair<unsigned char, std::list<unsigned char> >(busId, p_lines));
+        
+        return busId;
+    }
+
+    void CMCP23017_I2C::deleteBus(const unsigned char p_busId) {
+        // Sanity check
+        std::map<unsigned char, std::list<unsigned char> >::iterator result = _buses.find(p_busId);
+        if (result == _buses.end()) { // Invalid bus identifier
+            return;
+        }
+        
+        _buses.erase(p_busId);
+    }
+
+    int CMCP23017_I2C::busRead(const unsigned char p_busId) {
+        return -1;
+    }
+
+    int CMCP23017_I2C::busWrite(const unsigned char p_busId, const unsigned char p_value) {
+        return -1;
+    }
+
+    bool CMCP23017_I2C::writeRegister(const unsigned char p_registerId, const unsigned char p_value) {
+//        DEBUG_ENTER("CMCP23017_I2C::writeRegister: Memory address: 0x%02x - 0x%02x", p_registerId, p_value)
+    
+        // 1.Prepare buffer
+        char i2cBuffer[2]; // Register address + one byte of data
+        // 1.1. Memory address
+        i2cBuffer[0] = p_registerId;
+        // 1.2. Datas
+        i2cBuffer[1] = p_value;
+//        DEBUG("CMCP23017_I2C::writeRegister: Data=0x%02x 0x%02x", i2cBuffer[0], i2cBuffer[1])
+    
+        // 2. Send I2C start + I2C address + Memory Address + Datas + I2C stop
+        int result = _i2cInstance->write(_slaveAddress, i2cBuffer, 2);
+//        wait(0.02);
+    
+//        DEBUG_LEAVE("CMCP23017_I2C::writeRegister %x", (bool)(result == 0))
+        return (bool)(result == 0);
+    }
+    
+    bool CMCP23017_I2C::readRegister(const unsigned char p_registerId, unsigned char * p_value) {
+//        DEBUG_ENTER("CMCP23017_I2C::readRegister: Memory address: 0x%02x", p_registerId)
+    
+        // 1.Prepare buffer
+        char i2cBuffer[1]; // Register address + one byte of data
+        // 1.1. Memory address
+        i2cBuffer[0] = p_registerId;
+//        DEBUG("CMCP23017_I2C::readRegister: Data=0x%02x", i2cBuffer[0])
+    
+        // 2. Send I2C start + memory address
+        if (_i2cInstance->write(_slaveAddress, i2cBuffer, 1, true) == 0) {
+//            wait(0.02);
+//            DEBUG("CMCP23017_I2C::readRegister: Write memory done")
+            // 2. Read data + I2C stop
+            int result = _i2cInstance->read(_slaveAddress, (char *)p_value, 1);
+//            wait(0.02);
+    
+//            DEBUG_LEAVE("C24LCXX_I2C::readRegister (byte): %x", (bool)(result == 0))
+            return (bool)(result == 0);
+        }
+            
+//        DEBUG_LEAVE("CMCP23017_I2C::readRegister false")
+        return false;
+    }
+    
+    void CMCP23017_I2C::DumpRegisters() {
+        
+        unsigned char value;
+        for (unsigned int registerId = 0; registerId < 0x16; registerId++) {
+            readRegister(registerId, &value);
+            DEBUG("CMCP23017_I2C::DumpRegisters: register[%d] = 0x%02x", registerId, value)
+        }
+    }
+    
+    void CMCP23017_I2C::DumpRegister(unsigned int p_registerId) {
+        
+        unsigned char value;
+        readRegister(p_registerId, &value);
+        DEBUG("CMCP23017_I2C::DumpRegister: register[%d] = 0x%02x", p_registerId, value)
+    }
+    
+} // End of namespace MCP23017_I2C
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MCP23017_I2C.h	Fri Jan 09 14:37:42 2015 +0000
@@ -0,0 +1,197 @@
+/* mbed simplified access to Microchip MCP28x17 GPIO expender devices (I2C)
+ * Copyright (c) 2014-2015 ygarcia, MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
+ * and associated documentation files (the "Software"), to deal in the Software without restriction, 
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
+ * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or 
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 
+ * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#pragma once
+
+#include <map>
+
+#include "Debug.h"
+
+#include "AbstractGpioExpender.h"
+
+namespace MCP23017_I2C {
+    /** This class provides simplified I2C access to a Microchip 2MCP28x17 GPIO expender. V0.0.0.1
+     *
+     * Note that if the LPC1768 is powered in 3.3V and Microchip MCP28x17 GPIO expender device could be powered at 3.3V or 5V.
+     * In this case, you shall use a bi-directional level shifter for I2C-bus. Please refer to AN97055 (http://ics.nxp.com/support/documents/interface/pdf/an97055.pdf)
+     * Microchip MCP28x17 GPIO expender device reference: DS21952B
+     *
+     * Note that for I2C details, please visit http://www.datelec.fr/fiches/I2C.htm
+     *
+     * Note that this header file include following headers:
+     * - <string>
+     * - <vector>
+     * - <mbed.h>
+     *
+     * @author Yann Garcia (Don't hesitate to contact me: garcia.yann@gmail.com)
+     */
+    class CMCP23017_I2C : public AbstractGpioExpender {
+        /** Reference counter used to guarentee unicity of the instance of I2C class
+         */
+        static unsigned char I2CModuleRefCounter;
+        
+        /** Device address input: A0, A1, A2 (Pins <1,3>). See DS21203K/DS21189D - Figure 5-1: Control Byte Format for address format details
+         */
+        unsigned char _slaveAddress;
+        /** Interrupt on GPIOA
+         */
+        InterruptIn *_intA;
+        /** Interrupt on GPIOB
+         */
+        InterruptIn *_intB;
+        /** Device reset pin
+         */
+        DigitalOut *_reset;
+        /** PortA config
+         */
+        unsigned char _gpioAFlags;
+        /** PortB config
+         */
+        unsigned char _gpioBFlags;
+        
+        std::map<unsigned char, std::list<unsigned char> > _buses;
+        
+        unsigned char _busesIndex;
+        
+        /** An unique instance of I2C class
+         */
+        I2C *_i2cInstance;
+        
+    public:
+        /** Constructor with Write Protect command pin wired. Use it to manage the first I2C module on 3.3V or 5V network
+         *
+         * @param p_sda: MBed pin for SDA
+         * @param p_scl: MBed pin for SCL
+         * @param p_address: Device address input: A0, A1, A2 (Pins <1,3>)
+         * @param p_intA: MBed pin to manage interrupt on GPIOA, default value is NC, not connected
+         * @param p_intB: MBed pin to manage device reset. If NC, WP is not managed, default value is NC, not connected
+         * @param p_reset: MBed pin to manage Write Protect input. If NC, WP is not managed, default value is NC, not connected
+         * @param p_internalPullUp: Set to true to use internal pull-up resistor, default value is true
+         * @param p_frequency: Frequency of the I2C interface (SCL), default value is 400KHz
+         * Example:
+         * - If A1 and A2 pins are tired to Vdd and A0 is tired to Vss, address shall '00000110'B
+         * - If A0 and A1 pins are tired to Vss and A2 is tired to Vdd, address shall '00000100'B
+         */
+        CMCP23017_I2C(const PinName p_sda, const PinName p_scl, const unsigned char p_address, const PinName p_intA = NC, const PinName p_intB = NC, const PinName p_reset = NC, const bool p_internalPullUp = true, const unsigned int p_frequency = 400000);
+    
+        /** Destructor
+         */
+        virtual ~CMCP23017_I2C();
+        
+        /** Initialize the module, configuring the module and starting the clock
+         * @param p_gpioAFlags GPIO A port configuration: bit set for input mode, 0 for output mode. Default: ports as outputs
+         * @param p_gpioBFlags GPIO B port configuration: bit set for input mode, 0 for output mode. Default: ports as outputs
+         *
+         * @return true on success, false otherwise
+         */
+        bool Initialize(const unsigned char p_gpioAFlags = 0x00, const unsigned char p_gpioBFlags = 0x00);
+
+        /** Used to return the unique instance of I2C instance
+         */
+        inline const I2C * operator * () { return (const I2C *)_i2cInstance; };
+        
+        /** Reset the device
+         */
+        virtual void reset();
+        
+        virtual void setupInterrupts(const unsigned char mirroring = 0x00, const unsigned char p_openDrain = 0x00, const unsigned char polarity = 0x00);
+        /** Setup interrupt for a specific IO port
+         * @param p_pinId The IO port identifier
+         * @param p_mode  The interrupt mode
+         * @return 0 on success, negative value otherwise
+         */
+        virtual int setupInterruptPin(const unsigned char p_gpioId, const InterruptModes p_mode = OnRising);
+        virtual int getLastInterruptPin(unsigned char * p_gpioId, unsigned char * p_value);
+        
+        virtual int read(const unsigned char p_gpioId, unsigned char * p_value);
+        virtual int write(const unsigned char p_gpioId, const unsigned char p_value);
+        
+        virtual unsigned char createBus(const std::list<unsigned char> p_lines, const PinMode p_mode = PullNone);
+        virtual void deleteBus(const unsigned char p_busId);
+        virtual int busRead(const unsigned char p_busId);
+        virtual int busWrite(const unsigned char p_busId, const unsigned char p_value);
+        
+        /** Attach a function to call when a interrupt occurs on the GPIOA input ports
+         */
+        virtual void setIntrACallback(void (* p_fptr)(void));
+        
+        /** Attach a function to call when a rising edge occurs on the input
+         */
+        virtual void setIntrBCallback(void (* p_fptr)(void));
+        
+        /** Attach a member function to call when a interrupt occurs on the GPIOA input ports
+         */
+        template<typename T> 
+        void setIntrACallback(const T * p_tptr, void(T::* p_mptr)(void));
+        
+        /** Attach a member function to call when a rising edge occurs on the input
+         */
+        template<typename T> 
+        void setIntrBCallback(const T * p_tptr, void(T::* p_mptr)(void));
+        
+    private:
+    
+        /** Configure the device:
+         *   BANK0 (A register followed by B register)
+         *   INTs: active low
+         *   INTs for all ports
+         *   INTs are not mirrored
+         * @param p_gpioAFlags GPIO A port configuration: bit set for input mode, 0 for output mode. Default: ports as outputs
+         * @param p_gpioBFlags GPIO B port configuration: bit set for input mode, 0 for output mode. Default: ports as outputs
+         */
+        void configure(const unsigned char p_gpioAFlags, const unsigned char p_gpioBFlags);
+        
+        bool registerIdFromGpioId(const unsigned char p_gpioId, unsigned char * p_gpioRegisterId);
+        
+        inline unsigned char gpioBitFromGpioId(const unsigned char p_gpioId) {
+            return static_cast<unsigned char>((p_gpioId < GPIO_MED) ? p_gpioId : (p_gpioId - GPIO_MED));
+        };
+        inline bool isBitSet(const unsigned char p_gpioRegisterValue, const unsigned char p_gpioBit) {
+            return (bool)((unsigned char)(p_gpioRegisterValue >> p_gpioBit) == 0x01);
+        };
+        inline bool isBitEqual(const unsigned char p_gpioRegisterValue, const unsigned char p_gpioBit, const unsigned char p_value) {
+            return (bool)((unsigned char)(p_gpioRegisterValue >> p_gpioBit) == (p_value & 0x01));
+        };
+        inline unsigned char setBit(unsigned char p_gpioRegisterValue, const unsigned char p_gpioBit, const unsigned char p_value) {
+            return (p_gpioRegisterValue & ~(1 << p_gpioBit)) | ((p_value & 0x01) << p_gpioBit);
+        };
+        
+    protected:
+    
+        /** Write value to a register
+         * @param p_address The register address
+         * @param p_byte The value to write to the register
+         * @return 1 on success, false otherwise
+         */
+        virtual bool writeRegister(const unsigned char p_registerId, const unsigned char p_value);
+        
+        virtual bool readRegister(const unsigned char p_registerId, unsigned char * p_value);
+        
+    private:
+        /** Internal reference identifier
+         */
+        std::string _internalId;
+
+        void DumpRegisters();
+        void DumpRegister(unsigned int p_registerId);
+        
+    }; // End of class CMCP23017_I2C
+
+} // End of namespace _MCP23017_I2C
+
+using namespace MCP23017_I2C;