[Elixir Nerves] Seven-segment LED, shift register and SPI interface
![[Elixir Nerves] Seven-segment LED, shift register and SPI interface](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1612786446212%2F-ojNjNl29.gif&w=3840&q=75)
Search for a command to run...
![[Elixir Nerves] Seven-segment LED, shift register and SPI interface](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1612786446212%2F-ojNjNl29.gif&w=3840&q=75)
I wrote a script called phx-docker-compose-new that can be used instead of mix phx.new to generate a new Phoenix application. Building the development environment for a Phoenix application using Docker Compose is convenient because you can set up not...
この記事について Raspberry Pi、TensorFlow、Pythonのいずれにも詳しくない筆者が、物体検出をやって楽しんだ成果の記録です。 TensorFlow公式の物体検出のサンプルプログラムを実行します。 動作環境 ボード Raspberry Pi 4 Model B OS Raspberry Pi OS (32-bit または 64-bit) デスクトップ環境 カメラ Raspberry Pi カメラモジュール v2 Python Python ...
One day I was trying to upgrade one of my Phoenix 1.5 apps to Phoenix 1.6. In that app, I customize Bootstrap styles using SCSS. I want to talk about what I found and stumbled on when setting up SCSS. TIL All explained in Phoenix 1.6 Asset Managemen...
This is written in Japanese. I might convert it to English later, maybe. はじめに Elixirのテストでモックを用意するときに利用するElixirパッケージとして、moxが人気です。Elixir作者のJosé Valimさんが作ったからということもありますが、ただモックを用意するだけではなくElixirアプリの構成をより良くするためのアイデアにまで言及されているので、教科書のようなものと思っています。 一言でいうと「その場...
TIL Access supports keyword lists (Keyword) and maps (Map) out of the box, but not structs. There is a reason why Elixir does not implement Access behaviour for structs. Perfer data.key for accessing predefined atom keys of map or struct. Access be...
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.
According to Wikipedia:
There are two types of seven-segment LED display:
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.
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.
_KniJBSqQMw.jpg?auto=compress%2Cformat&w=680&h=510&fit=max>)
| 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.
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.
| 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 | - |


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)
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.
