備忘録　ラズパイ5 ROS2 勉強再開 ② GPIO　その1 libgpiod Lチカ

　ROS2以前の問題で、ラズパイ5で、GPIOをアクセスする方法を探ります。
　なぜって、一つは、Linux自体の変化で、
```
$ echo 21 > /sys/class/gpio/export
```
のような、sysfsを経由したGPIO操作がなくなるのです。
　もひとつの理由は、ラズパイ5で、ラズパイ4までと比べてそうとうハードが変更されて、従来のGPIOを操作するライブラリが使えなくなったことです。

### 環境
- Raspberry Pi 5 8GB
- 追加ボード；NVMe Base for Raspberry Pi 5 (NVMe Base by Pimoroni)
- Crucial クルーシャル P2シリーズ 500GB 3D NAND NVMe PCIe M.2 SSD CT500P2SSD8
- 初期；RaspberryPi OS Desktop 64bit (Debian version: 12 (bookworm) Release date: March 15th 2024)
- 現在；Ubuntu Desktop 24.04LTS(64-bit)
- ROS2；Jazzy

### gpiodが残りそう

　マニュアルです。libgpiodのgpiodです？？？ GPIOをアクセスするライブラリlibgpiodを使うコマンドの総称がgpiodです。

 https://libgpiod-dlang.dpldocs.info/gpiod.html

 　ラズパイ5で、GPIO用コマンド、Cライブラリ、Pythonライブラリのいずれにもに対応しているのがlibgpiodだけらしく、使わざるを得ません。コマンドだけならばpinctrlが対応しているようです。Pythonのライブラリはgpiozeroが対応しているのですが、単にGPIOを操作するには使いにくいです。
  　ラズパイ5では/dev/gpiochip4を使うようです。
  　コマンドには、gpiodetect、gpioinfo、gpioset、gpioget、gpiofind、gpiomonがあります。

https://www.acmesystems.it/gpiod
  
```
$ sudo ls /dev/gpio*
/dev/gpiochip0  /dev/gpiochip2  /dev/gpiochip4  /dev/gpiomem1  /dev/gpiomem3
/dev/gpiochip1  /dev/gpiochip3  /dev/gpiomem0   /dev/gpiomem2  /dev/gpiomem4

$ sudo apt install gpiod

$ gpioinfo gpiochip4
gpiochip4 - 54 lines:
        line   0:     "ID_SDA"       unused   input  active-high
        line   1:     "ID_SCL"       unused   input  active-high
        line   2:      "GPIO2"       unused   input  active-high
        line   3:      "GPIO3"       unused   input  active-high
        line   4:      "GPIO4"       unused   input  active-high
        line   5:      "GPIO5"       unused   input  active-high
        line   6:      "GPIO6"       unused   input  active-high
        line   7:      "GPIO7"   "spi0 CS1"  output   active-low [used]
        line   8:      "GPIO8"   "spi0 CS0"  output   active-low [used]
        line   9:      "GPIO9"       unused   input  active-high
        line  10:     "GPIO10"       unused   input  active-high
        line  11:     "GPIO11"       unused   input  active-high
        line  12:     "GPIO12"       unused   input  active-high
        line  13:     "GPIO13"       unused   input  active-high
        line  14:     "GPIO14"       unused   input  active-high
        line  15:     "GPIO15"       unused   input  active-high
        line  16:     "GPIO16"       unused   input  active-high
        line  17:     "GPIO17"       unused   input  active-high
        line  18:     "GPIO18"       unused   input  active-high
        line  19:     "GPIO19"       unused   input  active-high
        line  20:     "GPIO20"       unused   input  active-high
        line  21:     "GPIO21"       unused   input  active-high
        line  22:     "GPIO22"       unused   input  active-high
        line  23:     "GPIO23"       unused   input  active-high
        line  24:     "GPIO24"       unused   input  active-high
        line  25:     "GPIO25"       unused   input  active-high
        line  26:     "GPIO26"       unused   input  active-high
        line  27:     "GPIO27"       unused   input  active-high
        line  28: "PCIE_RP1_WAKE" unused output active-high
        line  29:   "FAN_TACH"       unused   input  active-high
        line  30:   "HOST_SDA"       unused   input  active-high
        line  31:   "HOST_SCL"       unused   input  active-high
        line  32:  "ETH_RST_N"  "phy-reset"  output   active-low [used]
        line  33:          "-"       unused   input  active-high
        line  34: "CD0_IO0_MICCLK" "cam0_reg" output active-high [used]
        line  35: "CD0_IO0_MICDAT0" unused input active-high
        line  36: "RP1_PCIE_CLKREQ_N" unused input active-high
        line  37:          "-"       unused   input  active-high
        line  38:    "CD0_SDA"       unused   input  active-high
        line  39:    "CD0_SCL"       unused   input  active-high
        line  40:    "CD1_SDA"       unused   input  active-high
        line  41:    "CD1_SCL"       unused   input  active-high
        line  42: "USB_VBUS_EN" unused output active-high
        line  43:   "USB_OC_N"       unused   input  active-high
        line  44: "RP1_STAT_LED" "PWR" output active-low [used]
        line  45:    "FAN_PWM"       unused  output  active-high
        line  46: "CD1_IO0_MICCLK" "cam1_reg" output active-high [used]
        line  47:  "2712_WAKE"       unused   input  active-high
        line  48: "CD1_IO1_MICDAT1" unused input active-high
        line  49: "EN_MAX_USB_CUR" unused output active-high
        line  50:          "-"       unused   input  active-high
        line  51:          "-"       unused   input  active-high
        line  52:          "-"       unused   input  active-high
        line  53:          "-"       unused   input  active-high
```
　GPIOxxは、従来から使われているI/Oピンの呼び名です。このライブラリでは、GPIOxxをlinexxという呼び方をしており、Cのプログラム内ではxxと数値だけで指定するようです。

 <img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407635/ad868c05-0924-b986-73c6-bd0c18447288.png">


　40ピン（GPIO21）と39ピン（GND）に抵抗入りLEDをつなぎます。
```
$ gpioset gpiochip4 21=1
```
　LEDが点灯しました。
``gpioinfo gpiochip4``では、下記のように変化しました。
```
line  21:     "GPIO21"       unused  output  active-high
```
　消灯です。
```
$ gpioset gpiochip4 21=0
```

（参考文献）　http://einstlab.web.fc2.com/RaspberryPi/5B.html

### Cのライブラリ libgpiod-dev
　ドキュメントは、インストールしたら、次のディレクトリに入っています。バージョンは1.6.3でした。
/usr/share/doc/libgpiod-dev/html/index.html

　最新バージョンは2.1.2のようですが、どのOSで実装しているか不明です。1.6.3
に比べて仕様が変化しているように感じます。

https://libgpiod.readthedocs.io/en/latest/modules.html

```
$ sudo apt install libgpiod2 libgpiod-dev libgpiod-doc

$ sudo apt info libgpiod-dev
Package: libgpiod-dev
Version: 1.6.3-1.1build1
```
　Lチカのプログラムです。
```ex401.cpp
#include <stdio.h>
#include <unistd.h>
#include <gpiod.h>
#include <error.h>

int main() {

  struct gpiod_chip *gchip;
  struct gpiod_line *glinein, *glineout;
  int value;

  // GPIOデバイスを開く
  if ((gchip = gpiod_chip_open("/dev/gpiochip4")) == NULL) {
    perror("gpiod_chip_open_lookup");
    return 1;
  }

  // GPIO出力
  // GPIOのピンのハンドラを取得する  ここではGPIO21ピンを
  if ((glineout = gpiod_chip_get_line(gchip, 21)) == NULL) {
    perror("gpiod_chip_get_line 21");
    return 1;
  }

  // GPIOを出力モードに設定する
  if (gpiod_line_request_output(glineout, "LED ON/OFF", 0) != 0) {
    perror("gpiod_line_request_output");
  }

  for (int  i=0; i < 10; i++){
    // GPIOの値を1に設定する
    if ((value = gpiod_line_set_value(glineout, 1)) == -1) {
      perror("gpiod_line_set_value");
    }
    sleep(1);

    // GPIOの値を0に設定する
    if ((value = gpiod_line_set_value(glineout, 0)) == -1) {
      perror("gpiod_line_set_value");
    }
    sleep(1);
  }

  // GPIOデバイスを閉じる
  gpiod_chip_close(gchip);
  return 0;
}
```
　実行します。10回、GPIO21につないだLEDが点滅します。
 ```
$ g++ ex401.cpp -lgpiod
$ ./a.out
```

（参考文献）

https://qiita.com/wancom/items/b041ee7408a87fabf48e#%E3%81%82%E3%81%A8%E3%81%8C%E3%81%8D

　いつものように、エラー処理を省略しました。
 
```ex402.cpp
#include <stdio.h>
#include <unistd.h>
#include <gpiod.h>

int main() {
    struct gpiod_chip *gchip;
    struct gpiod_line *glinein, *glineout;
    // GPIOデバイスを開く
    gchip = gpiod_chip_open("/dev/gpiochip4");
    // GPIOのピンのハンドラを取得  ここではGPIO21
    glineout = gpiod_chip_get_line(gchip, 21);
    // GPIOを出力モードに設定
    gpiod_line_request_output(glineout, "LED ON/OFF", 0);

    for (int  i=0; i < 10; i++){
        gpiod_line_set_value(glineout, 1);  // GPIOの値を1に
        sleep(1);
        gpiod_line_set_value(glineout, 0);  // GPIOの値を0に
        sleep(1);
    }

    gpiod_chip_close(gchip);  // GPIOデバイスを閉じる
    return 0;
}
```

 gpiod_chip_xxがGPIO chip operationsに分類される関数類でopen/closeにかかわります。
 gpiod_line_request_xxが、Line requestsに分類される関数類で、出力、入力の設定をします。
 gpiod_line_set_xxが、Reading & setting line valuesに分類される関数類で、出力、入力の値を設定をします。