個人情報保護方針 お支払いについて お問い合わせ

Rock Compute Module CM3でHATのSPI/GPIOを使用



Raspberry Piといえば、HAT(GPIO)です。HATにはSPI,I2C,I2S,PWM,GPIO等の拡張機能があり、デフォルトでは未使用になっていますが、設定によって有効にできます。接続する器機に応じて設定を行ってから使用します。ここではSPI接続の電子ペーパーを使って動作確認をしていきます。

raspi-configのインストール
Radxa ボードのデバイス ツリー オーバーレイ
GPIOとSPIへのアクセス
SPI/GPIOをPythonから使用する
DTB(デバイスツリー)の確認
起動時にWIFIを有効にしてVNCを使う


raspi-configのインストール

Raspberry Pi OS (Bullseye)にはHATの設定などが可能なraspi-configがインストールされていますが、Ubuntuには標準でインストールされていません。下記の方法でインストールすることができます。raspi-configはメニュー形式で機能を選択できるアプリで結果は/boot/config.txtに設定されます。

$ sudo echo "deb http://archive.raspberrypi.org/debian/ buster main" >> /etc/apt/sources.list
$ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 7FA3303E
$ sudo apt-get update
$ sudo apt-get install raspi-config


raspi-configはRaspberry Pi 4B/Compute Module 4では正しく動作するはずですが、RockCM3では正しく動作しないようです。/boot/config.txtに記載する項目が異なるからでしょう。CM3には不要なのでアンインストールすることにします。

$ sudo apt-get purge raspi-config
$ sudo apt-get autoremove


参考資料: https://qiita.com/penguinprogrammer/items/a252676a3ce6bd1410da

Radxa ボードのデバイス ツリー オーバーレイ

RK3566/RK3568 Available Overlay (ROCK 3A, ROCK 3B, Radxa CM3)で使用できるオーバーレイは以下の通りです。/boot/config.txtに追加して有効にします。
  • rk3568-can1-m0 [Activates CAN bus 1 M0 pins: CAN1_RX_M0, CAN1_TX_M0]
  • rk3568-can1-m1 [Activates CAN bus 1 M1 pins: CAN1_RX_M1, CAN1_TX_M1]
  • rk3568-i2c2-m0 [Activates I2C bus 2 M0 pins: I2C2_SDA_M0, I2C2_SCL_M0]
  • rk3568-i2c3-m0 [Activates I2C bus 3 M0 pins: I2C3_SDA_M0, I2C3_SCL_M0]
  • rk3568-pwm0-m0 [Activates PWM0_M0]
  • rk3568-pwm0-m1 [Activates PWM0_M1]
  • rk3568-pwm1-m0 [Activates PWM1_M0]
  • rk3568-pwm1-m1 [Activates PWM1_M1(5)]
  • rk3568-pwm2-m0 [Activates PWM2_M0]
  • rk3568-pwm2-m1 [Activates PWM2_M1(3)]
  • rk3568-pwm8-m0-fan [Activates PWM8_M0]
  • rk3568-pwm9-m0 [Activates PWM9_M0]
  • rk3568-pwm12-m0 [Activates PWM12_M0]
  • rk3568-pwm12-m1 [Activates PWM12_M1]
  • rk3568-pwm13-m0 [Activates PWM13_M0]
  • rk3568-pwm13-m1 [Activates PWM13_M1]
  • rk3568-pwm14-m0 [Activates PWM14_M0]
  • rk3568-pwm14-m1 [Activates PWM14_M1]
  • rk3568-pwm15-m0 [Activates PWM15_M0]
  • rk3568-pwm15-m1 [Activates PWM15_M1]
  • rk3568-spi0-m1-cs0-spidev [Activates SPI3 M0 (/dev/spidev0.0) pins: SPI0_CS0_M1(33), SPI0_CLK_M1(5), SPI0_MOSI_M1(3), SPI0_MISO_M1(31)]
  • rk3568-spi1-m1-cs0-spidev [Activates SPI3 M0 (/dev/spidev1.0) pins: SPI1_CS0_M1, SPI1_CLK_M1, SPI1_MOSI_M1, SPI1_MISO_M1]
  • rk3568-spi3-m0-cs0-spidev [Activates SPI3 M0 (/dev/spidev3.0) pins: SPI3_CS0_M0(24), SPI3_CLK_M0(23), SPI3_MOSI_M0(19), SPI3_MISO_M0(21)]
  • rk3568-spi3-m1-cs0-spidev [Activates SPI3 M0 (/dev/spidev3.0) pins: SPI3_CS0_M1(24), SPI3_CLK_M1(23), SPI3_MOSI_M1(19), SPI3_MISO_M1(21)]
  • rk3568-uart0 [Activates serial port 0 (/dev/ttyS0) pins: UART0_RX, UART0_TX]
  • rk3568-uart2-m0 [Activates serial port 2 on M0 (/dev/ttyS3) pins: UART2_RX_M0, UART3_TX_M0]
  • rk3568-uart3-m0 [Activates serial port 3 on M0 (/dev/ttyS3) pins: UART3_RX_M0, UART3_TX_M0]
  • rk3568-uart3-m1 [Activates serial port 3 on M1 (/dev/ttyS3) pins: UART3_RX_M1, UART3_TX_M1]
  • rk3568-uart5-m1 [Activates serial port 5 on M1 (/dev/ttyS5) pins: UART5_RX_M1, UART5_TX_M1]
  • rk3568-uart7-m1 [Activates serial port 7 on M1 (/dev/ttyS7) pins: UART7_RX_M1, UART7_TX_M1]
  • rk3568-uart8-m1 [Activates serial port 8 on M1 (/dev/ttyS8) pins: UART8_RX_M1, UART8_TX_M1]
  • rk3568-uart9-m1 [Activates serial port 9 on M1 (/dev/ttyS9) pins: UART9_RX_M1, UART9_TX_M1]
  • rk3568-w1-gpio
    param_w1_pin=GPIO3_A5 [Activates 1-Wire GPIO master]


GPIO番号 機能4 機能3 機能2 機能1 Pin# Pin# 機能1 機能2 機能3 機能4 GPIO番号
+3.3V 1 2 +5.0V
14 SPI0_MOSI_M0 I2C2_SDA_M0 PWM2_M1 GPIO0_B6 3 4 +5.0V
13 SPI0_CLK_M0 I2C2_SCL_M0 PWM1_M1 GPIO0_B5 5 6 GND
125 I2S1_SDI3_M1 SDMMC2_PWREN_M0 GPIO3_D5 7 8 GPIO0_D1 UART2_TX_M0 25
GND 9 10 GPIO0_D0 UART2_RX_M0 24
23 UART0_CTSn PWM0_M1 GPIO0_C7 11 12 GPIO3_C7 I2S1_SCLK_TX_M1 119
15 PWM0_M0 GPIO0_B7 13 14 GND
19 PWM4 VOP_PWM_M0 GPIO0_C3 15 16 GPIO3_D4 I2S1_SDI2_M1 124
+3.3V 17 18 GPIO3_D3 I2S1_SDI1_M1 123
138 I2S2_SDI_M1 SPI3_MOSI_M0 I2C4_SDA_M0 GPIO4_B2 19 20 GND
136 SPI3_MISO_M0 I2S1_SDO1_M1 GPIO4_B0 21 22 GPIO3_C6 I2S1_MCLK_M1 118
139 I2S2_SDO_M1 SPI3_CLK_M0 I2C4_SCL_M0 GPIO4_B3 23 24 GPIO4_A6 I2S1_SCLK_RX_M1 SPI3_CS0_M0 134
GND 25 26 SARADC_IN3
140 I2C2_SDA_M1 GPIO4_B4 27 28 GPIO4_B5 I2S1_SDO3_M1 I2C2_SCL_M1 141
137 I2S1_SDO2_M1 ISP_PRELIGHT_TRIG GPIO4_B1 29 30 GND
21 SPI0_MISO_M0 PWM6 GPIO0_C5 31 32 GPIO4_C0 PWM11_IR_M1 144
22 SPI0_CS0_M0 PWM7_IR GPIO0_C6 33 34 GND
120 I2S1_LRCK_TX_M1 GPIO3_D0 35 36 GPIO4_A7 I2S1_LRCK_RX_M1 SPI3_CS1_M0 135
18 PWM3_IR GPIO0_C2 37 38 GPIO3_D2 I2S1_SDI0_M1 122
GND 39 40 GPIO3_D1 I2S1_SDO0_M1 121

この情報はOSのバージョンなどで異なる可能性があります、どのオーバーレイが使用できるかは下記のコマンドで確認できます。

$ ls -l /boot/dtbs/$(uname -r)/rockchip/overlay/rk3568*



今回はHATのSPI3_MOSI(19), SPI3_CLK_M0(23),SPI0_MISO(21),SPI0_CS0_M0(24)を有効にしたいので、/boot/config.txtにdtoverlay=rk3568-spi3-m0-cs0-spidevの1行を追加します。ひとつ前のカーネルカーネル4.19.193-58-rockchip用にはspi0/spi1用のdtboもあったのですが、最新のカーネル4.19.193-67-rockchip用ではspi3_m0/m1のみになっています。SPI0/1でなにか問題があったのかもしれません。(注意:rk3568-spi3-m1-cs0-spidevを有効にしたら有線LANが使えなくなりました)

$ ls /boot/dtbs/4.19.193-58-rockchip-*/rockchip/overlay/rk3568-spi*

rk3568-spi0-m1-cs0-spidev.dtbo
rk3568-spi1-m1-cs0-spidev.dtbo
rk3568-spi3-m0-cs0-spidev.dtbo
rk3568-spi3-m1-cs0-spidev.dtbo

$ls /boot/dtbs/4.19.193-67-rockchip-*/rockchip/overlay/rk3568-spi*

rk3568-spi3-m0-cs0-spidev.dtbo
rk3568-spi3-m1-cs0-spidev.dtbo

$ sudo vi /boot/config.txt

dtoverlay=rk3568-spi3-m0-cs0-spidev

$ sudo update_extlinux.sh
$ sudo reboot


再起動したら/dev/spidev3.0があることを確認します。

$ ls /dev/spidev*

/dev/spidev3.0


現在アサインされているGPIO/I2Cピンを確認してみます。す

$ sudo mraa-gpio list
$ sudo mraa-i2c list



参考資料: https://wiki.radxa.com/Device-tree-overlays
参考資料: https://wiki.radxa.com/Rock3/CM3/IO/GPIO
参考資料: https://wiki.radxa.com/Rock3/dev/libmraa-cm3-io

GPIOとSPIへのアクセス

電子ペーパーにはHATコネクタとピン位置が合わなかったときのためにコネクタ経由の単一ピンで接続できるようになっていますが、CM3のSPIに関してはSPI3でピンは合っているようです。あとはDC/RES/BUSYの3つのピンを個別に操作できるようにGPIOに指定する必要があります。

HAT信号名(Zero) HAT信号名(CM3) Pin ケーブル信号名 Pin
3.3V 3.3V 1 VCC(3.3-5V) 1
GND GND 6,9,14,20,25,30,34,39 GND 2
MOSI (GPIO10) SPI3_MOSI_M0 19 DIN 3
SCLK (GPIO11) SPI3_SCLK_M0 23 CLK 4
CE0 (GPIO8) SPI3_CS0_M0
GPIO4_A6(134)
24 CS 5
GPIO 25 GPIO3_C6(118) 22 DC 6
GPIO 17 GPIO0_C7(23) 11 RES 7
GPIO 24 GPIO3_D3(123) 18 BUSY 8
CE1(GPIO7) SARADC_IN3 26 - - -
MISO (GPIO9) SPI3_MISO_M0 21 - - -


1. GPIOへの基本的アクセス方法

基本的な方法としてはGPIOはRaspberryPiとRockCM3で同じように操作ができるようです。ただしGPIOピンの番号は異なります。RaspberryPiでも4Bと3B、zero、nanoなどの間でもピン番号は異なっているようです。GPIOの番号をピン名から計算します。たとえばGPIO4_C0ならば、4*32 + 2*8 (A=0,B=1,C=2...) + 0 = 144になります。GPIO3_C6ならば3*32+2*8+6=118、GPIO0_C7なら0*32+2*8+7=23、GPIO3_D3なら3*32+3*8+3=123です。この番号が操作に必要になります。

GPIOの操作は/sys/class/gpioフォルダ以下にアクセスすることで行います。まずは、/sys/class/gpio/exportにアクセスして23,118,123の3pinに対してアクセスができるようにします。アクセスはスーパーユーザーの権限が必要になります。

$ su root
Password:
$cd /sys/class/gpio
$ls

export gpiochip0 gpiochip128 gpiochip253 gpiochip32 gpiochip64 gpiochip96 unexport

$echo 23 > export
$echo 118 > export
$echo 123 > export
$ls

export gpio118 gpio123 gpio23 gpiochip0 gpiochip128 gpiochip253 gpiochip32 gpiochip64 gpiochip96 unexport



exportに対してGPIO番号を書き込むとgpio[番号]というフォルダができます。このフォルダに対してアクセスすることでGPIOピンを制御できます。まずはピンの入出力方向を設定します。DCとRESを出力ピンに、BUSYを入力ピンに設定します。

$ echo out gpio23/direction

out gpio23/direction

$ echo out gpio118/direction

out gpio118/direction

$ echo in gpio123/direction

in gpio123/direction



ここで、出力ピンを1にするには/sys/class/gpio/gpio[番号]/valueに値を書きます。入力ピンから値を読むには/sys/class/gpio/gpio[番号]/valueを読み出します。

$ echo 1 gpio23/value

1 gpio23/value

$ echo 0 gpio118/value

0 gpio118/value

$ cat gpio123/value

0






2. raspi-gpio

Raspberry Piではこの操作をもう少し簡単なコマンドでできるように、"Wiring Pi"や"raspi-gpio"と呼ばれるコマンドが用意されています。"Wiring Pi"は2019に開発停止になり、bulleseyeからは非推奨になったようで、レポジトリから削除されました。"raspi-gpio"はあるようなので、インストールしてみましたが、CM3では動作しないようです。(/proc/device-tree/soc/rangesがないため)





2. i2cdetect / mraa-gpio / mraa-i2c

I2Cにつながっているものを調べるコマンドとしてi2cdetectがあります。i2cdetect -lでI2Cバスのリストが、sudo i2cdetect -y [バスNo] で接続されているデバイスが表示されます。I2C0には00/2F/51のデバイスが、I2C6はHDMIケーブル経由でモニタが見えているようです。



ほかにもmraa-gpioとmraa-i2cというコマンドもインストールされているのですが、うまく動いていないようです。



Radxaのwikiに従ってインストールし直します。

$ sudo apt-get install -y git build-essential swig3.0 python-dev cmake libjson-c-dev libnode-dev
$ sudo apt-get install -y libc6 libgcc1 libstdc++6 python3 python3-dev
$ sudo apt-get install -y libgtest-dev pkg-config cmake-data
$ git clone -b master https://github.com/radxa/mraa.git
$ cd mraa

ここで、CMakeList.txtを編集して

option (BUILDSWIGNODE "Build swig node modules." ON)
option (USEPYTHON3TESTS "Force tests to run with python3" OFF)

の2行を

option (BUILDSWIGNODE "Build swig node modules." OFF)
option (USEPYTHON3TESTS "Force tests to run with python3" ON)

に書き換えます。そのあとビルドします。

$ mkdir build
$ cd build/
$ cmake ..
$ make
$ sudo make install
$ sudo ldconfig
$ mraa-gpio version

Version v2.1.0-26-gd59936f on Radxa ROCK CM3 IO




mraa-gpio listでHATのピンアサインが、mraa-i2c listでI2Cバスが見れます。



参考資料: https://wiki.radxa.com/Rock3/CM3/raspcm4io/gpio
参考資料: https://qiita.com/ma2shita/items/b11045717333bcd79d15#ピンの指定方法
参考資料: https://wiki.radxa.com/Mraa



SPI/GPIOをPythonから使用する

1.RPi.GPIOを試す

Raspberry PiでPython3からSPIやGPIOをアクセスする場合には以下のようなライブラリをインストールします。

$ sudo apt-get update
$ sudo apt-get install python3-pip
$ sudo apt-get install python3-pil
$ sudo apt-get install python3-numpy
$ sudo pip3 install RPi.GPIO
$ sudo pip3 install spidev


電子ペーパーのサンプルプログラムをダウンロードします。

$ sudo apt-get install git
$ git clone https://github.com/waveshare/e-Paper.git
$ cd e-Paper/RaspberryPi_JetsonNano/


Pythonの実行はpython3 epd_2in13b_V4_test.pyと入力するだけですが、エラーになるようです。

$ cd python/examples/
$ python3 epd_2in13b_V4_test.py
Trackback (most recent call last):
File "epd_2in13b_V4_test.py", line 11, in from waveshare_epd import epd2in13b_V4
File "/home/rock/temp/e-Paper/RaspberryPi_JetsonNano/python/lib/sharewave_epd/epd2in13b_V4.py", line 31, in
from .import epdconfig
File "/home/rock/temp/e-Paper/RaspberryPi_JetsonNano/python/lib/sharewave_epd/epdconfig.py", line 238, in
omplementation = JetsonNano()
File "/home/rock/temp/e-Paper/RaspberryPi_JetsonNano/python/lib/sharewave_epd/epdconfig.py", line 121, in __init__
import Jetson.GPIO = JetsonNano()


epdconfig.pyの238行付近を見てみると

if os.path.exist('/sys/bus/platform/drivers/gpiomem-bcm2835'):
  implementation = RaspberryPi()
elif os.path.exist('/sys/bus/platform/drivers/gpio-x3'):
  implementation = SunrizeX3
else:
  implementation = JetsonNano()


と書かれています。つまり、/sys/bus/platform/driver フォルダ以下にあるファイルを見て、機種判定をして機種依存の処理をしているようですが、RaspberriPiはbcm2835を用いたRaspberryPi1/Pi Zeroに対応しており、RockCM3は想定していないようです。PythonのRPi.GPIO自体はRaspberryPi4Bでも使えるようです。

Model Soc CPU 発売日
Raspberry Pi 1 Model A BCM2835 ARM1176JZF-S 700MHz 2013年2月
Raspberry Pi 1 Model A+ BCM2835 ARM1176JZF-S 700MHz 2014年11月
Raspberry Pi 3 Model A BCM2837B0 Coretex-A53 1.4GHz 2018年11月
Raspberry Pi 1 Model B BCM2835 ARM1176JZF-S 700MHz 2012年2月
Raspberry Pi 1 Model B+ BCM2835 ARM1176JZF-S 700MHz 2014年7月
Raspberry Pi 2 Model B BCM2836 Coretex-A7 900MHz 2014年7月
Raspberry Pi 2 Model B V1.2 BCM2837 Coretex-A53 900MHz 2015年2月
Raspberry Pi 3 Model B BCM2837 Coretex-A53 1.2GMHz 2016年2月
Raspberry Pi 3 Model B+ BCM2837B0 Coretex-A53 1.4GMHz 2018年3月
Raspberry Pi 4 Model B BCM2711 Coretex-A53 1.4GMHz 2018年3月
Raspberry Pi Zero BCM2835 ARM1176JZF-S 1GHz 2015年11月
Raspberry Pi Zero W BCM2835 ARM1176JZF-S 1GHz 2015年11月
Raspberry Pi Zero WH BCM2835 ARM1176JZF-S 1GHz 2015年11月
Raspberry Pi Zero 2W BCM2710A1 Coretex-A53 1GHz 2015年11月


RPi.GPIOを無理やりimplementationするように書き換えてみましたが、RockCM3では実行できないようです。

$ python3 epd_2in13b_V4_test.py
Trackback (most recent call last):
File "epd_2in13b_V4_test.py", line 11, in
from waveshare_epd import epd2in13b_V4
File "/home/rock/temp/e-Paper/RaspberryPi_JetsonNano/python/lib/sharewave_epd/epd2in13b_V4.py", line 31, in
from .import epdconfig
File "/home/rock/temp/e-Paper/RaspberryPi_JetsonNano/python/lib/sharewave_epd/epdconfig.py", line 294, in
omplementation = RockChip()
File "/home/rock/temp/e-Paper/RaspberryPi_JetsonNano/python/lib/sharewave_epd/epdconfig.py", line 48, in __init__
import Rpi.GPIO
File "/usr/local/lib/phthon3.8/dist-packages/RPi/GPIO/__init__.py", line 23, in
RuntimeError: This module can only be run on a Raspberry Pi!

参考資料: https://www.waveshare.com/wiki/2.13inch_e-Paper_HAT_(B)_Manual#Working_With_Raspberry_Pi
参考資料: https://ja.wikipedia.org/wiki/Raspberry_Pi
参考資料: https://tech-and-investment.com/raspberrypi2-1-gpio/
参考資料: https://deviceplus.jp/raspberrypi/raspberrypi-python-rpi-gpio/


2.Peripheryを試す

Rock Pi4用のPythonGPIOライブラリがあるようですので、インストールしてみます。/usr/local/lib/python3.8/dist-packages/フォルダにインストールされます。

$ sudo pip3 install python-periphery

Collecting python-periphery
Downloading python-periphery-2.4.1-py2.py3-none-any.whl (36kB)
Installing collected packages: python-periphery
Successfully installed python-periphery-2.4.1

$ls /usr/local/lib/python3.8/dist-packages/

phriphery




そのままではこのperipheryライブラリは使用されませんので、電子ペーパーのサンプルプログラムを編集してこのperiphery使ってGPIOにアクセスするようにします。pdconfig.pyに下記内容を追加します。peripheryではピン番号ではなく、GPIO番号を使用します。

$ vi e-Paper/RaspberryPi_JetsonNano/python/lib/sharewave_epd/epdconfig.py

class RockChip:
  def __init__(self):
    import spidev
    from periphery import GPIO

    # Pin definition
    self.RST_PIN = 23
    self.DC_PIN = 118
    self.CS_PIN = 134
    self.BUSY_PIN = 123
#   self.POWER_PIN = None

    self.SPI = spidev.SpiDev()
    self.GPIO = GPIO

    self.GPIO_RST = self.GPIO(self.RST_PIN, "out")
    self.GPIO_DC = self.GPIO(self.DC_PIN, "out")
    self.GPIO_CS = self.GPIO(self.CS_PIN, "out")
#   self.GPIO_POWER = self.GPIO(self.POWER_PIN, "out")
    self.GPIO_BUSY = self.GPIO(self.BUSY_PIN, "in")

  def digital_write(self, pin, value):
    if value == 0:
      value = True
    else:
      value = False

    if pin == self.RST_PIN:
      self.GPIO_RST.write(value)
    elif pin == self.DC_PIN:
      self.GPIO_DC.write(value)
    elif pin == self.CS_PIN:
      self.GPIO_CS.write(value)

  def digital_read(self, pin):
    value = False
    if pin == self.BUSY_PIN:
      vaule = self.GPIO_BUSY.read()
    if value == True:
      return 1
    return 0

  def delay_ms(self, delaytime):
    time.sleep(delaytime / 1000.0)

  def spi_writebyte(self, data):
    self.SPI.writebytes(data)

  def spi_writebyte2(self, data):
    self.SPI.writebytes2(data)

  def module_init(self):
#   self.GPIO_POWER.write(True)
#   SPI device, bus = 0, device = 0
    self.SPI.open(3, 0)
    self.SPI.max_speed_hz = 4000000
    self.SPI.mode = 0b00
    return 0

  def module_exit(self):
    logger.debug("spi end")
    self.SPI.close()

    logger.debug("close 5V or 3.3V, Module enters 0 power consumption ...")
    self.GPIO_RST.write(False)
    self.GPIO_DC.write(False)
#   self.GPIO_POWER.write(False)

    self.GPIO_RST.close();
    self.GPIO_DC.close();
    self.GPIO_CS.close();
    self.GPIO_POWER.close();
    self.GPIO_BUSY.close();


peripheryに対応したpdconfig.pyはこちらからダウンロードできます。epdconfig.py.periphery (右クリックしてファイルに保存)

e-Paper/RaspberryPi_JetsonNano/python/python/examples/フォルダに移動して、epd_2in13b_V4_test.pyを実行します

$ cd ../../examples
$ sudo python3 epd_2in14b_V4_test.py



実行はできますが、電子ペーパーにはなにも表示されません。Raspberry Pi Zeroでは表示できています。ほかの方法も試してみます。

参考資料: https://osoyoo.com/2022/07/20/rock-pi-4-gpio-control/

3.RMAAを試す

rmaaでは、pin番号を使用します。HATなら1-40です。mraa-gpio listでピンアサインを確認します。

ピン番号 選択されたピン機能 機能一覧 ePaper ピン番号 選択されたピン機能 機能一覧 ePaper
01 3V3 VCC(3.3-5V) 02 5V
03 SDA2 GPIO I2C 04 5V
05 SCL2 GPIO I2C 06 GND
07 GPIO3_D5 GPIO 08 TXD2 GPIO UART
09 GND 10 RXD2 GPIO UART
11 PWM0_M0 GPIO RES 12 GPIO3_A7 GPIO
13 PWM0_M1 GPIO PWM 14 GND
15 PWM4 GPIO PWM 16 GPIO3_D4 GPIO
17 3V3 18 GPIO3_D3 GPIO BUSY
19 SPI3_MOSI_M GPIO SPI UART DIN 20 GND
21 SPI3_MISO_M GPIO SPI UART 22 GPIO3_C6 GPIO DC
23 SPI1_CLK_M0 GPIO SPI UART CLK 24 SPI3_CS0_M0 GPIO SPI CS
25 GND 26 SARADC AIO
27 SDA2_M1 GPIO I2C 28 SCL2_M1 GPIO I2C
29 SDO2,SPI3_C GPIO I2C SPI 30 GND
31 SDA6,SPI0_M GPIO I2C SPI 32 PWM11_IR_M1 GPIO
33 SPI0_CS0_M0 GPIO SPI 34 GND
35 GPIO3_D0 GPIO 36 SPI3_SC1_M0 GPIO
37 PWM3_IR GPIO 38 GPIO3_D2 GPIO
39 GND 40 GPIO3_D1 GPIO


電子ペーパーのサンプルプログラムを編集してこのmraa使ってGPIOにアクセスするようにします。pdconfig.pyに下記内容を追加します。mraaではピン番号を使用します。

$ vi e-Paper/RaspberryPi_JetsonNano/python/lib/sharewave_epd/epdconfig.py

class Mraa:
  def __init__(self):
    import mraa
    import spidev

    # Pin definition
    self.RST_PIN = 11
    self.DC_PIN = 22
    self.CS_PIN = 24
    self.BUSY_PIN = 18
#   self.POWER_PIN = None

#   self.SPI = mraa.Spi(0)
    self.SPI = spidev.SpiDev()

    self.mraa = mraa

    self.GPIO_RST = mraa.Gpio(self.RST_PIN)
    self.GPIO_DC = mraa.Gpio(self.DC_PIN)
    self.GPIO_CS = mraa.Gpio(self.CS_PIN)
#   self.GPIO_POWER = mraa.Gpio(self.POWER_PIN)
    self.GPIO_BUSY = mraa.Gpio(self.BUSY_PIN)

  def digital_write(self, pin, value):
#   logger.debug(pin)
#   logger.debug(value)

    if pin == self.RST_PIN:
      self.GPIO_RST.write(value)
    elif pin == self.DC_PIN:
      self.GPIO_DC.write(value)
    elif pin == self.CS_PIN:
      self.GPIO_CS.write(value)

  def digital_read(self, pin):
    if pin == self.BUSY_PIN:
      return self.GPIO_BUSY.read()
    return 0

  def delay_ms(self, delaytime):
    time.sleep(delaytime / 1000.0)

  def spi_writebyte(self, data):
#   self.SPI.write(data)
    self.SPI.writebytes(data)

  def spi_writebyte2(self, data):
#   self.SPI.write(data)
    self.SPI.xfer3(data)

  def module_init(self):
    logger.debug("mraa module_init")
    self.GPIO_RST.dir(mraa.DIR_OUT)
    self.GPIO_DC.dir(mraa.DIR_OUT)
    self.GPIO_CS.dir(mraa.DIR_OUT)
#   self.GPIO_POWER.dir(mraa.DIR_OUT)
    self.GPIO_BUSY.dir(mraa.DIR_IN)
    self.GPIO_BUSY.mode(mraa.MODE_PULLUP)

    # SPI device, bus = 0, device = 0
    self.SPI.open(3, 0)
    self.SPI.max_speed_hz = 4000000
    self.SPI.mode = 0b00
#   self.SPI.frequency(4000000)
#   self.SPI.frequency(mraa.SPI_MODE0)
    return 0

  def module_exit(self):
    logger.debug("mraa module_exit")
    self.SPI.close()

    logger.debug("close 5V or 3.3V, Module enters 0 power consumption ...")
    self.GPIO_RST.write(0)
    self.GPIO_DC.write(0)
#   self.GPIO_POWER.write(0)


rmaaに対応したpdconfig.pyはこちらからダウンロードできます。epdconfig.py.mraa (右クリックしてファイルに保存)

e-Paper/RaspberryPi_JetsonNano/python/python/examples/フォルダに移動して、epd_2in13b_V4_test.pyを実行します。これも実行できますが、なにも起こりません。どうやらRSTピン(11)にPWMがアサインされてしまっているのが原因のようです。調べてみると以前差し替えたrk3566-radxa-cm3-io.dtbが元に戻っているようです。apt updateをすると元に戻ってしまうようなので都度パッチを当てます。

CM4 IO boardを使用している場合にはDTBを書き換えます。Radza CM3 IO boardを使用している場合には変更の必要はありません

$ cd /boot/dtbs/$(uname -r)/rockchip/
$ sudo mv rk3566-radxa-cm3-io.dtb rk3566-radxa-cm3-io.dtb.bak
$ sudo cp rk3566-radxa-cm3-rpi-cm4-io.dtb rk3566-radxa-cm3-io.dtb
$ sudo reboot

このあとrrma-gpio listと入力するとNo Pinsになってしまうようです。


どうやらdtb(デバイスツリー)を書き換えると、/proc/cpuinfoの内容が"Radxa CM3 IO"から"Radxa CM3 RPI CM4 IO"に変更になります。これをmraaのrmaa/src/arm/arm.c内で参照しているようです。以下の変更をすることで、dtb変更後にもmraaを使用できるようになりました。

  • mraa/include/arm/radxa_cm3_rpi_cm4_io.hを追加、radxa_cm3_io.hをコピーしファイル内のcm3 を cm3_rpi_cm4 に変更
  • mraa/src/arm/radxa_cm4_rpi_cm4_io.cを追加、radxa_cm3_io.cをコピーしファイル内のcm3 を cm3_rpi_cm4 に変更
  • rmaa/api/mraa/type.hにMARR_RADXA_CM3_RPI_CM4_IOを追加
  • rmaa/src/CMakeLists.txt に${PROJECT_SOURCE_DIR}/src/arc/cm4_rpi_cm4_io.cを追加
  • build以下のファイルを一旦すべて削除してcmakeからビルドし直し、インストール
rk3566-radxa-cm3-rpi-cm4-io.dtbに対応したmraaはこちらからダウンロードできます。mraa_cm3_rpi_cm4.tar.gz (右クリックしてファイルに保存)


結局RSTを11ピンから16ピンに移動することで正常に動作することが確認できました。SPIとGPIOの機能としては正常で、11ピンだけが異常なようです。


参考資料: https://qiita.com/matsujirushi/items/c6842f3c517c4898e28d
参考資料: https://iotdk.intel.com/docs/master/mraa/python/example.html#hello-gpio
参考資料: https://github.com/eclipse/mraa/blob/master/docs/raspberry_pi.md>
参考資料: https://github.com/eclipse/mraa/blob/master/docs/rockpi4.md



DTB(デバイスツリー)の確認

オシロスコープで確認すると11ピンにPWM信号と思われる波形が出ています。


rk3566のデータシートを見ると11ピンはGPIOのほかにPWM0、UART0に切り替えることができるようです


DTBの内容を確認してみます。device-tree-compilerをインストールするとDTBを可読なDTSに戻すことができます。

$ sudo apt-get install device-tree-compiler
$ dtc -I dtb -O dts -o rk3566-radxa-cm3-rpi-cm4-io.dts rk3566-radxa-cm3-rpi-cm4-io.dtb
$ dtc -I dtb -O dts -o rk3566-radxa-cm3.dts rk3566-radxa-cm3-io.dtb
$ dtc -I dtb -O dts -o rk3568-pwm0-m0.dts rk3568-pwm0-m0.dtbo
$ dtc -I dtb -O dts -o rk3568-pwm0-m1.dts rk3568-pwm0-m1.dtbo

CM4I/OBoard用の変更をする前のrk3566-radxa-cm3.dtsはgithubにもあります。https://github.com/torvalds/linux/blob/master/arch/arm64/boot/dts/rockchip/rk3566-radxa-cm3.dtsi
まず、rk3566-radxa-cm3.dtsとrk3566-radxa-cm3-rpi-cm4-io.dtsを比較してみます。

  usbhost {
    compatible = "rockchip,rk3568-dwc3\0rockchip,rk3399-dwc3";
    status = "okay"; => status = "disabled";
    dwc3@fd000000 {
      status = "okay"; => status = "disabled";
  i2c@fdd40000 {
    compatible = "rockchip,rk3399-i2c";
    gc2053-1@3f { => 削除
    gc2093-1@7e { => 削除
  rkcif@fdfe0000 {
    compatible = "rockchip,rk3568-rkisp";
    status = "disabled"; => status = "okay";
  rkisp@fdff0000 {
    compatible = "rockchip,iommu-v2";
    status = "disabled"; => status = "okay";
  rkisp-vir0 {
    compatible = "rockchip,rkisp-vir";
    port { => 削除
  rkisp-vir1 {
    compatible = "rockchip,rkisp-vir";
    port { => 削除
  vop@fe040000 {
    compatible = "rockchip,rk3568-vop";
    ports {
      port@0 {
        endpoint@1 {
          status = "disabled"; => 削除
      port@1 {
        endpoint@1 {
          status = "disabled"; => 削除
  dsi@fe060000 {
    compatible = "rockchip,rk3568-mipi-dsi";
    ports {
    port@1 { => 削除
  dsi@fe070000 {
    compatible = "rockchip,rk3568-mipi-dsi";
    ports {
    port@1 { => 削除
  edp@fe0c0000 {
    compatible = "rockchip,rk3568-edp";
    force-hpd; => 削除
    port@1 { => 削除
  pcie@fe260000 {
    compatible = "rockchip,rk3568-pcie\0snps,dw-pcie";
    status = "disabled"; => status = "okay";
  i2c@fe5b0000 {
    compatible = "rockchip,rk3399-i2c";
    focaltech@38 { => 削除
    gc2053-2@37 { => 削除
  csi2-dphy1 {
    compatible = "rockchip,rk3568-csi2-dphy";
    ports { => 削除
  csi2-dphy2 {
    compatible = "rockchip,rk3568-csi2-dphy";
    ports { => 削除
  pinctrl {
    compatible = "rockchip,rk3568-pinctrl";
    pcfg-pull-down {
      bias-pull-down; => 削除
    pcfg-pull-none-drv-level-15 { => 削除
    pcfg-pull-none-smt { => 削除
    leds {
      sata1-led { => 削除
      sata2-led { => 削除
    lcd-vcc { => 削除
    dsi1-lcd { => 削除
    touch { => 削除
    dsi0-lcd { => 削除
    usb { => 削除
    headphone { => 削除
    ch482d { => 削除
  gpio-leds {
    compatible = "gpio-leds";
    pi-led-green {
      linux,default-trigger = "heartbeat"; => linux,default-trigger = "timer";
  backlight-edp { => 削除
  vcc3v3-lcd-edp { => 削除
  edp-panel { => 削除
  vcc-lcd-mipi1 { => 削除
  vcc-lcd-tp { => 削除
  dsi1-backlight { => 削除
  vcc-mipi-lcd0 { => 削除
  lcd0-backlight { => 削除
  vcc-avdd { => 削除
  vcc-dovdd { => 削除
  vcc-dvdd { => 削除
  external-camera-clock-24m { => 削除
  vcc5v0-usb30-regulator { => 削除
  tf-det-regulator { => 削除
  __symbols__ {
    gc2053_1 = "/i2c@fdd40000/gc2053-1@3f"; => 削除
    gc2053_1_out = "/i2c@fdd40000/gc2053-1@3f/port/endpoint"; => 削除
    gc2093_1 = "/i2c@fdd40000/gc2093-1@7e"; => 削除
    gc2093_1_out = "/i2c@fdd40000/gc2093-1@7e/port/endpoint"; => 削除
    isp0_in = "/rkisp-vir0/port/endpoint@0"; => 削除
    isp1_in = "/rkisp-vir1/port/endpoint@0"; => 削除
    dsi0_out_panel = "/dsi@fe060000/ports/port@1/endpoint"; => 削除
    dsi0_panel = "/dsi@fe060000/panel@0"; => 削除
    disp_timings0 = "/dsi@fe060000/panel@0/display-timings"; => 削除
    lcd0_timing0 = "/dsi@fe060000/panel@0/display-timings/timing0"; => 削除
    panel_in_dsi0 = "/dsi@fe060000/panel@0/ports/port@0/endpoint"; => 削除
    dsi1_out_panel = "/dsi@fe070000/ports/port@1/endpoint"; => 削除
    dsi1_panel = "/dsi@fe070000/panel@0"; => 削除
    display_timings0 = "/dsi@fe070000/panel@0/display-timings"; => 削除
    dsi1_timing = "/dsi@fe070000/panel@0/display-timings/timing0"; => 削除
    panel_in_dsi1 = "/dsi@fe070000/panel@0/ports/port@0/endpoint"; => 削除
    edp_out = "/edp@fe0c0000/ports/port@1"; => 削除
    edp_out_panel = "/edp@fe0c0000/ports/port@1/endpoint@0"; => 削除
    focaltech = "/i2c@fe5b0000/focaltech@38"; => 削除
    gc2053_2 = "/i2c@fe5b0000/gc2053-2@37"; => 削除
    gc2053_2_out = "/i2c@fe5b0000/gc2053-2@37/port/endpoint"; => 削除
    csi2dphy1_ucam0 = "/csi2-dphy1/ports/port@0/endpoint@1"; => 削除
    csi2dphy1_out = "/csi2-dphy1/ports/port@1/endpoint@1"; => 削除
    csi2dphy2_ucam0 = "/csi2-dphy2/ports/port@0/endpoint@1"; => 削除
    csi2dphy2_out = "/csi2-dphy2/ports/port@1/endpoint@1"; => 削除
    pcfg_pull_down = "/pinctrl/pcfg-pull-down"; => 削除
    pcfg_pull_none_drv_level_15 = "/pinctrl/pcfg-pull-none-drv-level-15"; => 削除
    pcfg_output_low = "/pinctrl/pcfg-output-low"; => 削除
    sata1_led = "/pinctrl/leds/sata1-led"; => 削除
    sata2_led = "/pinctrl/leds/sata2-led"; => 削除
    vcc_mipi = "/pinctrl/lcd-vcc/vcc-mipi"; => 削除
    vcc_tp = "/pinctrl/lcd-vcc/vcc-tp"; => 削除
    dsi1_lcd_rst_gpio = "/pinctrl/dsi1-lcd/dsi1-lcd-rst-gpio"; => 削除
    dsi1_backlight_en = "/pinctrl/dsi1-lcd/dsi1-backlight-en"; => 削除
    touch_gpio = "/pinctrl/touch/touch-gpio"; => 削除
    dsi0_lcd_rst_gpio = "/pinctrl/dsi0-lcd/dsi0-lcd-rst-gpio"; => 削除
    lcd0_backlight_drv = "/pinctrl/dsi0-lcd/lcd0-backlight-drv"; => 削除
    vcc5v0_usb30_en = "/pinctrl/usb/vcc5v0-host-en"; => 削除
    hp_det = "/pinctrl/headphone/hp-det"; => 削除
    tf_det_en = "/pinctrl/sdcard/tf-det-en"; => 削除
    ch482d_en1 = "/pinctrl/ch482d/ch482d-en1"; => 削除
    ch482d_en2 = "/pinctrl/ch482d/ch482d-en2"; => 削除
    backlight_edp = "/backlight-edp"; => 削除
    vcc3v3_lcd_edp = "/vcc3v3-lcd-edp"; => 削除
    edp_panel = "/edp-panel"; => 削除
    timing0 = "/edp-panel/display-timings/timing0"; => 削除
    panel_in_edp = "/edp-panel/ports/endpoint"; => 削除
    vcc_lcd_mipi1 = "/vcc-lcd-mipi1"; => 削除
    vcc_lcd_tp = "/vcc-lcd-tp"; => 削除
    dsi1_backlight = "/dsi1-backlight"; => 削除
    vcc_mipi_lcd0 = "/vcc-mipi-lcd0"; => 削除
    lcd0_backlight = "/lcd0-backlight"; => 削除
    vcc_avdd = "/vcc-avdd"; => 削除
    vcc_dovdd = "/vcc-dovdd"; => 削除
    vcc_dvdd = "/vcc-dvdd"; => 削除
    clk_cam_24m = "/external-camera-clock-24m"; => 削除
    hdmi_sound = "/hdmi-sound"; => 削除
    vcc5v0_usb30 = "/vcc5v0-usb30-regulator"; => 削除
    tf_det = "/tf-det-regulator"; => 削除

変更点はrk3568-rkisp、iommu-v2、dw_pcieを有効にし、あとは3つ目のコネクタ経由で接続できない機能を無効にしていると思われます。PWM0に関する差異はなさそうです。次にオーバーレイのほうを見てみます。

  ######## rk3568-pwm0-m0.dts ########

  fragment@0 {
    target = <0xffffffff>;
    __overlay__ {
      status = "okay";
      pinctrl-names = "active";
      pinctrl-0 = <0xffffffff>;
    };
  };
  __fixups__ {
    pwm0 = "/fragment@0:target:0";
    pwm0m0_pins = "/fragment@0/__overlay__:pinctrl-0:0";
  };

  ######## rk3568-pwm0-m1.dts ########

  fragment@0 {
    target = <0xffffffff>;
    __overlay__ {
      status = "okay";
      pinctrl-names = "active";
      pinctrl-0 = <0xffffffff>;
    };
  };
  __fixups__ {
    pwm0 = "/fragment@0:target:0";
    pwm0m1_pins = "/fragment@0/__overlay__:pinctrl-0:0";
  };

pwm0の中のpwm0m*_pinsのstatusを"okey"に、pinctrl-namesを"active"に、pinctrl-0を<0xffffffff>に変更するという意味に読み取れます。もともとのpinctrl-0は<0x41>でphandleの内容(実態はpinctrl/pwm0/pwm0m0-pins)と思われるので、<0xffffffff>がそのまま入るのではなく、該当するphandle値に置き換えられるのかもしれません。さらに、rk3566-radxa-cm3-rpi-cm4-io.dts の中でこのオーバーレイに関係する箇所を抜き出してみます。

  pcfg-pull-none {
    bias-disable;
    phandle = <0xf6>;
  };

  pwm@fdd70000 {
    compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm";
    reg = <0x00 0xfdd70000 0x00 0x10>;
    #pwm-cells = <0x03>;
    pinctrl-names = "active";
    pinctrl-0 = <0x41>;
    clocks = <0x30 0x0d 0x30 0x30>;
    clock-names = "pwm\0pclk";
    status = "disabled";
    phandle = <0x144>;
  };

  pinctrl {
    compatible = "rockchip,rk3568-pinctrl";
    rockchip,grf = <0x31>;
    rockchip,pmu = <0x32>;
    #address-cells = <0x02>;
    #size-cells = <0x02>;
    ranges;
    phandle = <0xf5>;
    (中略)
    pwm0 {
      pwm0m0-pins {
        rockchip,pins = <0x00 0x0f 0x01 0xf6>; (GPIO番号15=0x0f)
        phandle = <0x41>;
      };

      pwm0m1-pins {
        rockchip,pins = <0x00 0x17 0x02 0xf6>; (GPIO番号23=0x17)
        phandle = <0x22d>;
      };
    };
  };

  __symbols__ {
    (中略)
    pwm0 = "/pwm@fdd70000";
    (中略)
    pwm0m0_pins = "/pinctrl/pwm0/pwm0m0-pins";
    pwm0m1_pins = "/pinctrl/pwm0/pwm0m1-pins";
  };

rk3566-radxa-cm3-rpi-cm4-io.dtsでは、

    pinctrl-names = "active";
    pinctrl-0 = <0x41>;
    status = "disabled";

となっているところを、オーバーレイで

    pinctrl-names = "active";
    pinctrl-0 = <0x41>;
    status = "okey";

に変更している。ここでrk3566-radxa-cm3-rpi-cm4-io.dtsにおいて

    pinctrl-names = "default";

になっていないのが気になる。そこで、明示的に"default"にするオーバーレイを作成してみます。違う名前のdtboを作成して/boot化に入れて、sudo update_extlinux.shを実行したら消されてしまい。すべてのdtbファイルが初期値に戻ってしまいました。

  ######## rk3568-pwm0-m1.dts ########
  fragment@0 {
    target = <0xffffffff>;
    __overlay__ {
      status = "disabled";
      pinctrl-names = "default";
      pinctrl-0 = <0xffffffff>;
    };
  };
  __fixups__ {
    pwm0 = "/fragment@0:target:0";
    pwm0m1pins = "/fragment@0/__overlay__:pinctrl-0:0";
  };

dtboファイルに変換してインストールします。

$ dtc -I dts -O dtb -o rk3568-pwm0-m1.dtbo rk3568-pwm0-m1.dts
$ sudo vi /boot/config.txt

dtoverlay=rk3568-pwm0-m1 <= 追加します

$ sudo update_extlinux.sh

<= これを実行するとdtbファイルがすべて初期値に戻ってしまうので、このあとコピーを行います。

$ sudo cp rk3568-pwm0-m1.dtbo /boot/dtbs/$(uname -r)/rockchip/overlays
$ cd
$ sudo mv /boot/dtbs/$(uname -r)/rockchip/rk3566-radxa-cm3-io.dtb /boot/dtbs/$(uname -r)/rockchip/rk3566-radxa-cm3-io.dtb.org
$ sudo cp /boot/dtbs/$(uname -r)/rockchip/rk3566-radxa-cm3-rpi-cm4-io.dtb /boot/dtbs/$(uname -r)/rockchip/rk3566-radxa-cm3-io.dtb
$ sudo reboot


PWMで変調された信号は出なくなりましたが、常にLowレベルでGPIOとしては動作しないようです。結論としては、CM3の11ピンはGPIOとして使えないということになりそうです。

参考資料: https://qiita.com/koara-local/items/ed99a7b96a0ca252fc4e
参考資料: https://qiita.com/eofz/items/20cfa99601756fc98905
参考資料: https://qiita.com/cello_piano_violin/items/8a80caef5d1c316e988e
参考資料: https://www.toradex.com/ja-jp/blog/device-tree-overlay


起動時にWIFIを有効にしてVNCを使う

SPI3_M1を有効にすると有線LANが使用できなくなるようです。(SPI3_M0なら問題は起きません)そうなるとモニタとキーボードを接続すれば操作できますが、VNCが利用できなくて不便です。モニタとキーボードを使用して通常のログインをするとWifiが使えるようになるのですが、それ以前は使えません。そこで起動時にWIFIを接続するように設定を行います。まずは/etc/network/interfacesを編集して下記の記述を追加します。(重複する記述は#でコメントアウトしてください)

$ sudo vi /etc/network/interfaces

auto wlan0
allow-hotplug wlan0
iface wlan0 inet dhcp
wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf


次に/etc/wpa_supplicant/wpa_supplicant.confを編集します

$ sudo vi /etc/wpa_supplicant/wpa_supplicant.conf

network={
ssid="elecom-5G" <= 使用しているWifiのSSIDを指定します
scan_ssid=1
key_mgmt=WPA-PSK
proto=WPA2
pairwise=CCMP TKIP
group=CCMP TKIP
psk="[パスワード]" <= 使用しているWifiのパスワードを指定します

$sudo reboot


参考資料: http://marchan.e5.valueserver.jp/cabin/comp/jbox/arc101/doc10107.html
参考資料: https://www.hiramine.com/physicalcomputing/raspberrypi2/wlan_howto.html


アクセスカウンター アクセスカウンター