MCP23008 General Purpose Driver

Discussion about programs, libraries and tools that work with MicroPython. Mostly these are provided by a third party.
Target audience: All users and developers of MicroPython.
Post Reply
Stevo52
Posts: 38
Joined: Sun Feb 03, 2019 10:28 pm

MCP23008 General Purpose Driver

Post by Stevo52 » Fri Aug 28, 2020 9:29 am

Can anyone point me to a working MCP23008 (I2C GPIO Expander) for Pyb D or even Pyboard 1.1 or other mPython boards.

I have found some examples that I have massaged to get some way to a suitable working example but it's a bit inconsistent and not quite right. I have the application note but I have found the examples I have quite different in the way the are implemented and work - get a lot of errors from the older drivers because they use older i2c commands, but even with the new commands it doesn't work as I imagined. Much searching hasn't turned up many drivers/applications/examples but they might be hidden in other code.

One intended application is to read some relay closures, with the GPIO pins set as inputs with pull ups, but the contact closures are not detected correctly. other applications would be similar, reading switches, keypads, running displays etc. The display example I have (D. Hylands) works quite well and I am using that in a modified form. not a lot of speed required for the setting and getting options either.

I have the X and Y i2c busses on the Pyb D working very well, other sensors are addressed and work with great stability so that is not the option, and the custom IO board I made up tests OK statically, but this MCP23008 device is giving me grief. :|

Thanks in advance for any help. Steve W.

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

Re: MCP23008 General Purpose Driver

Post by dhylands » Fri Aug 28, 2020 3:15 pm

Adafruit has an i2c LCD backpack. I have working code for that backpack (I know it works on the pyboard 1.1)

You can see it here: https://github.com/dhylands/python_lcd/ ... uit_lcd.py

This line: https://github.com/dhylands/python_lcd/ ... lcd.py#L50 sets all of the registers to known values.

The very first \x00 is the register number for IODIR
The \xff is the data for the IODIR which sets all of the GPIOs to inputs
The remaining \x00's go into the following registers to initialize them all to \x00 (the address is auto incremented).

After this initial write, the remaining manipulations all occur like this: https://github.com/dhylands/python_lcd/ ... lcd.py#L53 which specifies a register and a value.

Stevo52
Posts: 38
Joined: Sun Feb 03, 2019 10:28 pm

Re: MCP23008 General Purpose Driver

Post by Stevo52 » Fri Aug 28, 2020 9:03 pm

@dhylands

Thanks, I have used tried, because usually your coding works, is clear, short and direct - which I like) but had to modify it because of changes to the i2c object in V1.12..

1. There are 11 registers? but that line of code only allows for 10? I have tried the original code, plus adding the 11th reg (0A)?

2. Send command is no longer used apparently (Ver 1.12 docs I2C), and now ic2_addr is the first parameter so I use:

Code: Select all

self_.i2c.writeto(self.i2c_addr, b'\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00')


I'll keep working on it, because it 'should' be working :-)
Last edited by Stevo52 on Fri Aug 28, 2020 10:08 pm, edited 1 time in total.

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

Re: MCP23008 General Purpose Driver

Post by dhylands » Fri Aug 28, 2020 9:37 pm

I took a look at the datasheet and yeah you could add the extra \x00 to write to the OLAT register for initialization.

It turns out that writing to the GPIO register also writes to the OLAT register, so it doesn't make too much difference one way or the other.

Note that if you use writeto_mem then you would probably need to do something like this:

Code: Select all

self_.i2c.writeto_mem(self.i2c_addr, IODIR, b'\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00')
The first byte of send is the register that you're writing to (and \x00 corresponds to IODIR).

When you use writeto_mem you split these into two parameters so you'd remove the leading \x00 from the data.

Also be aware that there is a difference between the machine.I2C and pyb.I2C modules (this code was written for use with the pyb.I2C and methods like writeto_mem only exist in the machine module and not in the pyb module).

Stevo52
Posts: 38
Joined: Sun Feb 03, 2019 10:28 pm

Re: MCP23008 General Purpose Driver

Post by Stevo52 » Fri Aug 28, 2020 10:20 pm

Yes, I am using the machine.I2C module (mainly for future use on other devices).

Definitely some differences in using writeto and writeto_mem but have got that bit.. especially nominating the initial register

I think my problems are all related to the readfrom, readfrom_mem differences, but I am making progress after going back a few edits...

Thanks for your response.

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

Re: MCP23008 General Purpose Driver

Post by dhylands » Sat Aug 29, 2020 12:25 am

For the MCP23008, I would expect you would only use readfrom_mem. This is basically a combined write/read.

addr is the address of the i2c device.
memaddr is the register that you want to read.

It will first send memaddr (which would be the register number) and then do a read of a nbytes bytes.

Even if you're reading a single byte, you'll still get back a bytes object with length 1, so you'll need to use something like:

Code: Select all

data = i2c.readfrom_mem(i2c_addr, GPIO, 1)
print('Read ', hex(data[0]))
readfrom just does a read, it doesn't send a register number, so it's designed for using with different devices (or perhaps different setups of devices).

readfrom_mem will allocate a new bytes object every time (for the return value), whereas readfrom_mem_into will read into a previously allocated buffer (which can help to prevent heap fragmentation).

Stevo52
Posts: 38
Joined: Sun Feb 03, 2019 10:28 pm

Re: MCP23008 General Purpose Driver

Post by Stevo52 » Sat Aug 29, 2020 2:02 am

Thanks again Dave,

Agreed, readfrom_mem is the best option, which I have done and now have the code running for the last couple of hours while I cycle through the DUT tests. I had already implemented much of what you have suggested - probably because of a decent sleep.. :-) but sometimes it is handy to bounce ideas.. some of the examples I found were over complicated, which is not my favorite flavour in anything.

So far the entire code is working great, so thanks for your help which helped me straighten out my thinking and code (which is now compact and non frivolous apart from some debugging prints). Two sets of the GPIO's are managing 3 wire RS232, and also haven't missed a beat so pleased with that part (uasyncio helps there).

User avatar
mcauser
Posts: 507
Joined: Mon Jun 15, 2015 8:03 am

Re: MCP23008 General Purpose Driver

Post by mcauser » Tue Sep 08, 2020 12:56 am

I wrote a driver for the MCP23017, which is similar to the MCP23008, only with two 8-bit ports instead of one.
https://github.com/mcauser/micropython-mcp23017
You may be able to pinch some code/ideas from it.

Stevo52
Posts: 38
Joined: Sun Feb 03, 2019 10:28 pm

Re: MCP23008 General Purpose Driver

Post by Stevo52 » Mon Sep 14, 2020 9:16 pm

@mcauser Thanks, I had worked out everything I needed before you posted but it is always good to look at other builds and techniques, so I will keep yours as a reference for the future.

Post Reply