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:
Tue Jan 13 10:09:01 2015 +0000
Parent:
1:ec9e770173d5
Child:
3:b902729a1675
Commit message:
Add PullUp support

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
--- a/AbstractGpioExpender.h	Fri Jan 09 15:35:40 2015 +0000
+++ b/AbstractGpioExpender.h	Tue Jan 13 10:09:01 2015 +0000
@@ -1,3 +1,21 @@
+/* mbed simplified interface for GPIO expender (e.g. Microchip MCP23x17)
+ * 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 <list>
@@ -24,6 +42,8 @@
 #define GPIO_MED    GPB0
 #define GPIO_SIZE   8
 
+/** This class describes a simplified interface for GPIO expender (e.g. Microchip MCP23x17)
+ */
 class AbstractGpioExpender {
 public:
     /** Interrupt modes
@@ -34,40 +54,68 @@
         OnFalling = 0x03
     };
     
+    /** Pull modes
+     */
+    enum PullModes {
+        PullOff = 0x00,
+        PullUp = 0x01,
+        PullDown = 0x03
+    };
+    
     /** Reset the device
      */
     virtual void reset() = 0;
 
-    /** Setup interrupt mechanism for all GPIO ports
+    /** Setup device interrupt mechanism
      * @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
+    /** Setup interrupt mode 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
+     * @return 0 on success, -1 on wrong parameters and -2 otherwise
      */
     virtual int setupInterruptPin(const unsigned char p_gpioId, const InterruptModes p_mode = OnRising) = 0;
     
+    /** Setup pull mode for a specific IO port
+     * @param p_gpioId The IO port identifier
+     * @param p_mode  The interrupt mode
+     * @return 0 on success, -1 on wrong parameters and -2 otherwise
+     */
+    virtual int setupPullPin(const unsigned char p_gpioId, const PullModes p_mode = PullOff) = 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
+     * @return 0 on success, -1 on wrong parameters and -2 otherwise
      */
-    virtual int getLastInterruptPin(unsigned char * p_gpioId, unsigned char * p_value) = 0;
+    virtual int getLastInterruptPinAndValue(unsigned char * p_gpioId, unsigned char * p_value) = 0;
     
     /** Attach a function to call when a interrupt occurs on the GPIOA input ports
+     * @param p_fptr The pointer to the "C" callback function
      */
     virtual void setIntrACallback(void (* p_fptr)(void)) = 0;
     
-    /** Attach a function to call when a rising edge occurs on the input
+    /** Attach a function to call when a interrupt occurs on the GPIOB input ports
+     * @param p_fptr The pointer to the "C" callback function
      */
     virtual void setIntrBCallback(void (* p_fptr)(void)) = 0;
     
+    /** Read the specific GPIO port pin
+     * @param p_gpioId The GPIO port pin to be read 
+     * @param p_value The GPIO port pin value
+     * @return 0 on success, -1 on wrong parameters and -2 otherwise
+     */
     virtual int read(const unsigned char p_gpioId, unsigned char * p_value) = 0;
+    
+    /** Write value to the specific GPIO port pin
+     * @param p_gpioId The GPIO port pin to be written
+     * @param p_value The GPIO port pin value
+     * @return 0 on success, -1 on wrong parameters and -2 otherwise
+     */
     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;
--- a/MCP23017_I2C.cpp	Fri Jan 09 15:35:40 2015 +0000
+++ b/MCP23017_I2C.cpp	Tue Jan 13 10:09:01 2015 +0000
@@ -71,7 +71,7 @@
         if (p_intA != NC) {
             DEBUG("CMCP23017_I2C: INTA managed");
             _intA = new InterruptIn(p_intA);
-            if (p_internalPullUp) _intA->mode(PullDown);
+            if (p_internalPullUp) _intA->mode(::PullDown);
             _intA->enable_irq(); // Enable interrupt
         } else {
             DEBUG("CMCP23017_I2C: INTA not managed");
@@ -80,7 +80,7 @@
         if (p_intB != NC) {
             DEBUG("CMCP23017_I2C: INTB managed");
             _intB = new InterruptIn(p_intB);
-            if (p_internalPullUp) _intB->mode(PullDown);
+            if (p_internalPullUp) _intB->mode(::PullDown);
             _intB->enable_irq(); // Enable interrupt
         } else {
             DEBUG("CMCP23017_I2C: INTB not managed");
@@ -272,27 +272,33 @@
     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
+        // Retrieve the register address
         unsigned char gpioIntconId, gpioDefvalId, gpioGpintenId;
         if (!registerIdFromGpioId(p_gpioId, &gpioIntconId)) {
             DEBUG_LEAVE("CMCP23017_I2C::setupInterruptPin: -1")
             return -1;
         }
+        unsigned char gpioFlags;
         if (gpioIntconId == GPIOA) {
             gpioIntconId = INTCONA;
             gpioDefvalId = DEFVALA; 
             gpioGpintenId = GPINTENA;
+            gpioFlags = _gpioAFlags;
         } else {
             gpioIntconId = INTCONB;
             gpioDefvalId = DEFVALB; 
             gpioGpintenId = GPINTENB;
+            gpioFlags = _gpioBFlags;
         }
-        DEBUG("CMCP23017_I2C::setupInterruptPin: gpioIntconId=%02x gpioDefvalId=%02x gpioGpintenId=%02x", gpioIntconId, gpioDefvalId, gpioGpintenId)
+        DEBUG("CMCP23017_I2C::setupInterruptPin: gpioIntconId=%02x gpioDefvalId=%02x gpioGpintenId=%02x gpioFlags=%02x", gpioIntconId, gpioDefvalId, gpioGpintenId, gpioFlags)
         
-        // Retrive the GPIO pin number
+        // Retrieve the GPIO pin number
         unsigned char gpioBit = gpioBitFromGpioId(p_gpioId);
         DEBUG("CMCP23017_I2C::setupInterruptPin: gpioBit=%02x", gpioBit)
-        
+        if (!isBitEqual(gpioFlags, gpioBit, 0x01)) { // Port pin is not configure as input
+            DEBUG_LEAVE("CMCP23017_I2C::setupPullPin: -1")
+            return -1;
+        }        
         // Read it
         unsigned char gpioIntconValue, gpioDefvalValue, gpioGpintenValue;
         if (!readRegister(gpioIntconId, &gpioIntconValue)) {
@@ -338,8 +344,65 @@
         return 0;
     }
     
-    int CMCP23017_I2C::getLastInterruptPin(unsigned char * p_gpioId, unsigned char * p_value) {
-        DEBUG_ENTER("CMCP23017_I2C::getLastInterruptPin")
+    int CMCP23017_I2C::setupPullPin(const unsigned char p_gpioId, const PullModes p_mode) {
+        DEBUG_ENTER("CMCP23017_I2C::setupPullPin: %02x, %02x", p_gpioId, p_mode)
+        
+        // Retrieve the register address
+        unsigned char gpioGppuId;
+        if (!registerIdFromGpioId(p_gpioId, &gpioGppuId)) {
+            DEBUG_LEAVE("CMCP23017_I2C::setupPullPin: -1")
+            return -1;
+        }
+        unsigned char gpioFlags;
+        if (gpioGppuId == GPIOA) {
+            gpioGppuId = GPPUA;
+            gpioFlags = _gpioAFlags;
+        } else {
+            gpioGppuId = GPPUB;
+            gpioFlags = _gpioBFlags;
+        }
+        DEBUG("CMCP23017_I2C::setupPullPin: gpioGppuId=%02x gpioFlags=%02x", gpioGppuId, gpioFlags)
+        
+        // Retrieve the GPIO pin number
+        unsigned char gpioBit = gpioBitFromGpioId(p_gpioId);
+        DEBUG("CMCP23017_I2C::setupPullPin: gpioBit=%02x", gpioBit)
+        if (!isBitEqual(gpioFlags, gpioBit, 0x01)) { // Port pin is not configure as input
+            DEBUG_LEAVE("CMCP23017_I2C::setupPullPin: -1")
+            return -1;
+        }
+        
+        // Read it
+        unsigned char gpioGppuValue;
+        if (!readRegister(gpioGppuId, &gpioGppuValue)) {
+            DEBUG_LEAVE("CMCP23017_I2C::setupPullPin: -2")
+            return -2;
+        }
+        DEBUG("CMCP23017_I2C::setupPullPin: gpioGppuId=%02x", gpioGppuId)
+        
+        //         
+        switch (static_cast<unsigned char>(p_mode)) {
+            case static_cast<unsigned char>(AbstractGpioExpender::PullOff):
+                gpioGppuValue = setBit(gpioGppuValue, gpioBit, 0x00);
+                break;
+            case static_cast<unsigned char>(AbstractGpioExpender::PullUp):
+                gpioGppuValue = setBit(gpioGppuValue, gpioBit, 0x01);
+                break;
+            case static_cast<unsigned char>(AbstractGpioExpender::PullDown):
+                // Not supporte, nothing to do
+                break;
+        } // End of 'switch' statement
+        
+        // Write register
+        DEBUG("CMCP23017_I2C::setupPullPin: gpioGppuValue=%02x", gpioGppuValue)
+        writeRegister(gpioGppuId, gpioGppuValue);
+        
+        
+        DEBUG_LEAVE("CMCP23017_I2C::setupPullPin: 0")
+        return 0;
+    }
+    
+    int CMCP23017_I2C::getLastInterruptPinAndValue(unsigned char * p_gpioId, unsigned char * p_value) {
+        DEBUG_ENTER("CMCP23017_I2C::getLastInterruptPinAndValue")
         
         // Read first INTFA if required
         unsigned char vregister;
@@ -355,7 +418,7 @@
                     readRegister(INTCAPA, p_value);
                     *p_value = (*p_value >> bit) & 0x01;
                     
-                    DEBUG_LEAVE("CMCP23017_I2C::getLastInterruptPin (A): %02x %02x", *p_gpioId, *p_value)
+                    DEBUG_LEAVE("CMCP23017_I2C::getLastInterruptPinAndValue (A): %02x %02x", *p_gpioId, *p_value)
                     return 0;
                 }
             } // End of 'for' statement
@@ -374,20 +437,20 @@
                     readRegister(INTCAPB, p_value);
                     *p_value = (*p_value >> bit) & 0x01;
                     
-                    DEBUG_LEAVE("CMCP23017_I2C::getLastInterruptPin (B): %02x %02x", *p_gpioId, *p_value)
+                    DEBUG_LEAVE("CMCP23017_I2C::getLastInterruptPinAndValue (B): %02x %02x", *p_gpioId, *p_value)
                     return 0;
                 }
             } // End of 'for' statement
         }
         
-        DEBUG_LEAVE("CMCP23017_I2C::getLastInterruptPin: 0")
+        DEBUG_LEAVE("CMCP23017_I2C::getLastInterruptPinAndValue: 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
+        // Retrieve the register address
         unsigned char gpioRegisterId;
         if (!registerIdFromGpioId(p_gpioId, &gpioRegisterId)) {
             DEBUG_LEAVE("CMCP23017_I2C::read: -1")
@@ -430,7 +493,7 @@
     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
+        // Retrieve the register address
         unsigned char gpioRegisterId;
         if (!registerIdFromGpioId(p_gpioId, &gpioRegisterId)) {
             DEBUG_LEAVE("CMCP23017_I2C::write: -1")
@@ -438,7 +501,7 @@
         }
         DEBUG("CMCP23017_I2C::write: gpioRegisterId=%02x", gpioRegisterId)
         
-        // Retrive the GPIO pin number
+        // Retrieve the GPIO pin number
         unsigned char gpioBit = gpioBitFromGpioId(p_gpioId);
         DEBUG("CMCP23017_I2C::write: gpioBit=%02x", gpioBit)
         
@@ -484,12 +547,16 @@
     }
 
     int CMCP23017_I2C::busRead(const unsigned char p_busId, unsigned short * p_value) {
+//        DEBUG_ENTER("CMCP23017_I2C::busRead: 0x%02x", p_busId)
+
         // Sanity checks
         if (_buses.size() == 0) {
+            DEBUG_LEAVE("CMCP23017_I2C::busRead: -1")
             return -1;
         }
         std::map<unsigned char, std::list<unsigned char> >::iterator result = _buses.find(p_busId);
         if (result == _buses.end()) { // Invalid bus identifier
+            DEBUG_LEAVE("CMCP23017_I2C::busRead: -1")
             return -1;
         }
         
@@ -503,16 +570,20 @@
             }
         } // End of 'for' statement
         
+//        DEBUG_LEAVE("CMCP23017_I2C::busRead: 0")
         return 0;
     }
 
     int CMCP23017_I2C::busWrite(const unsigned char p_busId, const unsigned short p_value) {
+//        DEBUG_ENTER("CMCP23017_I2C::busWrite: 0x%02x - 0x%02x", p_busId, p_value)
+
         // Sanity checks
         if (_buses.size() == 0) {
             return -1;
         }
         std::map<unsigned char, std::list<unsigned char> >::iterator result = _buses.find(p_busId);
         if (result == _buses.end()) { // Invalid bus identifier
+            DEBUG_LEAVE("CMCP23017_I2C::busWrite: -1")
             return -1;
         }
         
@@ -523,6 +594,7 @@
             value >>= 1;
         } // End of 'for' statement
         
+//        DEBUG_LEAVE("CMCP23017_I2C::busWrite: 0")
         return 0;
     }
 
--- a/MCP23017_I2C.h	Fri Jan 09 15:35:40 2015 +0000
+++ b/MCP23017_I2C.h	Tue Jan 13 10:09:01 2015 +0000
@@ -41,6 +41,7 @@
      * @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;
@@ -48,24 +49,33 @@
         /** 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;
         
+        /** Map of declared buses
+         */
         std::map<unsigned char, std::list<unsigned char> > _buses;
         
+        /** Buses identifier index
+         */
         unsigned char _busesIndex;
         
         /** An unique instance of I2C class
@@ -109,22 +119,46 @@
          */
         virtual void reset();
         
+        /** Setup device interrupt mechanism
+         * @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 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
+         * @return 0 on success, -1 on wrong parameters and -2 otherwise
          */
         virtual int setupInterruptPin(const unsigned char p_gpioId, const InterruptModes p_mode = OnRising);
         
+        /** Setup pull mode for a specific IO port
+         * @param p_gpioId The IO port identifier
+         * @param p_mode  The interrupt mode
+         * @return 0 on success, -1 on wrong parameters and -2 otherwise
+         */
+        virtual int setupPullPin(const unsigned char p_gpioId, const PullModes p_mode = PullOff);
+        
         /** 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
+         * @return 0 on success, -1 on wrong parameters and -2 otherwise
          */
-        virtual int getLastInterruptPin(unsigned char * p_gpioId, unsigned char * p_value);
+        virtual int getLastInterruptPinAndValue(unsigned char * p_gpioId, unsigned char * p_value);
         
-        virtual int read(const unsigned char p_gpioId, unsigned char * p_value);
+        /** Read the specific GPIO port pin
+         * @param p_gpioId The GPIO port pin to be read 
+         * @param p_value The GPIO port pin value
+         * @return 0 on success, -1 on wrong parameters and -2 otherwise
+         */
+            virtual int read(const unsigned char p_gpioId, unsigned char * p_value);
+        
+        /** Write value to the specific GPIO port pin
+         * @param p_gpioId The GPIO port pin to be written
+         * @param p_value The GPIO port pin value
+         * @return 0 on success, -1 on wrong parameters and -2 otherwise
+         */
         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);
@@ -133,10 +167,12 @@
         virtual int busWrite(const unsigned char p_busId, const unsigned short p_value);
         
         /** Attach a function to call when a interrupt occurs on the GPIOA input ports
+         * @param p_fptr The pointer to the "C" callback function
          */
         virtual void setIntrACallback(void (* p_fptr)(void));
         
-        /** Attach a function to call when a rising edge occurs on the input
+        /** Attach a function to call when a interrupt occurs on the GPIOB input ports
+         * @param p_fptr The pointer to the "C" callback function
          */
         virtual void setIntrBCallback(void (* p_fptr)(void));