How to use the MCP23017 Port Expander with the Raspberry Pi
The Raspberry Pi is a great device with a load of I/O’s; but sometimes it’s just not enough! If you need more I/O's than the Pi can deliver, then there are a number of IC’s that you can use to expand the number of I/O’s available. In this tutorial I’m going to show you how to use the MCP23017 port expander chip; it uses two i2c pins (which can be shared with other devices if required) and in exchange gives the Pi another 16 GPIO's! Each I/O pin can be configured as an in input, output or an input with a pullup. You can set the i2c address for up to 8 unique MCP23017’s on the same i2c bus, which means you can daisy-chain these IC's to give you up to 128 I/O's!
Seven segment displays are a good example of devices that require a lot of I/O’s, so in this tutorial we’re going to hook up a couple of seven segment displays to an MCP23017 IC and control it all via the Pi’s i2c bus! We’re going to create a little counter using 2 seven segment displays and 2 buttons, where the buttons will allow you to increase and decrease the value displayed on the seven segments. Perfect for scoring games!
Setting up the Raspberry Pi
There’s a couple things we need to do with our Pi before we can use the MCP23017 chip and python.
Enable I2C
If your raspberry pi has booted to desktop, then navigate to the Raspberry Pi Configuration Screen (under preferences), click the “Interfaces” tab, then enable I2C.
If you are using a terminal, then load up raspi-config:
sudo raspi-config
Go to Advanced Options
Then to I2C
Then Yes to enable
You’ll need to reboot your Pi once you have made this change.
Install wiringpi python library
First make sure our package list is up to date
sudo apt-get update
Now install some prerequisites
sudo apt-get install python-dev python-pip
Now we can install wiringpi
sudo pip install wiringpi
Wiring Up the MCP23017
Let’s take a look at the pin layout of the MCP23017, so we know how to wire it up
Starting at pin 1 – pin 8 we have 8 of our additional GPIO pins GPB0 – GPB7
Pin 9 Vdd needs to be wired to the Pis 3v3 pin, Pin 1
Pin 10 Vss needs to be wired to a ground pin on our Pi, Pin 6
Pin 12 SCL needs to be wired to the SCL pin on our Pi, Pin 5
Pin 13 SDA needs to be wired to the SDA pin on our Pi, Pin 3
Pins 15 – 17 allow us to change the address of the chip, by hooking them up to either ground or 3v3. Wire all three pins to ground on our Pi, Pin 6
Pin 18 needs to wired to 3v3, Pin 1
Pins 21 – 28 are another 8 additional GPIO pins GPA0 – GPA7
Now we know where each pin needs to go, we can wire the whole thing up.
The resistors used in the circuit are:
Brown Black Brown (outer most resistors) – 100 Ohms
Red Red Brown (inner most resistors) – 220 Ohms
These resistors serve two purposes. Firstly, they are setup to be voltage dividers, and secondly, they will limit the amount of current drawn.
Once everything is wired up, simply download our example python script from github and run it.
git clone git://github.com/modmypi/MCP23017_7_segment_display.git
cd MCP23017_7_segment_display
sudo python counter.py
When you first run the code you should see “00” displayed on the seven segments. If you don’t you’ll need to go back and double check everything has been wired up correctly.
If everything is showing as expected, press the button closest to the chip to increase the value, and the other to decrease it. You’ll notice that when you get to -9 you can’t go any lower. Likewise, if you go all the way up to 99, you won’t be able to go any higher.
The script has some comments in explaining what the various lines do. Some of the code needs a little more explanation which can be found below.
Script comments
Increase function
Line 74 – Let’s break this line up into the different functions:
len(seven_seg) – This simply returns the length of the dictionary “seven_seg” which is 2
ljust(arg1,arg2) – ljust pads a string out to a specific length, adding a defined character to the right of the string. Arg1, in our case, is the length of the seven_seg dictionary, 2. Arg2 is “0”
str() – Simply converts to a string. In our case we want to treat the integer 1 as a string.
So what does it mean when it’s put together? It means – Treat the integer, 1, as a string then add zeros at the beginning of that 1 until we have the same amount of characters as we do seven segment displays. In this example we have 2 seven segment displays in our seven_seg dictionary, so we end up with max = 10.
Line 75 – Convert our string “10” to an integer, or number. Multiply it by 10, then minus 1. This gives us 99.
So what’s the purpose of these two lines of code? Well as we only have 2 seven segment displays, we are limited to a maximum number, 99. If the number rolled over to 100, we wouldn’t be able to display it. So with this “max” variable, we can compare it against our “counter” variable and when the counter becomes greater than max, 99, we just set the counter variable back to max so it never reaches 100.
Line 79 – A while statement will continually loop through its code all the time its statement is true. We use “while not” here because of the pull up resistor used for our switches. When the switch is not being pressed, its pin has a high state, or “true”. When the switch is pressed, it connects to ground and has a low state, or “false”. So “while not false” actually reads “while true” because of the double negative, which is the same as saying “while our button is pressed”. So whilst our button is pressed we force the script into a little loop, but as soon as our switch changes to a true state, or when we release the switch, the statement then reads “while not true” or “while false” and no longer loops because the statement is no longer true. Remember, statements only execute when they are “true”.
Decrease function
Line 85 – same as line 74 in our increase function
Line 86 – When the plus symbol is used with strings, it simple joins the strings together. So this line adds the minus symbol to our “min” string. At this point min = “-10”
Line 87 – Again we convert our string to an integer/number then add 1. Now min = -9
Similar to the increase function, the purpose of this min variable is to make sure our number doesn’t go below a certain number so we can always display the number on our seven segment displays. Remember we are limited to -9 because we want to display the “-“ as a character, therefore taking up one of our seven segment displays.
Display function
Line 94 – You’ll notice that this function has a couple or parameters, number and display. This allows us to pass variables to the function. In this case, we can pass the number we want to display, and the group of seven segment displays we want to show it on.
Line 95 – zfill in this line simply adds zeros to the beginning of a string until the string length matches the specified length. So our specified length is len(display) display will contain the amount of seven segments that our in the group. As we only have one group of seven segments in this example script we know this value will be 2. So if our number is 1, we pad the beginning of it with zeros until its length is 2, so we end up with “02”
Line 96 – Here we have a for loop. By enumerating the number variable we are able to get the key (or position) of each element within the variable, then use it later on within the loop. The “i” variable will contain the key and the “c” variable will contain the value. So for example if the number is “04” at position 0 the value is 0, and at position 1 the value is 4.
Line 97 – Another for loop! This time we are looping through a range of numbers. Starting at 0, loop 7 times incrementing by 1 each loop. 0,1,2,3,4,5,6. We loop 7 times because we have 7 segments in our seven segment displays. Makes sense huh J
Line 98 – Here we are setting a pin on our port expander either high or low. The first argument “display[i][x]” tell us which pin we want to set, whilst the second argument “num[c][x]” tells us if we are going high or low. Ok so how does that tell us that information? Our display variable will be passed to the function when the function is called, you’ll see later on in the script, but for now I can tell you that the variable will be seven_seg. So if we look at that variable we can see it’s our dictionary containing how many seven segments we have, and the pin number for each of the segments on each of the displays. display[i], this tells us which seven segment in the group we are targeting, then display[i][x] the “x” tells us which segment on the seven segment we are targeting. Similarly, num[c] this tells us which character we want to target, then num[c][x] tells us the state (high or low) in which each segment needs to be on the display to show us the correct character.