はじめに
ラズベリーパイ(Raspberry Pi / Raspberry Pi Zero) のGPIO、I2C、SPIをPythonで制御する方法をまとめる。PythonのライブラリにはGPIO(RPi.GPIO)、I2C(smbus2)、SPI(spidev)等あるが、それぞれ使い分けが必要となる。本記事では、GPIO、I2C、SPI全てが一つにまとまっているpigpioで制御する。
RaspberryPi Picoの場合はmicropythonなので、別記事の下記参照。
目次
- Raspberry Piのピン配置
- ペリフェラル通信の設定
- pigpio
- GPIO制御基本
- GPIOの割り込み
- I2C制御
- SPI制御
- おまけ1:UART制御
- おまけ2:bytearray型の扱いについて
Raspberry Piのピン配置
5Vと3.3Vの電源出力を備えている。GPIOは3.3V。GPIOはシリアル通信のI2C/SPI/UARTを兼ねている。ピン配置は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 |
ペリフェラル通信の設定
[設定]→[Raspberry Piの設定]→[インターフェース]でSPIとI2Cとを有効にする。シリアルポートはUARTのこと。
pigpio
pigpioはRaspberryPiでGPIO/I2C/SPIの制御ができるPythonライブラリ。RaspberryPiにデフォルトでインストールされている。使用する場合は、sudo pigpiod
を打つ必要がある。起動時に自動起動させたい場合はsystemctl enable。解除したい場合はdisable。
systemctl enable pigpiod
GPIO制御基本
pigpioでGPIO制御するためのAPIは下記の通り。基本のGPIO制御を列挙しているがPWMの制御もできる。
設定内容 | API | パラメータ |
---|---|---|
初期化 | pi=pigpio.pi() | |
IN/OUT設定 | pi.set_mode(gpio,mode) | gpio: GPIO番号 mode: pigpio.INPUT pigpio.OUTPUT |
プルアップ・ダウン | pi.set_pull_up_down(gpio,pud) | gpio: GPIO番号 pud: pigpio.PUD_UP pigpio.PUD_DOWN pigpio.PUD_OFF |
GPIO状態確認 | ret=pi.get_mode(gpio) | gpio: GPIO番号 ret: 0:IN/1:OUT |
Write | pi.write(gpio,level) | gpio: GPIO番号 level: 0:low/1:high |
Read | level=pi.read(gpio) | gpio: GPIO番号 level: 0:low/1:high |
GPIO17とGPIO27を線で直結、GPIO5をOpenにしている状態で動作確認。
import pigpio
pi = pigpio.pi()
#GPIO17をOUT設定
pi.set_mode(17, pigpio.OUTPUT)
#GPIO27をIN設定
pi.set_mode(27, pigpio.INPUT)
#GPIOのモード確認
print(f'# {pi.get_mode(17)=}/{pi.get_mode(27)=}')
# pi.get_mode(17)=1/pi.get_mode(27)=0
#GPIO17=Low
pi.write(17,0)
print(f'# {pi.read(27)=}')
# pi.read(27)=0
#GPIO17=High
pi.write(17,1)
print(f'# {pi.read(27)=}')
# pi.read(27)=1
#GPIO5 PullUp
pi.set_mode(5, pigpio.INPUT)
pi.set_pull_up_down(5,pigpio.PUD_UP)
print(f'# {pi.read(5)=}')
# pi.read(5)=1
GPIOの割り込み
pigpioは入力信号のエッジ割り込みによって、コールバック関数を呼ぶこともできる。
設定内容 | API | パラメータ |
---|---|---|
初期化 | pi=pigpio.pi() | |
コールバック設定 | pi.callback(gpio, edge, cb_func) | gpio: GPIO番号 edge: pigpio.FALLING_EDGE(↓) pigpio.RISING_EDGE(↑) pigpio.EITHER_EDGE((↓↑)) cb_func: コールバック関数 |
コールバック関数の型は
def cb_func(gpio, level, tick):
#gpio : gpio番号
#level: 0:low_edge/1:high_edge
#tick : bootからの時間 usec 72minでオーバフロー
GPIO17とGPIO27を線で直結、GPIO17を1秒毎にHigh/Lowさせ、GPIO27に1秒毎に割り込みを発生させたときの例。
import pigpio
import time
#GPIO27のコールバック関数
def cb_gpio27(gpio, level, tick):
print (f'# {gpio=} : {level=} : {tick/(1000*1000)=}s')
pi = pigpio.pi()
#GPIO27をINでPullUp設定。両エッジでコールバック。
pi.set_mode(27, pigpio.INPUT)
pi.set_pull_up_down(27,pigpio.PUD_UP)
cb = pi.callback(27, pigpio.EITHER_EDGE, cb_gpio27)
#GPIO17=Low
#GPIO17をOUT設定
pi.set_mode(17, pigpio.OUTPUT)
pi.write(17,1)
while(1):
#1秒毎にトグル
time.sleep(1)
pi.write(17,0)
time.sleep(1)
pi.write(17,1)
# gpio=27 : level=1 : tick/(1000*1000)=540.658528s
# gpio=27 : level=0 : tick/(1000*1000)=541.659448s
# gpio=27 : level=1 : tick/(1000*1000)=542.660413s
# gpio=27 : level=0 : tick/(1000*1000)=543.661243s
# gpio=27 : level=1 : tick/(1000*1000)=544.662802s
# gpio=27 : level=0 : tick/(1000*1000)=545.664352s
# gpio=27 : level=1 : tick/(1000*1000)=546.665922s
....
I2C制御
I2Cは、組み込みデバイスを制御するときに使う通信手段。通信速度は数100kbps程度。組み込みデバイスのアドレスを指定して通信を行う。アドレスは7bitで、下位1bitでRead(1)、Write(0)を切り替える。I2Cアドレスがわからない場合は下記で確認できる。下記例では0x1Aと0x50のI2Cデバイスが接続している結果。
sudo i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- 1a -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: 50 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
設定内容 | API | パラメータ |
---|---|---|
初期化 | pi=pigpio.pi() | |
I2C Open | handle=pi.i2c_open(ch, addr) | ch:チャネル1に設定 addr:アドレス7bit handle:Openハンドル |
書き込み | pi.i2c_write_device(handle,data) | handle: i2c_openの戻り値 data: bytearray型 |
読み出し | (cnt,data)= pi.i2c_read_device(handle,size) |
handle: i2c_openの戻り値 size: Readバイト数 (cnt,data): 読込データtupple型 |
例として、EEPROM 24FC1025のI2Cアクセス例を示す。
import pigpio
import time
pi = pigpio.pi()
#open adddre=0x50
h = pi.i2c_open(1, 0x50)
#write:offset 0x0000から3byte 0xAA,0xBB,0xCCを書き込む。
pi.i2c_write_device(h, b'\x00\x00\xAA\xBB\xCC')
time.sleep(0.01) #10ms wait EEPROMの書き込み時間用。
#read:offset 0x0000を設定してから3byte読み込む。
pi.i2c_write_device(h, b'\x00\x00')
print(f'# {pi.i2c_read_device(h, 3)=}')
# pi.i2c_read_device(h, 3)=(3, bytearray(b'\xaa\xbb\xcc'))
SPI制御
SPIは、組み込みデバイスを制御するときに使う通信手段。通信速度は数Mbps程度。高速通信が可能。1対1で通信することが多いが、チップセレクトを使えば、デバイスを指定して通信することもできる。SPIは通信なし時の極性(POL)と通信取り込みエッジ(PHA)の組み合わせで4種類のモードがある。
Mode | POL(通信なし時の極性) | PHA(通信取り込みエッジ) |
---|---|---|
0 | 0 | 0 |
1 | 0 | 1 |
2 | 1 | 0 |
3 | 1 | 1 |
SPIのAPIは下記。spi_open()のspi_flagsは、22bitで詳細設定ができるが、今回はModeを設定する下位3bitを使う。詳細は https://abyz.me.uk/rpi/pigpio/python.html 参照。
設定内容 | API | パラメータ |
---|---|---|
SPI Open | spi_open(spi_channel, baud, spi_flags) | spi_channel: CE0(0)/CE1(1)選択 baud: 通信速度bps spi_flags: Mode設定(0/1/2/3) |
書き込み | pi.spi_write(handle, data) | handle: spi_openの戻り値 data: 書込データbytearray型 |
読み込み | (cnt, data) = pi.spi_read(handle, size) |
handle: spi_openの戻り値 size: Readバイト数 (cnt,data): 読込データtupple型 |
読み書き | (cnt, data) = pi.spi_xfer(handle, data) |
handle: spi_openの戻り値 data: 書込データbytearray型 (cnt,data): 読込データtupple型 |
読み書きは、書き込んだByte数分、読み出しができる。
import pigpio
import time
pi = pigpio.pi()
#open CE0 1000000bps Mode=3
h = pi.spi_open(0, 1000000, 3)
#write 3byte 0x40.0x09.0xFF を書き込み
pi.spi_write(h, b'\x40\x09\xFF') # write 3 bytes
#read 3byte
read_data=pi.spi_xfer(h, 3)
print(f'# {read_data=}')
# read_data=(3, bytearray(b'\x00\x00\x00'))
#write/read 3byte 0x41.0x09.0x00 を書き込み 同時に3byte読み出し
read_data=pi.spi_xfer(h, b'\x41\x09\x00')
print(f'# {read_data=}')
# read_data=(3, bytearray(b'\x00\x00\xFF'))
おまけ1:UART制御
ラズベリーパイでUART制御が一筋縄でいかなかったので、下記記事にまとめた。
おまけ2:bytearray型の扱いについて
bytearray⇔HEX文字列の変換例を下記に示す。
#HEX文字列 → bytearray 変換
hex_string='ABCDEF01ABCDEF01ABCDEF01'
binary=bytes.fromhex(hex_string)
print(type(binary),binary)
# <class 'bytes'> b'\xab\xcd\xef\x01\xab\xcd\xef\x01\xab\xcd\xef\x01'
#bytearray → HEX文字列変換
binary=b'\xab\xcd\xef\x01\xab\xcd\xef\x01\xab\xcd\xef\x01'
hex_string=binary.hex()
print(type(hex_string),hex_string)
# <class 'str'> abcdef01abcdef01abcdef01
その他、数値関連の変換は下記にまとめる。参考に。
以上