RaspberryPi

Raspberry pi pico(ラズピコ)をslave(スレーブ)化し、ラズパイとI2C通信。

ラズピコ(Raspberry Pi Pico)は、SPIコントローラー2系統、I2Cコントローラー2系統、UART2系統、PWM16系統を持つ、小型のマイコンボードです。

I/Oがふんだんに用意されているので、ラズピコ(Raspberry Pi Pico)はマスターとし、複数のデバイスをスレーブとして繋いで制御する用途がメインになります。

実は、ラズピコ(Raspberry Pi Pico)には、スレーブ動作するモードも用意されています

本記事では、ラズピコ(Raspberry Pi Pico)をスレーブとし、ラズパイ(Raspberry Pi)の信号を受けて動作させる方法を紹介します

本記事では、下記の内容を紹介します。

この記事で分かること

本記事では、下記の内容を紹介しています。

  • 必要な部品
  • ラズパイ(Raspberry Pi)とラズピコ(Raspberry Pi Pico)の配線図
  • ラズピコ(Raspberry pi pico)をI2Cスレーブ動作させるMicroPythonのサンプルコード

スポンサーリンク

必要な部品

今回使用した部品を紹介します。

ラズピコ(Raspberry Pi Pico)

Amazonは単価は高いですが、送料無料にできるメリットがあります。

秋月電子通商だと、USBケーブルとピンヘッダがセットになったキットなども販売されています。

ブレッドボード

ラズピコ(Raspberry pi pico)を使用する際に、必要になります。

ジャンパワイヤ

オス-オス、オス-メス、メス-メスがセットになったジャンパワイヤです。

スポンサーリンク

ラズパイ(Raspberry Pi)とラズピコ(Raspberry Pi Pico)の配線図

ラズパイ(Raspberry Pi)とラズピコ(Raspberry Pi Pico)の配線について紹介します。

I2C通信に必要な、SDA、SCLのPINを接続するだけなので、とても簡単です。

ラズパイ(Raspberry Pi)のピン配置

ラズパイのピン配置を載せておきます。

知っている方は読み飛ばしてください。

ラズパイ ピン配置

ラズパイのピン配置

ラズピコ(Raspberry Pi Pico)のピン配置

ラズピコのピン配置を載せておきます。

知っている方は読み飛ばしてください。

ラズピコ(Raspberry Pi Pico)のピン配置

ラズピコ(Raspberry Pi Pico)のピン配置

ラズパイ(Raspberry Pi)とラズピコ(Raspberry Pi Pico)の配線図

ラズパイと、ラズピコの配線図はこちら。

ラズパイ(Raspberry Pi)とラズピコ(Raspberry Pi Pico)の配線図
ラズパイ(Raspberry Pi)とラズピコ(Raspberry Pi Pico)の配線図

I2C通信を行うため、ラズパイとラズピコのSDA同士、SCL同士を接続しています。

ラズピコの入力電圧は 1.8~5.5V であるため、ラズパイから5V電源を供給しています。

ラズピコのGNDも忘れずに接続しましょう。

スポンサーリンク

ラズピコ(Raspberry pi pico)をI2Cスレーブ動作させるサンプルコード

ここからは、ラズピコ(Raspberry pi pico)をスレーブ動作させるための、サンプルコードを紹介していきます。

コード紹介前に、いくつか注意点がありますので、順に説明します。

【注意点1】

ラズピコ(Raspberry pi pico)を使用するためには、

はじめにMicroPythonのUF2ファイルをインストールする必要があります

ラズピコ(Raspberry pi pico)の公式サイトの手順に従い、

UF2ファイルをインストールした後に、以降のコードを保存するようにしてください。

【注意点2】

ラズピコ(Raspberry pi pico)へのコード書き込みは、"Thonny"環境をおすすめします。

"Thonny"は、ラズピコ(Raspberry pi pico)へのコード書き込みを簡単に行えてしまうpythonの統合開発環境です。

pythonの総合開発環境 thonny
pythonの総合開発環境 thonny

Thonnyの使い方に関しては、詳細に解説しているサイトがありますので、探してみてください。

ラズピコ(Raspberry pi pico)に書き込むMicroPythonコード

以下の3つのファイルを用意し、ラズピコ(Raspberry pi pico)に書き込みます

  • i2cSlave.py
  • led.py
  • main.py

Thonnyから、ラズピコに保存されているファイルを確認すると、このように見えます。

ラズピコ(Raspberry Pi Pico)内のファイル
ラズピコ(Raspberry Pi Pico)内のファイル

ラズピコの電源投入と同時にmain.pyが立ち上がり、led.py、i2cSlave.pyをコールする仕組みになっています。

なお、i2cSlave.pyの記述には、こちらのサイトを参考にさせて頂いています。

このコードを作成してくださったdanjperron氏に感謝です。

 --- i2cSlave.py ---

### i2cSlave.py
from machine import mem32,mem8,Pin

class i2c_slave:
    I2C0_BASE = 0x40044000
    I2C1_BASE = 0x40048000
    IO_BANK0_BASE = 0x40014000

    mem_rw = 0x0000
    mem_xor = 0x1000
    mem_set = 0x2000
    mem_clr = 0x3000

    IC_CON = 0 #I2C Control Register
    IC_TAR = 4 #I2C Target Address Register
    IC_SAR = 8 #I2C Slave Address Register

    IC_DATA_CMD = 0x10 #I2C Rx/Tx Data Buffer and Command Register

    IC_RAW_INTR_STAT = 0x34 # I2C Raw Interrupt Status Register

    IC_RX_TL = 0x38 #I2C Receive FIFO Threshold Register
    IC_TX_TL = 0x3C #I2C Transmit FIFO Threshold Register

    IC_CLR_INTR = 0x40 #Clear Combined and Individual Interrupt Register

    IC_CLR_RD_REQ = 0x50
    IC_CLR_TX_ABRT = 0x54

    IC_ENABLE = 0x6c #I2C ENABLE Register
    IC_STATUS = 0x70 #I2C STATUS Register


    def write_reg(self, reg, data, method=0):
        mem32[ self.i2c_base | method | reg] = data

    def set_reg(self, reg, data):
        self.write_reg(reg, data, method=self.mem_set) #methodをsetにしている

    def clr_reg(self, reg, data):
        self.write_reg(reg, data, method=self.mem_clr) #methodをclrにしている

    def __init__(self, i2cID = 0, sda=0, scl=1, slaveAddress=0x41):
        self.scl = scl
        self.sda = sda
        self.slaveAddress = slaveAddress
        self.i2c_ID = i2cID
        if self.i2c_ID == 0:
            self.i2c_base = self.I2C0_BASE
        else:
            self.i2c_base = self.I2C1_BASE

        # 1 Disable DW_apb_i2c
        self.clr_reg(self.IC_ENABLE, 1)

        # 2 set slave address
        # clr bit 0 to 9
        # set slave address
        self.clr_reg(self.IC_SAR, 0x1ff)
        self.set_reg(self.IC_SAR, self.slaveAddress &0x1ff)

        # 3 write IC_CON 7 bit, enable in slave-only
        self.clr_reg(self.IC_CON, 0b01001001)
        # set SDA PIN
        mem32[ self.IO_BANK0_BASE | self.mem_clr | ( 4 + 8 * self.sda) ] = 0x1f
        mem32[ self.IO_BANK0_BASE | self.mem_set | ( 4 + 8 * self.sda) ] = 3
        # set SLA PIN
        mem32[ self.IO_BANK0_BASE | self.mem_clr | ( 4 + 8 * self.scl) ] = 0x1f
        mem32[ self.IO_BANK0_BASE | self.mem_set | ( 4 + 8 * self.scl) ] = 3

        # 4 enable i2c
        self.set_reg(self.IC_ENABLE, 1)

    def anyRead(self):
        status = mem32[ self.i2c_base | self.IC_RAW_INTR_STAT] & 0x20
        if status :
            return True
        return False

    def put(self, data):
        # reset flag
        self.clr_reg(self.IC_CLR_TX_ABRT,1)
        status = mem32[ self.i2c_base | self.IC_CLR_RD_REQ]
        mem32[ self.i2c_base | self.IC_DATA_CMD] = data & 0xff

    def any(self):
        # get IC_STATUS
        status = mem32[ self.i2c_base | self.IC_STATUS]
        # check RFNE receive fifio not empty
        if (status & 8) :
            return True
        return False

    def get(self):
        while not self.any():
            pass
        return mem32[ self.i2c_base | self.IC_DATA_CMD] & 0xff



    if __name__ == "__main__":
        import utime
        from machine import mem32
        from i2cSlave import i2c_slave

        s_i2c = i2c_slave(0,sda=0,scl=1,slaveAddress=0x41)
        counter =1
        try:
            while True:
                if s_i2c.any():
                    print(s_i2c.get())
                if s_i2c.anyRead():
                    counter = counter + 1
                    s_i2c.put(counter & 0xff)

        except KeyboardInterrupt:
            pass

--- led.py --- 

### led.py
import utime
import time
from machine import Pin, Timer

 

led = Pin(25, Pin.OUT) #GP25を出力モードに設定

def led_power_on() :

    for i in range(5) :

        led.value(1)
        time.sleep(0.5)
        led.value(0)
        time.sleep(0.5)
        led.value(1)
        time.sleep(0.5)
        led.value(0)
        time.sleep(1.0)

def led_on() :
        led.value(1)

def led_off() :
        led.value(0)

 --- main.py --- 

### main.py
import utime
import time
from machine import mem32,Pin
import led
from i2cSlave import i2c_slave

### --- check pico power on --- ###
led.led_power_on()

### --- pico connect i2c as slave --- ###
s_i2c = i2c_slave(0,sda=0,scl=1,slaveAddress=0x41)

try:
    while True:
        data = s_i2c.get()
        print(data)

        data_int = int(data)
        for i in range(data_int):
            led.led_on()
            time.sleep(0.5)
            led.led_off()
            time.sleep(0.5)


except KeyboardInterrupt:
    pass

ここまでがラズピコ(Raspberry Pi Pico)に書き込むコードです。

※このコードでラズパイ上は動作していますが、サイト移植時にインデントを手打ちしています。

もしエラーが出た場合は、インデント部分を確認いただければと思います。

Raspberry pi picoの公式ホームページのRP2040 Datasheetに、

picoのスレーブ化に関する記述があります。

興味と技術力のある方は、是非確認してみてください。

ラズピコ(Raspberry pi pico)のI2Cアドレス確認

ファイルを保存しmain.pyが読み込まれると、ラズピコのI2Cアドレスが認識されるようになります

本記事の例では、main.pyでI2Cアドレスを0x41と設定しています。

ラズピコのI2Cアドレスが、ほんとうに0x41に設定されているか確認してみます。

ラズパイのターミナルから下記のコマンドを入力します。

$ sudo i2cdetect -y 1
Raspberry Pi PicoのI2Cアドレス確認
Raspberry Pi PicoのI2Cアドレス確認

ラズピコのI2Cアドレスが、0x41として認識されていることを確認できました。

なお、0x29も認識されていますが、

こちらは筆者が別の用途で確認していたToFセンサになので気にしないでください(笑)。

ラズパイ(Raspberry pi)に書き込むpythonコード

次に、ラズパイ側で、ラズピコとI2C通信を行うためのコードを用意します。

--- pi_pico_i2c_test.py --- 

### pi_pico_i2c_test.py

import time
import smbus

bus = smbus.SMBus(1)

bus.write_byte(0x41,3)  #"3"が送信するデータ

ラズパイ側から、上記コードを実行してやると、

送信データに応じて、ラズピコのLEDが点滅するはずです。

ラズパイ(Raspberry Pi)とラズピコ(Raspberry Pi Pico)がI2C通信している様子

実際に、ラズピコ(Raspberry Pi Pico)がI2C通信でスレーブ動作している動画を紹介します。

スポンサーリンク

まとめ

ラズピコ(Raspberry Pi Pico)をI2C通信でスレーブ動作させる方法を紹介しました。

スレーブ動作できるようになると、ラズピコでできることも広がるので、是非試してみてください。

本記事を最後まで読んでいただき、ありがとうございました。

スポンサーリンク

-RaspberryPi
-