adafruit/Adafruit-Raspberry-Pi-Python-Code

patch for Adafruit_MCP230xx/Adafruit_MCP230xx.py

lmamakos opened this issue · 2 comments

I had problem using this library with an MCP23017 device, and made some bugfix changes and I think some robustness improvements.

I think the design of the library needs to change a bit; this notion of using the number of GPIOs to select between two different devices rather than explicit selection between an MCP23008 vs an MCP23017 is a bit confusing. I don't think it useful to have anything other than 8 or 16 as values for num_gpios..

If, for example, someone is only using a 2 GPIO pins on port A but they have an MCP23017 device, the library will malfunction because the register addresses will be wrong. This could be mitigated by changing the IOCON.BANK bit in the IOCON configuration register to 1 so that the register address for both devices match..

In any case, here are the changes I made if there's interest. I was attempting to use the write8 and readU16 methods, without success. In the write8 case, there seems to be an implicit assumption that the use will only use this with an MCP23008 device since that register address is hardcoded.

In the case of doing a readU16 or readS16 call, that code was just broken, apparently due to a cut-n-paste error and was referencing the wrong register entirely.

I also attempted to initialize the chip into a deterministic state (resetting input polarity registers), and set the interrupt output to open-drain to minimize damage if the outputs are connected together in whatever circuit the device might happened to be installed in.

--- a/Adafruit_MCP230xx.py
+++ b/Adafruit_MCP230xx.py
@@ -32,6 +32,13 @@ MCP23017_GPPUA  = 0x0C
 MCP23017_GPPUB  = 0x0D
 MCP23017_OLATA  = 0x14
 MCP23017_OLATB  = 0x15
+MCP23017_IPOLA  = 0x02
+MCP23017_IPOLB  = 0x03
+
+MCP23017_IOCON  = 0x0A
+MCP23017_IOCON_ALT = 0x05
+MCP23017_IOCON_INIT = 0x44   # mirror INT pins, INT pins are open-drain
+
 MCP23008_GPIOA  = 0x09
 MCP23008_GPPUA  = 0x06
 MCP23008_OLATA  = 0x0A
@@ -52,8 +59,15 @@ class Adafruit_MCP230XX(object):
             self.direction = self.i2c.readU8(MCP23017_IODIRA)
             self.i2c.write8(MCP23008_GPPUA, 0x00)
         elif num_gpios > 8 and num_gpios <= 16:
+            self.i2c.write8(MCP23017_IOCON_ALT, 0x00)   # clear BANK bit if device in other mode
+                                                        # if device is in correct mode, this register
+                                                        # is aliased by GPINTENB and this will disable interrupts
+            self.i2c.write8(MCP23017_IOCON, MCP23017_IOCON_INIT)
+
             self.i2c.write8(MCP23017_IODIRA, 0xFF)  # all inputs on port A
             self.i2c.write8(MCP23017_IODIRB, 0xFF)  # all inputs on port B
+            self.i2c.write8(MCP23017_IPOLA, 0x00)   # normal input polarity (non-inverting) port A
+            self.i2c.write8(MCP23017_IPOLB, 0x00)   # normal input polarity (non-inverting) port B
             self.direction = self.i2c.readU8(MCP23017_IODIRA)
             self.direction |= self.i2c.readU8(MCP23017_IODIRB) << 8
             self.i2c.write8(MCP23017_GPPUA, 0x00)
@@ -80,9 +94,8 @@ class Adafruit_MCP230XX(object):
         if self.num_gpios <= 8:
             return self._readandchangepin(MCP23008_GPPUA, pin, value)
         if self.num_gpios <= 16:
-            lvalue = self._readandchangepin(MCP23017_GPPUA, pin, value)
             if (pin < 8):
-                return
+                return self._readandchangepin(MCP23017_GPPUA, pin, value)
             else:
                 return self._readandchangepin(MCP23017_GPPUB, pin-8, value) << 8

@@ -92,7 +105,7 @@ class Adafruit_MCP230XX(object):
             self.direction = self._readandchangepin(MCP23017_IODIRA, pin, mode)
         if self.num_gpios <= 16:
             if (pin < 8):
-                self.direction = self._readandchangepin(MCP23017_IODIRA, pin, mode)
+                self.direction = (self.direction & 0xff00) | (self._readandchangepin(MCP23017_IODIRA, pin, mode) & 0xff)
             else:
                 self.direction |= self._readandchangepin(MCP23017_IODIRB, pin-8, mode) << 8

@@ -125,29 +138,39 @@ class Adafruit_MCP230XX(object):
         return value & (1 << pin)

     def readU8(self):
-        result = self.i2c.readU8(MCP23008_OLATA)
+        if self.num_gpios > 8:
+            result = self.i2c.readU8(MCP23017_GPIOA)
+        else:
+            result = self.i2c.readU8(MCP23008_GPIOA)
         return(result)

     def readS8(self):
-        result = self.i2c.readU8(MCP23008_OLATA)
+        if self.num_gpios > 8:
+            result = self.i2c.readU8(MCP23017_GPIOA)
+        else:
+            result = self.i2c.readU8(MCP23008_GPIOA)
         if (result > 127): result -= 256
         return result

     def readU16(self):
         assert self.num_gpios >= 16, "16bits required"
-        lo = self.i2c.readU8(MCP23017_OLATA)
-        hi = self.i2c.readU8(MCP23017_OLATB)
+        lo = self.i2c.readU8(MCP23017_GPIOA)
+        hi = self.i2c.readU8(MCP23017_GPIOB)
         return((hi << 8) | lo)

     def readS16(self):
         assert self.num_gpios >= 16, "16bits required"
-        lo = self.i2c.readU8(MCP23017_OLATA)
-        hi = self.i2c.readU8(MCP23017_OLATB)
+        lo = self.i2c.readU8(MCP23017_GPIOA)
+        hi = self.i2c.readU8(MCP23017_GPIOB)
         if (hi > 127): hi -= 256
         return((hi << 8) | lo)

     def write8(self, value):
-        self.i2c.write8(MCP23008_OLATA, value)
+        if self.num_gpios > 8:
+            self.i2c.write8(MCP23017_OLATA, value)
+        else:
+            self.i2c.write8(MCP23008_OLATA, value)
+

     def write16(self, value):
         assert self.num_gpios >= 16, "16bits required"

Can you help me use this library with 2 or more MCP23017? I tried some ways but without succeed, only with a single MCP23017, according to the tutorial: https://cdn-learn.adafruit.com/downloads/pdf/mcp230xx-gpio-expander-on-the-raspberry-pi.pdf

Thank you for the Issue!
This library has been deprecated in favor of our python3 Blinka library. We have replaced all of the libraries that use this repo with CircuitPython libraries that are Python3 compatible, and support a wide variety of single board/linux computers!

Visit https://circuitpython.org/blinka for more information

CircuitPython has support for almost 200 different drivers, and a as well as FT232H support for Mac/Win/Linux!