Categories

Archives

Elsewhere

     
       

Raspberry Pi and the MCP23017 I2C I/O Expander

I had a quick play with the I2C drivers that are currently being developed for the Raspberry Pi this afternoon and managed to get a MCP23017 16-bit I/O Expander working with it without any fuss.

Here is a highly exciting video of it blinking an LED:

The MCP23017 is a handy 28 pin chip that gives you 16 pins that can be used as either inputs or outputs (max 25mA from each pin) and up to 8 of the MCP23017 can be used on one I2C bus so it can give you a whole lot more I/O than the Pi has built in as well as reducing the risk of frying the Pi and also has the added advantage that the expander can be located away from the Pi linked with only four wires. There is also the smaller MCP23008 which is an 8 I/O version that can be used in the same way. They are both available in DIP form making it easy for building your own boards with and experimenting on breadboard. Here is the datasheet for the MCP23017.

Read on for some info on what is required to get this working.

Connecting it up

For a quick demo the following needs to be connected on the expander:

Pin 9 to Vcc (eg. 5V on the Pi or external source up to 5.5V)
Pin 10 to Ground
Pin 12 to SCL0 on the Pi
Pin 13 to SDA0 on the Pi
Pins 15,16,17 to ground (this selects the I2C address as 0×20, other combinations can set different addresses)
Pin 18 to Vcc (this turns the expander on)

For the location of the I2C and power pins on the Pi see the diagram here.  Note that the maximum you can draw from the 5V pin on the Pi will be the USB input current, typically 1A less the draw of the Pi itself, around 700mA for the Model B so that leaves us with around 300mA max. the maximum you can draw from the 5v pin on the Pi is in the region of 150-250mA, maybe less depending on the devices plugged into the Pi, more here.

For this test I also connected an LED and resistor between GPA0 on the MCP23017 (pin 21) and ground.

Drivers and i2c-tools

See this thread on the Raspberry Pi forums for the current work that is being done on i2c drivers for the Raspberry Pi, it’s still very much in the early development stages. There is the original bitbanging one (slower) and the new faster hardware one, to take a short cut for now you can just download the pre-compiled kernel with the I2C bitbanging driver built in here and simply swap it with the one in /boot on the Debian Squeeze distro and reboot.

Then you will want to install i2c-tools package via apt-get, this gives us some command line tools for scanning the I2C bus and sending values to I2C addresses and registers.

Now we can check that the Pi is communicating with the expander by doing: i2cdetect -y 0
If it is working you should see an ASCII representation of a table with 20 in the first column on the row marked 20. This signifies there is something there with an I2C address of 0×20 as expected.

Controlling the expander

The I/O pins on the MCP23017 are in two banks, A and B and each bank is controlled together. To set whether each pin is an input or an output we need to send a hex value to the correct register (see Table 1.4 in the data sheet). IODIRA (0×00) sets the input/output state for bank A and IODIRB (0×01) for bank B, set each of the 8 bits as 1 for input (the default) and 0 for output.  eg. to set pins 0, 1, 7 as input and the rest as outputs it would be 10000011 in binary or 0×83 in hex, to set a whole bank as outputs would be 0×00.

Then to turn each pin on or off we send a hex value to the register for the relevant bank, 0×12 for bank A, 0×13 for bank B. We need to send a 1 to each bit we want on and a 0 for each we want to turn off in the same manner as above, so to turn pin 0 on it’s 00000001 in binary or 0×01 in hex.

For a quick demo we can use the i2cset command that comes with i2c-tools, its format is:
i2cset i2-cbus i2c-address i2c-register value

eg.
Set all of bank A to be outputs: i2cset -y 0 0×20 0×00 0×00
Set GPA0 as on: i2cset -y 0 0×20 0×12 0×01
Set GPA0 as off: i2cset -y 0 0×20 0×12 0×00

The -y switch just turns interactive mode off so it doesn’t ask for confirmation.

Here is a simple bash script to blink it 20 times:

#!/bin/bash
i2cset -y 0 0×20 0×00 0×00
COUNTER=20
until [ $COUNTER -lt 10 ]; do
i2cset -y 0 0×20 0×12 0×01
sleep 1
i2cset -y 0 0×20 0×12 0×00
sleep 1
let COUNTER-=1
done

So there you go, a quick demo of an I2C I/O expander working on the Pi. Next I need to get set up for cross compiling and try the new hardware driver and look at how to use I2C in something a bit more advanced than bash.

UPDATE 20/5/12: I’ve got the 3.2 kernel with hardware I2C driver from here working now. One thing to note is that the 3.2 kernels seem to have some issues working with SD cards that worked fine under 3.1 The 32GB Verbatim Class 6 card I had been using was throwing “mmc0: problem reading SD Status register” and “error -110 whilst initialising SD card” errors as someone else noted in the comments there. I’ve switched to a Sandisk Class 4 MicroSD in an adapter now and that is working fine.

UPDATE 24/5/12: I’ve started work on some Python tools to control the MCP23017 using the Raspberry Pi, see this post for more information.

UPDATE 2/6/12: I’ve created a plug in expander board using the Ciseco “Slice of Pi” and the MCP23017, see this post for details and a step by step guide to getting it working with the above Python tools including a link to a Raspberry Pi kernel/modules with I2C compiled in.

17 comments to Raspberry Pi and the MCP23017 I2C I/O Expander

  • selsinork

    due to the sdcard problems with 3.2.x I backported Chris’ drivers into 3.1.9. That, along with some changes necessary to use the in-kernel mcp23s08 driver can be found here https://github.com/selsinork/raspberry-pi-i2c-spi-drivers it should be trivial to change the I2C_BOARD_INFO line in the platform data to work with the mcp23017. The SPI versions seem to need some slightly different platform data, but should be fairly simple too.

  • Peter (@PeterHaban on Twitter)

    Wouldn’t the 5V I2C connection potentially kill the RasPi???
    http://elinux.org/Rpi_Low-level_peripherals
    GPIO voltage levels are 3v3 and are not 5v tolerant. There is no over-voltage protection on the board

    Had a look at the full datasheet and unless it’s meant to run at over +85DC the MCP23017-E/SP should run off 1.8-5.5V. wouldn’t it be better to run it off 3.3V instead of 5V?
    http://www.farnell.com/datasheets/12170.pdf

  • Hi Peter,

    Funnily enough somebody mentioned that on Twitter last night. The reason it works is that the I2C lines are pulled high (to 3v3) by the Pi and devices on the I2C bus will pull them low so the Pi is never exposed to 5V from the MC23017. The only caveat is that the I2C slave device has to be capable of registering the 3v3 as a high, not a problem for the MCP23017.

    You could run it from the Pi’s 3v3 pin but you can only draw 50mA from that which doesn’t leave much to play with across 16 potential outputs, whereas with the 5v pin you can draw the difference between the input current and the draw of the Pi + accessories as mentioned above. If you need 3v3 IO then it would be better to power the chip from an external source which will work fine as long as the ground is common.

    I’ve also used one of the Ciseco “Slice of Pi” boards to make a plug in expander board using this, see: http://nathan.chantrell.net/20120602/raspberry-pi-io-expander-board/

    Cheers,
    Nathan

  • selsinork

    “The only caveat is that the I2C slave device has to be capable of registering the 3v3 as a high, not a problem for the MCP23017″

    Well it is a problem. Practically it might just work, sometimes, if you’re lucky.

    Check the datasheet Peter linked, page 28, ‘Input High Voltage’ Param No. D041, CS, GPIO, SCL/SCK, SDA, A2, RESET, min 0.8 Vdd, For entire Vdd range.

    0.8 x 5v is greater than 3.3v here :) So will it work ? Probably, possibly, maybe.. Not guaranteed though, and relying on it for anything important probably not a good idea.

    Given that level shifting I2C can be done with two n channel fets with a total cost of about 10p I can’t see a reason why you wouldn’t.

    The main polyfuse on my Pi is only 700mA, so if you assume two 100mA usb devices and the base idle current of about 460mA I see with my Pi you can’t assume to have much spare current available even from the 5v pin on the gpio header.
    So I think you’re right with the advice to power external stuff from it’s own supply – it’s certainly what I do after finding out just how crappy phone chargers and micro usb cables really are. Plenty of threads on the forums and elsewhere on power issues.

  • Yes that’s the same datasheet I linked to in the post. I wasn’t sure if it would work until I tried it, agree it is out of spec but has worked flawlessly under my tests so far, maybe I’m just lucky. I guess it would drop off rapidly if you tried to distance the expander from the Pi though. Good tip about level shifting with a couple of FETs, thanks, I had thought about using something like a P82B96 if it didn’t work but that would be a nice cheap way to do it and definitely a good idea.

    Seems you are right about the polyfuse on the microUSB port being 700mA, I’d simply gone off what is says on the eLinux wiki here but that can’t be right. The link to the citation for that claim has been broken by the forum switch too.

  • Peter (@PeterHaban on Twitter)

    Thanks for the explanation, makes sense.
    I’ve ordered Slice of Pi and some MC23017s yesterday so your other post will come in very handy Thanks for sharing all this!

    Cheers,
    Peter

  • selsinork

    The fet level shifter isn’t my idea, the details are in http://ics.nxp.com/support/documents/interface/pdf/an97055.pdf

    There’s other IC solutions like the PCA9306 too

    These days 3.3v is the norm, heading towards 1.8v.. I think virtually all the i2c devices I’ve got will work from 1.8v up to 5.5v, so reading between the lines you’re probably fine for short runs. I’d be more concerned with a 5v only device, or a 10m cable.

    Really, the spec is there for one reason. So that they can disclaim liability when you run it out of spec, it doesn’t work and you’ve burned the house down with your home automation project. Much like lots of the datasheets come with disclaimers on using the device in medical equipment etc.

  • Thanks for the link, I found the same diagram in the I2C specification doc and included it here, nice and simple.

    It does the job for my purposes as is for now but good to have a easy cheap fix ready if required.

    Cheers,
    Nathan

  • Anderw (@arpsquire on Twitter)

    Great guide! I used it to recreate your flashing LED using a Raspberry Pi starter kit from SK Pang http://www.skpang.co.uk/catalog/starter-kit-for-raspberry-pi-b-p-1107.html

    Obligatory video here: http://www.youtube.com/watch?v=zHdDeJWnDHs

  • Sancho

    Great idea. Thanks for that. I used it a bit different – built an USB-tiny-i2c adaptor and now I have a cheap router with lots of I/O for cheap, too :)
    Thanks again!

  • bmi

    Hi,
    I’m trying to interface with a MCP23016 using i2c.
    I think I set up everything as it should, but when I try to do a i2cset, I keep getting write errors. Also trying to write to the ic from a sample program gives the same I/O errors

    Any ideas?

    - I’m running the latest wheezy (2012-07-15)
    - i2detect shows my ic at 0×20
    - I did a chmod 666 on /dev/i2c-0 (even a chmod 777 just to be sure)

  • Stephan

    @bmi I’ve the same problem with the 23016. No chance until now to write anything successful into the chip.

  • bmi

    Stephan,

    After not getting the 23016 to work, I just ordered a few 23017′s.
    And they are working just fine!
    I read somewhere that the 23016 has problems working with “new technology”.

  • idem2lyon (@idem2lyon on Twitter)

    Hi Nat,
    have you ever try to use a DS18B20 or a LN2803A with the MCP23017 ?

  • zorge

    Good day. Please, need some advice – how to connect several 8×8 matrixes to mcp23017.For example 4 of 8×8 in series to make running text. Is need more mcp chips or one enought? Thanks

  • syfon

    Hello
    Explain me, please, the different between register GPIOA and OLATA in MCP23017. After rtfm and stfw I thought, that OLATA (0×14) is dedicated to write high signal to “LED” (PIOA is to read…) I checked it and 0×12 going OK, but why?

    Sorry for bugs, i’m not english speaking.

    Z partyjnym pozdrowieniem
    syfon

  • David Walker

    Hi, get a copy of the MCP23017 datasheet, you cannot get into this level of playing with the registers without it . Here is a relevant statement from it (page 9):

    Reading the GPIOx register reads the value on the port. Reading the OLATn register only reads the latches, not the actual value on the port. The GPIO module is a general purpose, 16-bit wide, bidirectional port that is functionally split into two 8-bit wide ports. Writing to the GPIOn register actually causes a write to the latches (OLATn). Writing to the OLATn register forces the associated output drivers to drive to the level in OLATn. Pins configured as inputs turn off the associated output driver and put it in high-impedance.

    I have made the experience that it is much simpler to place the MCP23017 in IOCON.BANK=1 mode (avoids toggling the GPIOs registers as in IOCON.BANK=0 mode). But watch out though – the addresses of the registers in this mode are very DIFFERENT (see page 5 of the datasheet)

Leave a Reply


 


 


 

You can use these HTML tags
(links will be automatically marked with rel="nofollow")

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Notify me of followup comments via e-mail. You can also subscribe without commenting.

By submitting a comment you agree that it can be reproduced under the same licensing terms as the rest of the content on this site.