はじめに
いままで、PICO/ZERO同士のI²C通信は C言語や、Micropythonでclassを作る例が見られました。しかし、CircuitPythonで標準モジュールi2ctarget
を使った例は見つけられませんでした。
今回はシンプルな マスターとスレーブのコード例 を示します。
ポイント
- スレーブは
i2ctarget.I2CTarget
で簡単に作成可能 - CircuitPython の I²C は デフォルトで 100 kHz(標準モード)
- 数値*.**+改行
\n
区切りで送受信する例 - バッファ管理はCuircuitPythonから監視はできそうにない。高速化は信号品質も含め、別途考慮が必要
マスター側コード(RP2040: I2C マスター)
import board
import busio
import time
import random
# ===== I2C マスター設定 =====
sda_pin = board.GP0
scl_pin = board.GP1
i2c = busio.I2C(scl_pin, sda_pin)
SLAVE_ADDR = 0x42
while not i2c.try_lock():
pass # バスが使えるようになるまで待つ
try:
while True:
# ランダムな数字を生成
value = round(random.uniform(0.5, 1.0), 2)
msg = f"{value}\n" # 改行付き文字列
try:
# スレーブに送信
i2c.writeto(SLAVE_ADDR, msg.encode("utf-8"))
print("送信:", msg.strip())
except OSError:
# スレーブが応答しなかった場合
print("⚠ スレーブ未応答。再試行します…")
time.sleep(0.5) # 0.5秒ごとに送信
finally:
i2c.unlock()
スレーブ側コード(RP2040: I2C スレーブ)
import board
import time
import i2ctarget
# from i2ctarget import I2CTarget
import busio # I2C作成用
# ===== I2C スレーブ設定 =====
SLAVE_ADDR = 0x42
sda_pin = board.GP0
scl_pin = board.GP1
# ===== メイン =====
pot_num = 0
buffer_str = "" # 受信文字列バッファ
with i2ctarget.I2CTarget(scl_pin, sda_pin, (SLAVE_ADDR,)) as device:
# with I2CTarget(scl_pin, sda_pin, (SLAVE_ADDR,)) as device:
while True:
i2c_req = device.request(timeout=0)
if i2c_req: # 1バイトずつ読み込む
if not i2c_req.is_read:
data = i2c_req.read(1) # 1バイトずつ取得
if data:
c = data.decode("utf-8")
if c == "\n": # 改行が来た → 受信完了
try:
pot_value = float(buffer_str)
print("受信した値:", buffer_str)
except Exception as e:
print("受信エラー:", e, repr(buffer_str))
buffer_str = "" # バッファクリア
else:
buffer_str += c # バッファに追加
time.sleep(0.05)
実行すると、マスターがランダム数字*.**を送信し続けるので、スレーブはその数字を受信し続けます。
受信した値: 0.68
受信した値: 0.52
受信した値: 0.92
受信した値: 0.54
受信した値: 0.62
受信した値: 0.96
受信した値: 0.84
- 複数スレーブをぶら下げる場合はプルアップ抵抗や配線長に注意が必要です
- プルアップ抵抗について最初は4.7kΩ程度で試すのが良いようです
- 配置:プルアップはバスのどこか一箇所(たとえばコントローラ側)にまとめる必要があり、既製品の多くのデバイスにはあらかじめプルアップ抵抗がついていますので、複数デバイスをつなぐ場合は1つ以外の抵抗は外したほうが、電流過多・波形乱れの原因を取り除けます
- 静電容量が増える、つまり繋ぐデバイス数が増えるほど、電流や波形の余裕が減るので、強いプルアップ(小さい抵抗)が必要になります
- CircuitPythonはRP2040内部プルアップ抵抗(50~80kΩ)は有効化せず、わざわざ外付け抵抗が必要であり、もし抵抗を付けなければエラーが発生します
- MicropythonではI²C初期化時に内部プルアップを有効化するのとは対照的です
- RP2040(=Raspberry Pi Picoのマイコン)のデータシートには、I²Cバス上のデバイス数は**「バスの最大許容静電容量にのみ制限される(=400 pF)」**と記されています
- 一般的なIDCフラットリボン(28 AWG、0.05"ピッチ)で約 45–55 pF/mが典型的です。デバイス(各PicoのSDA/SCLピン)やコネクタの寄生容量も乗り、CMOS I/Oで数 pF~10 pF/端子が一般的と考えた場合、Pico 10台+コントローラ1台なら、ざっくり 60–120 pF 程度を見積もれそうです
- 正確には、オシロスコープ等で、SDA、SCLピン波形の観察が有効だそうです。WEBには波形の観察波形を掲載している所も見かけます