I enjoy Elixir programming for my Raspberry Pis using Nerves IoT framework. This is my study note about controlling a seven-segment LED with a shift register through SPI interface from my Nerves-powered Raspberry Pi.
When I was doing experiments with a character LCD display with serial interface, I learned many boards like Adafruit i2c/SPI LCD backpack has shift registers or I/O expanders built in so that we do not do the tedius work of physically wiring many I/O pins. All I need was just connect a few wires for i2c/SPI.
Today, I want to wire an 8-bit shift register by myself. I use one digit seven-segment LED display, which has eight LEDs internally. From my Raspberry Pi, I will control the seven-segment display though the SPI interface.
Seven-segment LED display
According to Wikipedia:
There are two types of seven-segment LED display:
- Common-Cathode
- Common-Anode
We need to use a resistor for GND or VCC in accordance with the forward current value in the product's data sheet just like we would for basic LEDs.
Common-Cathode (CC) display
Common-Anode (CA) display
SN74HC595
SN74HC595 is an 8-bit shift register, which can be used as a serial-to-parallel converter to send signals to the display. From our controller device, we serially send eight bits of data through one signal pin over to the shift register. Then the shift register can output it from eight pins at once.
Pin | Description |
---|---|
QA-QH | 8-bit parallel data output pins |
QH' | serial output that can be connected to SER of another 74HC595 |
SRCLR (Shift Register Clear) | negative logic pin; should be kept at HIGH. |
SRCLK (Shift Register Clock) | For each clock pulse, data in the shift register moves by one bit. |
RCLK (Register Clock) | For each clock pulse. data in the shift register moves into memory register. |
OE (Output Enable Input) | negative logic pin; should be kept at LOW. |
SER (Serial Data Input) | The data is entered serially through this pin, one bit a time. |
The Arduino website a nice tutorial on 74HC595 Shift Register.
SPI
According to Wikipedia:
I use Circuits.SPI library, which allows me to send a byte at a time
To me, the advantage of using SPI for a shift register is that SPI abstracts away the connection between the controller device and the shift register. All I need is just to send a byte using SPI library. Without SPI, I would have to write code to send one bit after another and then send a signal to trigger the output for each iteration.
Demo
Hardware
- Raspberry Pi
- Breadboard
- LED display seven-segment red common cathode
- Resistor (220Ω)
- 8-Bit Shift Register 74HC595
- Jumper wires
Software
- Nerves - IoT framework
- Circuits.SPI - Communicate over SPI from Elixir
Pinouts
74HC595 | Raspberry Pi | Seven-segment display |
---|---|---|
QB (Output) | - | B |
QC (Output) | - | C |
QD (Output) | - | D |
QE (Output) | - | E |
QF (Output) | - | F |
QG (Output) | - | G |
QH (Output) | - | DP |
GND (Ground) | GND (Ground) | - |
QH' (Output) | - | - |
SRCLR (Shift Register Clear Input) | 3.3V | - |
SRCLK (Serial Clock Input) | SPI SCLK (Clock) | - |
RCLK (Register Clock Input) | SPI CE (Chip Enable) | - |
OE (Output Enable Input) | GND (Ground) | - |
SER (Serial Data Input) | SPI COPI (Controller Out Peripheral In) | - |
QA (Output) | - | A |
VCC (Power) | 3.3V | - |
Programming
The seven-segment display has seven segments plus dot, a total of eight LEDs. We will specify which segment we want to be on/off by sending a byte (8 bits). Depending on the display type, a high bit (1) can mean "on" or "off" state. In my case, since I use a common-cathode display, a high bit represents the "on" state. Assuming the pin assignment is as stated earlier, the following list maps numbers to eight bits for a common-cathode display.
shape | DpGFEDCBA | hex |
---|---|---|
0 | 00111111 |
0x3f |
1 | 00000110 |
0x06 |
2 | 01011011 |
0x5b |
3 | 01001111 |
0x4f |
4 | 01100110 |
0x66 |
5 | 01101101 |
0x6d |
6 | 01111101 |
0x7d |
7 | 00000111 |
0x07 |
8 | 01111111 |
0x7f |
9 | 01101111 |
0x6f |
A | 01110111 |
0x77 |
B | 01111100 |
0x7c |
C | 00111001 |
0x39 |
D | 01011110 |
0x5e |
E | 01111001 |
0x79 |
F | 01110001 |
0x71 |
With a common-anode display, the on/off state above would be inverted. For example, 11000000
would represent the "0" shape.
Here is the Elixir code that I wrote. I ssh into my Raspberry Pi and run that program from the Interactive Elixir shell (iex).
Circuits.SPI.bus_names()
# ["spidev0.0", "spidev0.1"]
{:ok, ref} = Circuits.SPI.open("spidev0.0")
# {:ok, #Reference<0.50134155.268828678.71837>}
# Show "0" on the display.
Circuits.SPI.transfer(ref, <<0x3f>>)
# A function that repeats displaying from "0" to "9".
count_fn = fn(count_fn) ->
digits = [0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f]
digits |> Enum.each(fn x ->
Circuits.SPI.transfer(ref, <<x>>)
Process.sleep(500)
end)
count_fn.(count_fn)
end
# Call the function.
count_fn.(count_fn)
One mistake I made
I forgot to use a 220ohm resistor for the common cathode. I re-wire with a 220ohm resistor without turning off the device, which broke the LED closest to the pin I was working on.
That's it.
Top comments (0)