Nerves Livebook で LED ドットマトリックスにアニメーションを表示する

## はじめに

Nerves Livebook で Raspberry Pi 用スターター電子工作キットを楽しんでいます

[SunFounder Raspberry Pi 用のスターター電子工作キット](https://amzn.asia/d/04vHhGKQ)

これまでの記事

- [Nerves Livebook で Raspberry Pi 4 のLチカ](https://qiita.com/RyoWakabayashi/items/8119260ceeea462d6b7b)
- [Nerves Livebook + Delux で簡単Lチカ](https://qiita.com/RyoWakabayashi/items/281afab89249dbf68cd5)
- [MacBook 上の Livebook から Raspberry Pi 上の Livebook に接続してLチカする](https://qiita.com/RyoWakabayashi/private/844da38ff24efa3b923c)
- [Nerves Livebook から GPIO 拡張ボードの LED を点滅させる](https://qiita.com/RyoWakabayashi/private/bda56ee6987b30857266)
- [Nerves Livebook から RGB LED を制御する（ソフトPWMもやってみる）](https://qiita.com/RyoWakabayashi/private/143fda67a1798bd788b7)
- [Nerves Livebook から LED 棒グラフを制御する](https://qiita.com/RyoWakabayashi/items/a6e8b712388d68bdca44)
- [Nerves Livebook でボタンとLEDを連動させる](https://qiita.com/RyoWakabayashi/items/9bf9475d3c5d65cdfa4e)
- [Nerves Livebook で 7 セグメントディスプレイに文字を表示する](https://qiita.com/RyoWakabayashi/items/d13d86d91c86e816d899)
- [Nerves Livebook で 4 桁 7 セグメントディスプレイに文字を表示する](https://qiita.com/RyoWakabayashi/items/32edd890ade613cb4e37)

この記事では LED ドットマトリックスにアニメーションを表示します

実装したノートブックはこちら

https://github.com/RyoWakabayashi/elixir-learning/blob/main/livebooks/nerves_livebook/led_dot_matrix.livemd

## LED ドットマトリックス

LED　の点（ドット）が 8 * 8 の行列（マトリックス）状に並んでいます

![IMG_1619.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/6304ea72-383a-23b6-a326-ff2c0bc3f28b.jpeg)

16本の足があり、各行、各列に対応しています

## 回路の組み立て

オンライン説明書の通りに回路を組みます

https://docs.sunfounder.com/projects/davinci-kit/ja/latest/1.1.6_led_dot_matrix.html

74HC595 を行と列で二つ使います

## ノートブックの実装

Nerves Livebook を開き、以降のコードを実行していきます

### 準備

ビット演算のために `Bitwise` を読み込みます

```elixir
import Bitwise
```

### ピンを開く

GPIO の各ピンを出力用に開きます

シフトレジスタ 74HC595 に信号を送るためのピンを開きます

```elixir
{:ok, sdi} = Circuits.GPIO.open("GPIO17", :output)
{:ok, rclk} = Circuits.GPIO.open("GPIO18", :output)
{:ok, srclk} = Circuits.GPIO.open("GPIO27", :output)
```

### 関数の定義

前回までと同じ、`shift_out` 関数を定義します

シフトレジスタに指定した信号を順次送ってから並列伝送する関数です

```elixir
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
```

### 指定した行、列を光らせる

列と行に指定した２進数の各桁の値によって、どう光るかが変わります

列に 0 、行に 1 が指定されているドットだけが光ります

```elixir
# 1 行目だけが光る
shift_out.(0b00000000, sdi, rclk, srclk) # 列は 0 が光る
shift_out.(0b00000001, sdi, rclk, srclk) # 行は 1 が光る
```

![IMG_1611.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/bbe3b69c-dd16-d007-3aaf-6b4e79d2cb3b.jpeg)

```elixir
# 2 行目だけが光る
shift_out.(0b00000000, sdi, rclk, srclk) # 列は 0 が光る
shift_out.(0b00000010, sdi, rclk, srclk) # 行は 1 が光る
```

![IMG_1612.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/2075be59-3b7a-ec02-9182-5baa418cecbb.jpeg)

```elixir
# 1 列目だけが光る
shift_out.(0b11111110, sdi, rclk, srclk) # 列は 0 が光る
shift_out.(0b11111111, sdi, rclk, srclk) # 行は 1 が光る
```

![IMG_1613.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/356339fd-e158-1b06-5f96-61cfb756d833.jpeg)

```elixir
# 1 列目と 2列目が光る
shift_out.(0b11111100, sdi, rclk, srclk) # 列は 0 が光る
shift_out.(0b11111111, sdi, rclk, srclk) # 行は 1 が光る
```

![IMG_1614.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/8e543eef-ae29-7cdd-19a2-693141bbde08.jpeg)

```elixir
# 全部光る
shift_out.(0b00000000, sdi, rclk, srclk) # 列は 0 が光る
shift_out.(0b11111111, sdi, rclk, srclk) # 行は 1 が光る
```

![IMG_1615.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/2c42fc79-3857-8d86-a0da-689d942b32e3.jpeg)

```elixir
# 真ん中だけ光る
shift_out.(0b11000011, sdi, rclk, srclk) # 列は 0 が光る
shift_out.(0b00111100, sdi, rclk, srclk) # 行は 1 が光る
```

![IMG_1616.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/cde68d6f-38d8-b5f5-e723-23b1c4cee808.jpeg)

### アニメーションを表示する

順番に光らせることでアニメーションになります

指定した行列を順番に光らせる関数を用意します

```elixir
animate = fn code_row, code_col, sdi, rclk, srclk, sleep ->
  code_row
  |> Enum.zip(code_col)
  |> Enum.map(fn {row, col} ->
    shift_out.(col, sdi, rclk, srclk)
    shift_out.(row, sdi, rclk, srclk)
    Process.sleep(sleep)
  end)
end
```

行を順番に光らせるアニメーションを作ります

```elixir
code_row = [
  0b00000001,
  0b00000010,
  0b00000100,
  0b00001000,
  0b00010000,
  0b00100000,
  0b01000000,
  0b10000000
]

code_col = [
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000
]
```

```elixir
0..5
|> Enum.map(fn _ ->
  animate.(code_row, code_col, sdi, rclk, srclk, 1)
  animate.(code_row |> Enum.reverse(), code_col |> Enum.reverse(), sdi, rclk, srclk, 1)
end)
```

![rows.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/c7411df9-769f-46fb-9772-d140fa78ef59.gif)

列を順番に光らせるアニメーションを作ります


```elixir
code_row = [
  0b11111111,
  0b11111111,
  0b11111111,
  0b11111111,
  0b11111111,
  0b11111111,
  0b11111111,
  0b11111111
]

code_col = [
  0b11111110,
  0b11111101,
  0b11111011,
  0b11110111,
  0b11101111,
  0b11011111,
  0b10111111,
  0b01111111
]
```

```elixir
0..5
|> Enum.map(fn _ ->
  animate.(code_row, code_col, sdi, rclk, srclk, 1)
  animate.(code_row |> Enum.reverse(), code_col |> Enum.reverse(), sdi, rclk, srclk, 1)
end)
```

![cols.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/7c7a8460-34c2-092d-e93c-6daa5932c335.gif)

### ピンを閉じる

遊び終わったらピンを閉じておきます

```elixir
Circuits.GPIO.close(sdi)
Circuits.GPIO.close(rclk)
Circuits.GPIO.close(srclk)
```

## まとめ

LED ドットマトリックスで単純なアニメーションを作ることができました

複雑なものは作れませんが、ちょっとした遊びとしては面白いですね