RaspberryPi2 + CommonLisp(SBCL) で LED をチカチカさせる

# 動機

- Raspberry Pi があるのだからLチカくらいやってみたくなった
- どうせなら便利ライブラリを使わずにやってみたい
- 今後の展開を考えてCommon Lispで作ってみたい


# GPIOを使ったLEDの点灯・消灯


手順は以下の通り。

1. 指定したピンのモードを出力に指定
1. 指定したピンをHIGHに指定で点灯
1. 指定したピンをLOWに指定で消灯

今回はGPIOの17ピン、物理ピンだと11番を使います。

## 回路図

![回路図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13469/3c11fcd3-113e-4c78-12f4-7fa20505075a.png)

抵抗は手元にあった1KΩを使いました
（本当は10mAくらい流すと良いようなので300Ωくらいで良い）



# RaspberryPiのI/Oへのアクセス

いわゆるメモリマップドI/Oなので、然るべきメモリにアクセスできればI/Oにアクセスすることができます。


## Raspberry Pi のメモリ配置


手持ちは Raspberry Pi 2 で、 CPU は BCM2836 です。
BCM2836 は BCM2835 と同じなので、BCM2835のデータシートを参照します。

https://datasheets.raspberrypi.com/bcm2835/bcm2835-peripherals.pdf

![BCM2835 ARM Peripherals](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13469/abfe79c4-75a6-5b76-c6ee-440355fe2572.png)


I/O ペリフェラルは `7E000000` からで、実アドレスの `20000000` にマッピングされているのですが、 BCM2836 は `3F000000` に読み替えが必要とのこと。


### アウトプットモードのON

GPIO 17番ピンをアウトプットモードにセットするには、 `FSEL17` を `001` にセットします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13469/f89254af-59ab-8752-6ffb-553ab00acad1.png)

c で書くとこんな感じになるはず。

```c
uint32_t FSEL17_OUT = 1 << 21;
*(ADDRESS) = FSEL17_OUT;
```

アドレスは `GPFSEL1` で、 `7E000000` は `3F000000` に読み替えなので、 `3F200004` になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13469/1acc5de2-0e90-f523-220a-6769506cf389.png)


Cで書くとこんな感じです。

ちなみに、 `/dev/mem` を読み書きする関係で `root` での実行が必要です。

```c
#define GPIO_BASE 0x3F200000
#define BLOCK_SIZE (4 * 1024)

void gpio17_out() {
    int fd;
    volatile uint32_t * gpio;

    fd = open("/dev/mem", O_RDWR | O_SYNC);

    gpio = (uint32_t *)mmap(0, BLOCK_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO_BASE);

    close(fd);

    *(gpio + 1) = 1 << 21;
}
```

gpio コマンドで実行前後のステータスを確認すると、GPIO17番（BCM17)の MODE が IN から OUT にちゃんと変更されてました。


```plaintext: 実行前
gpio readall
 +-----+-----+---------+------+---+---Pi 2---+---+------+---------+-----+-----+
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
 |     |     |    3.3v |      |   |  1 || 2  |   |      | 5v      |     |     |
 |   2 |   8 |   SDA.1 |   IN | 1 |  3 || 4  |   |      | 5v      |     |     |
 |   3 |   9 |   SCL.1 |   IN | 1 |  5 || 6  |   |      | 0v      |     |     |
 |   4 |   7 | GPIO. 7 |   IN | 1 |  7 || 8  | 1 | ALT0 | TxD     | 15  | 14  |
 |     |     |      0v |      |   |  9 || 10 | 1 | ALT0 | RxD     | 16  | 15  |
 |  17 |   0 | GPIO. 0 |   IN | 0 | 11 || 12 | 0 | IN   | GPIO. 1 | 1   | 18  |
 |  27 |   2 | GPIO. 2 |  OUT | 0 | 13 || 14 |   |      | 0v      |     |     |
 |  22 |   3 | GPIO. 3 |  OUT | 0 | 15 || 16 | 0 | IN   | GPIO. 4 | 4   | 23  |
 |     |     |    3.3v |      |   | 17 || 18 | 0 | IN   | GPIO. 5 | 5   | 24  |
 |  10 |  12 |    MOSI |   IN | 0 | 19 || 20 |   |      | 0v      |     |     |
 |   9 |  13 |    MISO |   IN | 0 | 21 || 22 | 0 | IN   | GPIO. 6 | 6   | 25  |
 |  11 |  14 |    SCLK |   IN | 0 | 23 || 24 | 1 | IN   | CE0     | 10  | 8   |
 |     |     |      0v |      |   | 25 || 26 | 1 | OUT  | CE1     | 11  | 7   |
 |   0 |  30 |   SDA.0 |   IN | 1 | 27 || 28 | 1 | IN   | SCL.0   | 31  | 1   |
 |   5 |  21 | GPIO.21 |   IN | 1 | 29 || 30 |   |      | 0v      |     |     |
 |   6 |  22 | GPIO.22 |   IN | 1 | 31 || 32 | 0 | IN   | GPIO.26 | 26  | 12  |
 |  13 |  23 | GPIO.23 |   IN | 0 | 33 || 34 |   |      | 0v      |     |     |
 |  19 |  24 | GPIO.24 |   IN | 0 | 35 || 36 | 0 | IN   | GPIO.27 | 27  | 16  |
 |  26 |  25 | GPIO.25 |   IN | 0 | 37 || 38 | 0 | IN   | GPIO.28 | 28  | 20  |
 |     |     |      0v |      |   | 39 || 40 | 0 | IN   | GPIO.29 | 29  | 21  |
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
 +-----+-----+---------+------+---+---Pi 2---+---+------+---------+-----+-----+
```


```plaintext: 実行後
gpio readall
 +-----+-----+---------+------+---+---Pi 2---+---+------+---------+-----+-----+
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
 |     |     |    3.3v |      |   |  1 || 2  |   |      | 5v      |     |     |
 |   2 |   8 |   SDA.1 |   IN | 1 |  3 || 4  |   |      | 5v      |     |     |
 |   3 |   9 |   SCL.1 |   IN | 1 |  5 || 6  |   |      | 0v      |     |     |
 |   4 |   7 | GPIO. 7 |   IN | 1 |  7 || 8  | 0 | IN   | TxD     | 15  | 14  |
 |     |     |      0v |      |   |  9 || 10 | 1 | IN   | RxD     | 16  | 15  |
 |  17 |   0 | GPIO. 0 |  OUT | 0 | 11 || 12 | 0 | IN   | GPIO. 1 | 1   | 18  |
 |  27 |   2 | GPIO. 2 |  OUT | 0 | 13 || 14 |   |      | 0v      |     |     |
 |  22 |   3 | GPIO. 3 |  OUT | 0 | 15 || 16 | 0 | IN   | GPIO. 4 | 4   | 23  |
 |     |     |    3.3v |      |   | 17 || 18 | 0 | IN   | GPIO. 5 | 5   | 24  |
 |  10 |  12 |    MOSI |   IN | 0 | 19 || 20 |   |      | 0v      |     |     |
 |   9 |  13 |    MISO |   IN | 0 | 21 || 22 | 0 | IN   | GPIO. 6 | 6   | 25  |
 |  11 |  14 |    SCLK |   IN | 0 | 23 || 24 | 1 | IN   | CE0     | 10  | 8   |
 |     |     |      0v |      |   | 25 || 26 | 1 | OUT  | CE1     | 11  | 7   |
 |   0 |  30 |   SDA.0 |   IN | 1 | 27 || 28 | 1 | IN   | SCL.0   | 31  | 1   |
 |   5 |  21 | GPIO.21 |   IN | 1 | 29 || 30 |   |      | 0v      |     |     |
 |   6 |  22 | GPIO.22 |   IN | 1 | 31 || 32 | 0 | IN   | GPIO.26 | 26  | 12  |
 |  13 |  23 | GPIO.23 |   IN | 0 | 33 || 34 |   |      | 0v      |     |     |
 |  19 |  24 | GPIO.24 |   IN | 0 | 35 || 36 | 0 | IN   | GPIO.27 | 27  | 16  |
 |  26 |  25 | GPIO.25 |   IN | 0 | 37 || 38 | 0 | IN   | GPIO.28 | 28  | 20  |
 |     |     |      0v |      |   | 39 || 40 | 0 | IN   | GPIO.29 | 29  | 21  |
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
 +-----+-----+---------+------+---+---Pi 2---+---+------+---------+-----+-----+
```

### GPIOをHIGHに設定

GPIO 17番ピンをHIGHにするには、 `GPSET0` の `SET17` に `1` をセットします。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13469/d352280a-12f5-7555-0b01-269ec08e119e.png)

cだとこんな感じ。

```c
uint32_t SET17 = 1 << 17;
*(ADDRESS) = SET17;
```

### GPIOをLOWに設定

GPIO 17番ピンをLOWにするには、`GPCLR0` の `CLR17` に `1` をセットします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13469/269b201f-6486-2b49-1c5a-bfa8ca8a426d.png)

cのサンプルは割愛。


# SBCL で書く


## SBCL のインストール

https://www.sbcl.org/platform-table.html

上記のページから Linux の ARMhf 版をダウンロードします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13469/d9fc3744-2772-83be-e1e5-813058233d66.png)


適当なディレクトリで展開して完了です。

## Quicklisp のインストール

SBCL を展開したディレクトリで Quicklisp のコードを落としてきます。

```bash
$ curl -O https://beta.quicklisp.org/quicklisp.lisp
```

そうしたら Quicklisp を読み込みつつ起動します。
`/dev/mem` をアクセスする関係で root で起動します。


```bash
$ sudo ./run-sbcl.sh --load quicklisp.lisp
```

Quicklisp 本体のインストールを行います。

```lisp
(quicklisp-quickstart:install)
```

インストールが完了したら、次回起動時に Quicklisp を自動で読み込むように `.sbclrc` に設定を追記します。

```lisp
(ql:add-to-init-file)
```


## 実装


mmap ライブラリは以下のものを使用するので、 Quicklisp で落としておきます。

https://github.com/Shinmera/mmap

```lisp
(ql:quickload :mmap)
```


コードです。

```lisp
(let ((gpio (mmap:mmap #P"/dev/mem" :open '(:read :write :data-sync)
                                    :protection '(:read :write)
                                    :mmap '(:shared)
                                    :size (* 4 1024)
                                    :offset #x3F200000))
      (gpfsel1 1)
      (gpset0 7)
      (gpclr0 10)
      (gpfsel17-out (ash 1 21))
      (set17 (ash 1 17))
      (clr17 (ash 1 17)))
  ;; GPFSEL1 を OUT にセット
  (setf (cffi:mem-aref gpio :uint32 gpfsel1) gpfsel17-out)
  (loop repeat 10
        do
           ;; 点灯
           (setf (cffi:mem-aref gpio :uint32 gpset0) set17)
           (sleep 1)
           ;; 消灯
           (setf (cffi:mem-aref gpio :uint32 gpclr0) clr17)
           (sleep 1)))
```

実行結果です。

![led.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13469/33ea44ef-0839-f689-b504-5eb692afec6c.gif)

（暗いのでわかりにくいですが1秒おきに点滅してます）


# 参考

https://github.com/WiringPi/WiringPi

RaspberryPi で GPIO のアクセスを簡単にできるようにするライブラリ


https://datasheets.raspberrypi.com/bcm2835/bcm2835-peripherals.pdf

BCM2835のデータシート。Raspberry Pi 2のCPU BCM2836 もこれを見ればOK。


https://www.ei.tohoku.ac.jp/xkozima/lab/raspTutorial3.html

Raspberry Pi 2 で GPIO のアドレスが違っているなんてここを見るまで気付きませんでした。