6
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Raspberry Pi 5 のペリフェラル制御はpigpioが使えない。代わりの手段はないか?

6
Posted at

はじめに

Raspberry Pi5を購入したので、pigoioでGPIOの制御を使おうと思ったが対応していないらしい。pigpioの代わりに下記を使ってみた。使い分けは必要だが、同等のことができることは確認できた。

  • GPIO制御:gpiozero
  • I2C制御:smbus2
  • SPI制御:spidev
  • PWM制御:rpi_hardware_pwm

Raspberry Pi4やZero2であればpigpioを使えるので、下記記事を参照。

目次

Raspberry Piのピン配置

5Vと3.3Vの電源出力を備えている。GPIOは3.3V。GPIOはシリアル通信のI2C/SPI/UART/PWMを兼ねている。ピン配置はRaspberry Pi/Raspberry Pi Zero共通。下図は公式サイトより。

種別 Pin.No GPIO.No
I2C Pin3:SDA
Pin5:SCL
GPIO2
GPIO3
UART(シリアルコンソールと兼用) Pin8:TXD
Pin10:RXD
GPIO14
GPIO15
SPI Pin19:MOSI
Pin21:MISO
Pin23:SCLK
Pin24:CE0(チップセレクト0)
Pin26:CE1(チップセレクト1)
GPIO10
GPIO9
GPIO11
GPIO8
GPIO7
GPIO Pin.11
Pin.13
Pin.15
Pin.29
Pin.31
Pin.37
GPIO17
GPIO27
GPIO22
GPIO5
GPIO6
GPIO26
GPIO Pin.16
Pin.18
Pin.22
Pin.36
GPIO23
GPIO24
GPIO25
GPIO16
PWM Pin.32
Pin.33
PWM0
PWM1

image.png

image.png

戻る

ペリフェラル通信の設定

ペリフェラル通信の設定はsudo raspi-configの「3 Interface Options」で「I4 SPI」「I5 I2C」を<Yes>で設定する。

戻る

GPIO制御

GPIOはgpiozeroを使えばIN/OUT/割り込みができる。まずは、モジュールのインストールとpipを実施する。

sudo apt install python3-gpiozero
pip install gpiozero --break-system-packages

代表的なAPIは下記の通り

API 概要
out = gpiozero.DigitalOutputDevice(pin=27,initial_value=0) GPIO-OUTの設定。pinでGPIOを指定する。initial_valueで初期値を設定。
in = gpiozero.DigitalInputDevice(pin=17,pull_up=True,bounce_time=0.05) GPIO-INの設定。pinでGPIOを指定する。pull_upで内蔵プルアップの設定、bounce_timeでチャタリング除去の時間(sec)を設定できる。
in.when_activated = callback() GPIO-INでHigh検出時にコールするコールバック関数を設定する。
in.when_deactivated = callback() GPIO-INでLow検出時にコールするコールバック関数を設定する。
out.value 1,0を設定するとHigh,Low出力する。
in.value 1=High,0=Lowが読める。

GPIOの17と27を接続して下記を動作させると、0.5秒周期で状態変化がprint()で出力されるコード。

import gpiozero
import time

# PIN27をOUTで設定
pinO_27 = gpiozero.DigitalOutputDevice(pin=27,initial_value=0)
# PIN17をINで設定(プルアップ(pull_up=True)、チャタリング除去を50ms(bounce_time=0.05)で設定
PinI_17 = gpiozero.DigitalInputDevice(pin=17,pull_up=True,bounce_time=0.05)

# PIN17の割り込みコールバック関数
def pin_callback(pin,value):
    print(f'{pin=},{value=}')

#割り込コールバック設定 lamdbaでコールバック関数を汎用化
PinI_17.when_activated = lambda:pin_callback(17,1) # High時にコール
PinI_17.when_deactivated = lambda:pin_callback(17,0) # Low時にコール

while(1):
    pinO_27.value=1
    time.sleep(0.5)

    pinO_27.value=0
    time.sleep(0.5)

pinO_27.close()
PinI_17.close()

戻る

I2C制御

I2Cはsmbus2がpigpioの代わりになる。まずは、モジュールのインストールとpipを実施する。

sudo apt install python3-smbus
pip install smbus2 --break-system-packages

代表的なAPIは下記の通り

API 概要
h = SMBus(1) I2C1のオープン
msg = i2c_msg.write(addr, list(data)) I2C Writeするためのデータをmsgに設定する。dataはbytearray型
h.i2c_rdwr(msg) msgに設定されたデータをI2C Writeする。
msg = i2c_msg.read(addr, length) I2C Readするためのバッファをmsgに設定する。
h.i2c_rdwr(msg) I2C Readしたデータをmsgに詰める。

アドレス0x3CにWrite/Readする例。接続されているI2Cのアドレスはsudo i2cdetect -y 1で確認できる。

from smbus2 import SMBus, i2c_msg

h = SMBus(1)

#i2c write関数化
def i2c_write_device(h, addr, data):
    msg = i2c_msg.write(addr, list(data))
    h.i2c_rdwr(msg)

#i2c read関数化
def i2c_read_device(h, addr, length):
    msg = i2c_msg.read(addr, length)
    h.i2c_rdwr(msg)
    return bytes(msg)

# 0x3Cに 0x00,0xA8,0x3Fを Writeする
i2c_write_device(h, 0x3C, b'\x00\xA8\x3F')

# 0x3Cから3byte Readする
data = i2c_read_device(h, 0x3C, 3)

戻る

SPI制御

SPIはspidevがpigpioの代わりになる。まずは、モジュールのpipを実施する。

pip install spidev --break-system-packages

代表的なAPIは下記の通り

API 概要
SPI=spidev.SpiDev()
SPI.open(0, 0)
初期化
SPI.mode=0 SPIのモード設定0,1,2,3
SPI.max_speed_hz=100000 通信周波数設定Hz単位
rcv_data = SPI.xfer2(send_data) SPIの送受信。send_dataに送信データ列を設定。rcv_dataに受信したデータ列が格納される。
import spidev

# SPI初期化-------------
# bus=0, device=0(/dev/spidev0.0)でモード0で10MHzで初期化
SPI=spidev.SpiDev()
SPI.open(0, 0)
SPI.mode=0
SPI.max_speed_hz=100000

# SPI送受信--------------
#送信データ binnary_array型で設定
send_data=bytes.fromhex('ABCDEF01ABCDEF01ABCDEF01')
#SPI送受信
rcv_data = SPI.xfer2(send_data) 
#binarry列をHEX文字列に変換
print(rcv_data.hex().upper()) 

戻る

PWM制御

rpi_hardware_pwmを使えばHWでPWMができる。まずPWMを使う場合/boot/firmware/config.txtに下記行を追加し、PWM機能を有効にする。RaspberryPi5の場合、ch=2(GPIO18)、ch=3(GPIO19)となる点注意。

  • GPIO12=ch0/GPIO13=ch1の場合
[all]
dtoverlay=pwm-2chan,pin=12,func=4,pin2=13,func2=4
  • GPIO18=ch2/GPIO19=ch3の場合
[all]
dtoverlay=pwm-2chan

rpi_hardware_pwmモジュールをpipで取得する。

pip install rpi_hardware_pwm --break-system-packages

pwm = HardwarePWM()でPWMのチャネルと周波数を設定する。pwm.start()でデューティーを設定し出力開始する。pwm.change_frequency()で周波数の変更、pwm.change_duty_cycleでデューティーの変更ができる。

from rpi_hardware_pwm import HardwarePWM
import time

# GPIO12はPWMチャネル0に対応
pwm = HardwarePWM(pwm_channel=0, hz=48000)  # 1Hz
pwm.start(10)  # デューティ比10%
time.sleep(5)

pwm.change_frequency(10) # 10Hzに変更
time.sleep(5)

pwm.change_duty_cycle(50) #デューティー比50%に変更
time.sleep(5)

pwm.change_frequency(100) # 100Hzに変更
time.sleep(5)

pwm.stop()

rpi_hardware_pwm はDMAと組み合わせてPWMを制御することができない。そのため、pigpio の wave_chain() のように、事前にデューティ比の配列を用意してリアルタイムに切り替えるような使い方ができないのが惜しいところ。

戻る

動作確認

RaspberryPi5とEEPROM(24FC1025)、IOExpander(MPC23S08)、LED、SWを接続して確認してみた。
接続は下記の通り

  • GPIO02:SDA :24FC1025 SDA
  • GPIO03:SCL :24FC1025 SCL
  • GPIO27:GPO :MPC23S08 CS
  • GPIO22:GPO :MPC23S08 RST
  • GPIO10:MOSI:MPC23S08 Rx
  • GPIO09:MSIO:MPC23S08 Tx
  • GPIO11:SCLK:MPC23S08 CLK
  • GPIO06:GPI :SW
  • GPIO13:PWM1:LED Yellow
  • GPIO26:GPO :LED Blue
  • GPIO12:PWM0:PinHeader

コードは下記の通り、PWM1を定期的にデューティーと周波数を変えながら、SW押下で、LED、24FC1025、MPC23S08にアクセスする。

from smbus2 import SMBus, i2c_msg
import gpiozero
from rpi_hardware_pwm import HardwarePWM
import spidev
import time


# sudo i2cdetect -y 1
#     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
#00:                         -- -- -- -- -- -- -- -- 
#10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
#20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
#30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
#40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
#50: 50 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
#60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
#70: -- -- -- -- -- -- -- -- 

# GPIO02:SDA :24FC1025 SDA
# GPIO03:SCL :24FC1025 SCL
# GPIO27:GPO :MPC23S08 CS
# GPIO22:GPO :MPC23S08 RST
# GPIO10:MOSI:MPC23S08 Rx
# GPIO09:MSIO:MPC23S08 Tx
# GPIO11:SCLK:MPC23S08 CLK
# GPIO06:GPI :SW
# GPIO13:PWM1:LED Yellow
# GPIO26:GPO :LED Blue
# GPIO12:PWM0:PinHeader


def main():
    # SWの割り込みコールバック関数
    def pin_callback(pin,value):
        if(value==0):
            if(LED_B.value == 1):
                LED_B.value = 0
                #I2CWriteアクセス 24FC1025
                i2c_write_device(I2C, 0x50, b'\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A')

                #SPI writeアクセス MPC23S08
                spi_access(SPI, CS, b'\x40\x00\x00') # MPC23S08 OUTモードに設定
                spi_access(SPI, CS, b'\x40\x09\x00') # MPC23S08 Low出力に設定
            else:
                LED_B.value = 1
                #I2CWriteアクセス 24FC1025
                i2c_write_device(I2C, 0x50, b'\x00\x00\x0A\x09\x08\x07\x06\x05\x04\x03\x02\x01')
                
                #SPI writeアクセス MPC23S08
                spi_access(SPI, CS, b'\x40\x00\x00') # MPC23S08 OUTモードに設定
                spi_access(SPI, CS, b'\x40\x09\xFF') # MPC23S08 High出力に設定
        else:
            #I2CReadアクセス 24FC1025
            i2c_write_device(I2C, 0x50, b'\x00\x00')
            rcv_data = i2c_read_device(I2C, 0x50, 10)
            print(f'I2C={rcv_data.hex().upper()}')

            #SPI Readアクセス MPC23S08
            rcv_data=spi_access(SPI, CS, b'\x41\x00\x00') # IN/OUTモードレジスタ
            print(f'SPI={rcv_data.hex().upper()}')
            rcv_data=spi_access(SPI, CS, b'\x41\x09\x00') # 出力状態
            print(f'SPI={rcv_data.hex().upper()}')
            

    #I2C write
    def i2c_write_device(h, addr, data):
        msg = i2c_msg.write(addr, list(data))
        h.i2c_rdwr(msg)
    #I2C Read
    def i2c_read_device(h, addr, length):
        msg = i2c_msg.read(addr, length)
        h.i2c_rdwr(msg)
        return bytes(msg)
    #SPI
    def spi_access(h,cs,send_data):
        cs.value=0
        rcv_data = h.xfer2(send_data)
        cs.value=1
        return bytes(rcv_data)


    try:
        #初期化
        LED_B = gpiozero.DigitalOutputDevice(pin=26,initial_value=0)
        SW    = gpiozero.DigitalInputDevice(pin=6,pull_up=True,bounce_time=0.05)
        PWM_LED_Y = HardwarePWM(pwm_channel=1, hz=1000) 
        CS    = gpiozero.DigitalOutputDevice(pin=27,initial_value=1)
        RST   = gpiozero.DigitalOutputDevice(pin=22,initial_value=0)
        I2C   = SMBus(1)
        SPI   = spidev.SpiDev()
        SPI.open(0, 0) # bus=0, device=0(/dev/spidev0.0)
        SPI.mode = 0 # mode =0
        SPI.max_speed_hz = 100000 # 10MHz
        
        #割り込コールバック設定 lamdbaでコールバック関数を汎用化
        SW.when_activated = lambda:pin_callback(26,0) # Low時にコール
        SW.when_deactivated = lambda:pin_callback(26,1) # High時にコール

        # MPC23S08 RST解除
        RST.value=1

        while(1):
            PWM_LED_Y.start(10)  # デューティ比10%
            time.sleep(2)
            PWM_LED_Y.change_frequency(10) # 10Hzに変更
            time.sleep(2)
            PWM_LED_Y.change_duty_cycle(50) #デューティー比50%に変更
            time.sleep(2)
            PWM_LED_Y.change_frequency(100) # 100Hzに変更
            time.sleep(2)
    except KeyboardInterrupt:
        #終了処理
        LED_B.close()
        SW.close()
        PWM_LED_Y.stop()
        I2C.close()
        SPI.close()
        RST.close()
        CS.close()
        print('強制終了')

if __name__ == "__main__":
    main()

戻る

6
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?