MCP23008 I2C Driver

Discuss development of drivers for external hardware and components, such as LCD screens, sensors, motor drivers, etc.
Target audience: Users and developers of drivers.
Post Reply
papahabla
Posts: 4
Joined: Wed Jun 08, 2016 12:39 am

MCP23008 I2C Driver

Post by papahabla » Wed Jun 08, 2016 1:29 am

I'm trying to expand the GPIO of an Adafruit Feather HUZZAH using the MCP23008 expander. There is some Arduino code I'm using as a model and when I run the toggle example sketch, it works, so I know I have everything wired correctly. However, I can't seem to get the port expander working in micropython.

Working Arduino Driver: https://github.com/adafruit/Adafruit-MCP23008-library
MCP23008 Datasheet: https://cdn-shop.adafruit.com/datasheets/MCP23008.pdf


Below is some code attempting to setup the expander as output pins and toggle them.

#########################################
# Toggle MCP23008 output pins
#########################################
import time
from machine import Pin, I2C

MCP23008_ADDRESS = 0x20
i2caddr = MCP23008_ADDRESS | 0x0

i2c = I2C(scl=Pin(5), sda=Pin(4), freq=100000)
i2c.start()
i2c.writeto(i2caddr, b'\x00\x00') # MCP23008_IODIR = 0x00, ALL OUTPUT
i2c.writeto(i2caddr, b'\x01\x00') # MCP23008_IPOL = 0x01
i2c.writeto(i2caddr, b'\x02\x00') # MCP23008_GPINTEN = 0x02
i2c.writeto(i2caddr, b'\x03\x00') # MCP23008_DEFVAL = 0x03
i2c.writeto(i2caddr, b'\x04\x00') # MCP23008_INTCON = 0x04
i2c.writeto(i2caddr, b'\x05\x00') # MCP23008_IOCON = 0x05
i2c.writeto(i2caddr, b'\x06\x00') # MCP23008_GPPU = 0x06
i2c.writeto(i2caddr, b'\x07\x00') # MCP23008_INTF = 0x07
i2c.writeto(i2caddr, b'\x08\x00') # MCP23008_INTCAP = 0x08
i2c.writeto(i2caddr, b'\x09\xff') # MCP23008_GPIO = 0x09, ALL ON
i2c.writeto(i2caddr, b'\x0a\x00') # MCP23008_OLAT = 0x0A
i2c.stop()

for n in range(5):
i2c.start()
i2c.writeto(i2caddr, b'\x09\xff') # ON
i2c.stop()
time.sleep_ms(500)
i2c.start()
i2c.writeto(i2caddr, b'\x09\x00') # OFF
i2c.stop()
time.sleep_ms(500)
Last edited by papahabla on Wed Jun 08, 2016 2:28 pm, edited 1 time in total.

User avatar
deshipu
Posts: 1388
Joined: Thu May 28, 2015 5:54 pm

Re: MPC23008 I2C Driver

Post by deshipu » Wed Jun 08, 2016 8:01 am

You shouldn't be using the low-level i2c.start() and i2c.stop(). Basically every time you see beginTRansmission-endTransmission in Arduino, that should translate to a single i2c.writeto() or i2c.writeto_mem() call.

papahabla
Posts: 4
Joined: Wed Jun 08, 2016 12:39 am

Re: MCP23008 I2C Driver

Post by papahabla » Wed Jun 08, 2016 5:58 pm

Thanks @deshipu, I'm sure that's getting me a lot closer to making it work. I now have the following code which I think is equivalent to the Arduino code but it still doesn't seem to work.

#########################################
# Toggle MCP23008 output pins
#########################################
import time
from machine import Pin, I2C

# Setup expander equivalently to the following Arduino code except with GPIO
# pins as all outputs pins.
#
# i2caddr = addr;
# Wire.begin();
# Wire.beginTransmission(MCP23008_ADDRESS | i2caddr);
# Wire.write((byte)MCP23008_IODIR);
# Wire.write((byte)0xFF); // all inputs
# Wire.write((byte)0x00);
# Wire.write((byte)0x00);
# Wire.write((byte)0x00);
# Wire.write((byte)0x00);
# Wire.write((byte)0x00);
# Wire.write((byte)0x00);
# Wire.write((byte)0x00);
# Wire.write((byte)0x00);
# Wire.write((byte)0x00);
# Wire.endTransmission();

MCP23008_ADDRESS = 0x20
i2caddr = MCP23008_ADDRESS | 0x0
i2c = I2C(scl=Pin(5), sda=Pin(4), freq=100000)
i2c.writeto(i2caddr, b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')

# Toggle all output pins.
#
# Wire.beginTransmission(MCP23008_ADDRESS | i2caddr);
# Wire.write((byte)MCP23008_GPIO);
# Wire.write((byte)data);
# Wire.endTransmission();

for n in range(5):
i2c.writeto(i2caddr, b'\x09\xff') # MCP23008_GPIO, ON
time.sleep_ms(500)
i2c.writeto(i2caddr, b'\x09\x00') # MCP23008_GPIO, OFF
time.sleep_ms(500)

User avatar
deshipu
Posts: 1388
Joined: Thu May 28, 2015 5:54 pm

Re: MCP23008 I2C Driver

Post by deshipu » Thu Jun 09, 2016 5:42 am

Make sure to use the same address that i2c.scan() shows.

papahabla
Posts: 4
Joined: Wed Jun 08, 2016 12:39 am

Re: MCP23008 I2C Driver

Post by papahabla » Fri Jun 10, 2016 2:51 pm

The scan() method seems to return all scanned addresses.

>>> i2c.scan()
[8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119]

User avatar
deshipu
Posts: 1388
Joined: Thu May 28, 2015 5:54 pm

Re: MCP23008 I2C Driver

Post by deshipu » Fri Jun 10, 2016 5:25 pm

Did you reverse SDA and SCL? Did you forget the pullup resistors?

papahabla
Posts: 4
Joined: Wed Jun 08, 2016 12:39 am

Re: MCP23008 I2C Driver

Post by papahabla » Sat Jun 11, 2016 4:35 am

Yikes! Yes, 1k pull-ups did the trick. I guess the Arduino code automatically pulled up the pins in the Wire.begin().

i2c = I2C(scl=Pin(5, Pin.OUT, Pin.PULL_UP), sda=Pin(4, Pin.OUT, Pin.PULL_UP), freq=400000)



Thank for your help! I'll post the driver code when it's done in case it's useful to someone.

CrankshawNZ
Posts: 2
Joined: Tue Apr 11, 2017 10:00 am

Re: MCP23008 I2C Driver

Post by CrankshawNZ » Tue Apr 11, 2017 10:25 am

I needed this as well for a project myself so I've started a module as per below github link. I'm a bit new to all this so please speak up if you have (constructive) criticism.

All the registers are exposed as properties.
I've started adding convenience methods as I need them, but havn't done many as you'll see.

Hopefully this is of use to someone. Feel free to do whatever you want with it (I should probably put an MIT license in there somewhere). It's only had light testing, so don't be surprised if it's still got errors.

It should cope with multiple mcp23008's on the I2C bus if you instantiate several and give them different addresses. Addresses are same as show up in an i2c.scan() but should generally be 32+

Code is at:

https://github.com/CrankshawNZ/Micropyt ... cp23008.py

Example usage:

from machine import Pin, I2C
from mcp23008 import MCP23008

i2c = I2C(scl=Pin(4), sda=Pin(5), freq=100000)
m = MCP23008(i2c = i2c, address = 32)

m.setPinDir(2,0) # Make pin 2 output
m.setPinLow(2) # Set pin 2 low
m.setPinHigh(2) # Set pin 2 high
print m.OLAT # Print state of OLAT register (output)
print m.GPIO # Print state of GPIO register (input)

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: MCP23008 I2C Driver

Post by dhylands » Wed Apr 12, 2017 4:46 am

I didn't notice this post before. I did a driver for the Adafruit LCD backpack which uses the MCP23008. You can find the LCD driver here:
https://github.com/dhylands/python_lcd/ ... uit_lcd.py

If you're looking for a general purpose GPIO driver, then using one of the previously posted ones is probably better.

CrankshawNZ
Posts: 2
Joined: Tue Apr 11, 2017 10:00 am

Re: MCP23008 I2C Driver

Post by CrankshawNZ » Wed Apr 12, 2017 11:57 am

This one is for general purpose usage yes but I can already see how to make things a bit simpler from your lcd module thanks :)

I'll be using this to read switch states when some more parts arrive. For now it's still in blink an LED territory.

Do you have an example of an existing general purpose one? I've been looking but my searches aren't going so well (I'm probably using the wrong keywords).

Post Reply