はじめに
Nerves Livebook で Raspberry Pi 用スターター電子工作キットを楽しんでいます
SunFounder Raspberry Pi 用のスターター電子工作キット
これまでの記事
- Nerves Livebook で Raspberry Pi 4 のLチカ
- Nerves Livebook + Delux で簡単Lチカ
- MacBook 上の Livebook から Raspberry Pi 上の Livebook に接続してLチカする
- Nerves Livebook から GPIO 拡張ボードの LED を点滅させる
- Nerves Livebook から RGB LED を制御する(ソフトPWMもやってみる)
- Nerves Livebook から LED 棒グラフを制御する
- Nerves Livebook でボタンとLEDを連動させる
この記事では 7 セグメントディスプレイに文字を表示します
実装したノートブックはこちら
7 セグメントディスプレイ
7本の棒で文字を表現するディスプレイです
実は右下に「.」もあるので LED は 8 個ついています
GPIO のピンを単純に各 LED に繋げれば制御できますが、それだと GPIO のピンをどんどん消費してしまいます
そのため、今回はシフトレジスタとして 74HC595 を使って、3 つのピンだけで 8 つの LED を制御します
シフトレジスタ
シフトレジスタはシリアル伝送とパラレル伝送を変換します
今回の記事では、 74HC595 には順番に信号を送って(シリアル伝送)、そこから 7 セグメントディスプレイには並列に信号を送る(パラレル伝送)ように変換します
┌----┐
- 1 - 0 - 1 - 0 -> | | - 0 ->
| 変 | - 1 ->
| 換 | - 0 ->
| | - 1 ->
└----┘
回路の組み立て
オンライン説明書の通りに回路を組みます
ノートブックの実装
Nerves Livebook を開き、以降のコードを実行していきます
ピンを開く
まず、 GPIO の各ピンを出力用に開きます
{:ok, sdi} = Circuits.GPIO.open("GPIO17", :output)
{:ok, rclk} = Circuits.GPIO.open("GPIO18", :output)
{:ok, srclk} = Circuits.GPIO.open("GPIO27", :output)
-
sdi
: シリアル伝送を受ける -
rclk
: 1 にしたタイミングでsdi
の信号を並列伝送用に設定する -
srclk
: 1 にしたタイミングで設定されていた信号を並列伝送する
2進数をビットに分割する
今回実装する処理の流れは以下のようになっています
- 入力として
00111111
というような2進数を与える - 入力を
0
0
1
1
1
1
1
1
というように各桁に分割する - 各桁(ビット)を 74HC595 に順次送る(
sdi
に各桁の値をセットした後で一瞬rclk
を1
にする) - 全桁送り終わったら一瞬
srclk
を1
にする - 74HC595 から 7 セグメントディスプレイに各桁の値が並列に送られ、
1
が送られた LED だけ光る
そのため、まずは 2 進数を一桁ずつに分割します
もっと良い実装があるかもしれませんが、 Python 実装を翻訳したものになっています
シフト演算やビットの論理積を行うため、 Bitwise をインポートしておきます
import Bitwise
例えば以下のようなコードを実行します
0b10100001 |> inspect(base: :binary)
0b
から始まる値は2進数として解釈されます
inspect(base: :binary) で2進数として表示します(何もしないと10進数に変換して表示されます)
すると、結果は以下のようになります
"0b10100001"
シフト演算子 <<<
を使ってみます
0b10100001 <<< 1 |> inspect(base: :binary)
結果は以下のように右端に 0
が追加され、元のビット列は左にシフトしました
"0b101000010"
また、 0b10100001 <<< 2 |> inspect(base: :binary)
とすれば "0b1010000100"
となり、2桁左にシフトします
シフトしたあと 0b10000000
と論理積を取ってみます
(0b10100001 <<< 0 &&& 0b10000000) |> IO.inspect(base: :binary)
(0b10100001 <<< 1 &&& 0b10000000) |> IO.inspect(base: :binary)
(0b10100001 <<< 2 &&& 0b10000000) |> IO.inspect(base: :binary)
(0b10100001 <<< 3 &&& 0b10000000) |> IO.inspect(base: :binary)
(0b10100001 <<< 4 &&& 0b10000000) |> IO.inspect(base: :binary)
(0b10100001 <<< 5 &&& 0b10000000) |> IO.inspect(base: :binary)
(0b10100001 <<< 6 &&& 0b10000000) |> IO.inspect(base: :binary)
(0b10100001 <<< 7 &&& 0b10000000) |> IO.inspect(base: :binary)
各出力は以下のようになります
0b10000000
0b0
0b10000000
0b0
0b0
0b0
0b0
0b10000000
つまり、 0b10100001
の各桁を左から順に 1
であれば 0b10000000
、 0
であれば 0b0
になるように変換しています
この辺りは @kikuyuta さんの記事を参考にしました
分かりやすく 128 で割ると以下のようになります
data = 0b10100001
for bit <- 0..7 do
div(data <<< bit &&& 0x80, 128)
end
実行結果
[1, 0, 1, 0, 0, 0, 0, 1]
つまり、これによって 0b10100001
を [1, 0, 1, 0, 0, 0, 0, 1]
に分割できます
74HC595 に並列出力を設定する
以下のような関数を定義します
shift_out = fn data, sdi, rclk, srclk ->
for bit <- 0..7 do
bit = data <<< bit &&& 0x80
Circuits.GPIO.write(sdi, bit)
Circuits.GPIO.write(srclk, 1)
Process.sleep(1)
Circuits.GPIO.write(srclk, 0)
end
Circuits.GPIO.write(rclk, 1)
Process.sleep(1)
Circuits.GPIO.write(rclk, 0)
end
data
の各ビットについて、 sdi
に値をセットし、 srclk
を一瞬 1
にして並列伝送用の信号として設定します
全ビット分設定してから rclk
を一瞬 1
にして並列伝送を開始します
以下のコードを実行すると、 7 セグメントディスプレイに 0
が表示されます
shift_out.(0b00111111, sdi, rclk, srclk)
00111111
の各桁が「7セグメントディスプレイのどの LED を光らせるか」に対応しています
16 進数を表示する
16 進数(0〜9とA〜F)の各値を 7 セグメントディスプレイに表示します
0b00111111
の2進数を 0x3F
のように16進数として表現し、それぞれを 0.5 秒間隔で切り替えます
seg_code = [
0x3F,
0x06,
0x5B,
0x4F,
0x66,
0x6D,
0x7D,
0x07,
0x7F,
0x6F,
0x77,
0x7C,
0x39,
0x5E,
0x79,
0x71
]
seg_code
|> Enum.map(fn code ->
shift_out.(code, sdi, rclk, srclk)
Process.sleep(500)
end)
HELLO. を表示する
HELLO. の各文字を表示してみます
L が連続するため、文字の切り替わりが分かるように「何も光らない時間」を 0.2 秒挟むようにしました
elixir_code = [
0b01110110,
0b01111001,
0b00111000,
0b00111000,
0b00111111,
0b10000000
]
elixir_code
|> Enum.map(fn code ->
shift_out.(code, sdi, rclk, srclk)
Process.sleep(500)
shift_out.(0b00000000, sdi, rclk, srclk)
Process.sleep(200)
end)
ピンを閉じる
遊び終わったらピンを閉じておきます
Circuits.GPIO.close(sdi)
Circuits.GPIO.close(rclk)
Circuits.GPIO.close(srclk)
まとめ
Nerves Livebook から 7 セグメントディスプレイに文字を表示できました
シフトレジスタの使い方も分かったので、色々応用ができそうです