Testing out the SPI interface on the Raspberry Pi with 16 LEDs and a couple of shift registers.
The Raspberry Pi was designed for the educational market, but one of the reasons the hacker/maker community has become so attached to it is its GPIO (General Purpose Input Output) port – a set of connections for interfacing with external electronics. The Pi’s GPIO isn’t quite as full-featured as the Arduino – there are fewer pins, and no built-in analog inputs – but it does have a lot of the basic connections like a dedicated SPI bus, I²C bus, and an external serial port.
After having fun with my Python Life game last week, I wanted to try out the Python interface to the GPIO port. I actually had something specific in mind, but I was foiled by the most important difference between the Pi and the Arduino – voltage. The Pi runs at 3.3v, while the Arduino (well, most Arduinos) uses 5 volts. A bunch of the components I’ve got can use either voltage, but some are finicky that way. As always, check your datasheets before hooking things up!
Of course, this is one of those problems with a built-in solution – buy more components! – so once I’ve got what I need I’ll get back to my original idea. In the meantime, I’ve been working through the basics of using the port from Python. This is repeating some of the basic things I’ve talked about before, just with a Raspberry Pi in place of an Arduino.
I started with a pair of Python libraries that had to be built and installed: RPi.GPIO (for general purpose IO like blinking LEDs) and py-spidev (for hardware SPI support).
I had a few problems getting the libraries built, partly because of the version split in the Python community. Python 3 isn’t completely backward-compatible with Python 2, and the community as a whole has been kind of slow to migrate. The result is that Raspbian on the Pi includes installs of both versions 2 and 3.
For each of these libraries, you have to run a Python script that compiles the C++ portions and installs them with a command line like “sudo python setup.py install”. Py-spidev failed to build under Python 3 because of some compiler errors from GCC which I haven’t had time to track down. Both libraries failed to build with Python 2 (the default on Raspbian) because of a missing Python.h file. It turns out that’s because only the Python 3 development package (“python3.2-dev”) was installed. After a quick “sudo apt-get install python2.7-dev”, I was in business.
I started with the RPi.GPIO library, which is a very simple library for managing the general-purpose pins on the port. It lets you set them as inputs or outputs, turn on the built-in pullup resistors, and get or set the values – all the basic stuff you’re familiar with if you’ve done some Arduino programming. There’s also some nifty stuff like waiting for an edge trigger, or setting a callback function on an input change.
Trivial GPIO test. One button, one LED. And a resistor, a capacitor, and a bunch of wires.
I set up a basic test with one pushbutton used to toggle the state of one LED. Here’s the code for a simple version of the program:
import RPi.GPIO as GPIO
# LED on pin 16
GPIO.setup (16, GPIO.OUT)
# pushbutton on pin 12
GPIO.setup (12, GPIO.IN, pull_up_down=GPIO.PUD_UP)
val = False
val = not val
GPIO.add_event_detect (12, GPIO.FALLING, callback=simple_callback)
You’ll note there’s no debouncing of the pushbutton. I played with some timer-based ways of doing software debouncing, but in the end I just added a 10 μF capacitor to the positive side of the button to take care of the problem.
Really, I was more eager to try out the SPI library. For the hardware, I went back to an old favorite: a pair of 74HC595 shift registers and a bunch of LEDs. Now, this is technically cheating. The 595’s interface isn’t exactly SPI – there isn’t an explicit chip enable, for one thing – but if you connect the two input clocks (shift register clock and storage register clock) together to the SPI clock line, it’s close enough for a trivial test.
I ran into a Linux configuration issue getting the py-spidev code to work. As with the RPi.GPIO stuff, you have to run the Python script as root (or via sudo) so it can access the GPIO port. Even then, I hit an error that didn’t make a lot of sense to me:
Traceback (most recent call last):
File "gpio-spi1.py", line 5, in <module>
IOError: [Errno 2] No such file or directory
No such file or directory? No such file or directory as what? I had to Google for the solution to that riddle. It turns out that py-spidev uses a Linux kernel module to access the CPU’s SPI hardware. And it turns out that on Raspbian, that driver is blacklisted so that it won’t be loaded by default.
I suppose this is probably an efficiency thing, since many users won’t need the SPI module – it’s better to save RAM and not load it unless it’s needed. If you do need it, it’s like, “Gee, thanks. That was helpful.”
If this happens to you, the thing you need to do is edit the file “/etc/modprobe.d/raspi-blacklist.conf” and comment out (with a ‘#’ character) the entry for “spi-bcm2708”. If you’re planning to do some i²c stuff later on, you might want to comment out its driver as well.
After removing that module from the blacklist, I still had to load it with “sudo modprobe -a spi-bcm2708”. Happily, after rebooting the Pi I haven’t had to redo that step – the module’s just been loaded automatically ever since. I guess my Linux hackery has gotten a little rusty, so it’s good to brush up.
I couldn’t find much by way of examples for using py-spidev. It uses Python’s built-in documentation system, but even there the description is pretty minimal. Luckily, it’s not hugely complicated, and it was pretty easy to come up with a program that just switches all the LEDs on and off:
spi = spidev.SpiDev()
spi.writebytes([0,0,0]) # turn all lights off
spi.writebytes([1,255,254]) # turn all lights on
Python lists sure do make it easy to specify a set of bytes to send. I was using two 74HC595s and 16 LEDs, so you might ask why I’m sending three bytes of data out the SPI port. It has to do with the way the 595’s clocks work. I’ve got all the clocks hooked together, but when you do that the data in the storage register (which controls the output pins) is one clock cycle behind the shift register (where the new data is stored). You need to tick the clock one more time to move the correct data to the storage register. To control 16 pins, you need to send 17 bits of data (though it doesn’t actually matter if the last bit is 0 or 1).
Most SPI libraries prefer to send data in bytes. An easy way to reconcile this is to just shift all the data one bit to the left, making room for that extra clock bit on the right, and adding an extra byte at the start to store the carry. It looks something like this:
# sends data to one or more 74hc595 shift registers
# connected in serial. Adds an extra clock cycle
# at the end so all data gets moved from the shift
# register to the storage register.
def sendbytes (data):
carry = 0;
for x in range(len(data)-1,-1,-1):
data[x] = (data[x]<<1) + carry
carry = 0
data[x] -= 256
carry = 1
# add a new first element
And with that I’ve got a bunch of happy blinkenlights attached to my Raspberry Pi, flashing in whatever pattern I want.