3.24. コードでのI2C

machine.I2C はハードウェアI2Cコントローラーをラップします。バスIDと、任意のクロック周波数を指定して生成します。

from machine import I2C

i2c = I2C(2, freq=400_000)

id はどのハードウェアI2Cブロックを使うかを選択します。利用可能な番号と、それらが割り当てられるSDA / SCLピンはボードによって異なります(OpenMV ボード を参照)。freq はSCLクロックレートです。100_000 は標準モード、400_000 はほとんどのセンサーがサポートする一般的な "ファスト" 速度です。

3.24.1. バスの走査

scan() は7ビットのアドレス空間を走査し、自分のアドレスにACKを返したすべてのデバイスのリストを返します。

>>> i2c.scan()
[0x40, 0x68]

これは新しいバスを配線した後に最初に試すことです。デバイスがリストにあれば、配線とプルアップは問題ありません。リストが空(または予想より短い)場合は、コードを疑う前に、プルアップ、配線、あるいはデバイスのアドレスピンを疑ってください。

3.24.2. メモリマップ型のパターン

ほとんどのI2Cセンサーは、その状態を一連の内部レジスタとして公開します。あるアドレスにステータスフラグ、別のアドレスに測定バイト、3つ目のアドレスに設定、というように。ライブラリには、"デバイスをアドレス指定し、レジスタを指し示し、データを読み書きする" というパターンに直接対応する2つの便利なメソッドがあります。

レジスタの読み出し:

addr = 0x68
reg  = 0x3B
data = i2c.readfrom_mem(addr, reg, 6)
# `data` is 6 bytes starting at register 0x3B

この1回の呼び出しで、STARTを発行し、書き込みビット付きでデバイスアドレスを送り、レジスタアドレスを送り(リピーテッドスタート)、読み出しビット付きでアドレスを再送し、6バイトを読み出し、最後のバイトにNACKを返し、STOPします。デバイスは標準的な "レジスタからの読み出し" シーケンスを見ますが、スクリプトには1回のPython呼び出しに見えます。

レジスタへの書き込み:

i2c.writeto_mem(addr, 0x6B, b"\x00")

そのシーケンスは鏡像で同じです。START、アドレス + 書き込み、レジスタ、ペイロードバイト、STOP。

レジスタアドレスが2バイト幅のデバイス(一部のEEPROMや16ビットアドレスのメモリ)では addrsize=16 を渡します。

data = i2c.readfrom_mem(addr, 0x0100, 32, addrsize=16)

3.24.3. センサーの読み出し

典型的なMEMSセンサー上でWHO_AM_I識別子と加速度センサーのX軸を読み出す短いループ:

import time
import struct
from machine import I2C

i2c         = I2C(2, freq=400_000)
ADDR        = 0x68
WHO_AM_I    = 0x75
ACCEL_X_HI  = 0x3B

print("WHO_AM_I:", i2c.readfrom_mem(ADDR, WHO_AM_I, 1)[0])

while True:
    raw = i2c.readfrom_mem(ADDR, ACCEL_X_HI, 2)
    x   = struct.unpack(">h", raw)[0]
    print("accel X:", x)
    time.sleep_ms(100)

readfrom_mem()bytes オブジェクトを返すため、WHO_AM_Iの読み出しは [0] で最初の(そして唯一の)バイトをインデックス指定します。加速度センサーの読み出しは2つの生バイトであり、1つの符号付き16ビット値に組み立てる必要があります。struct のフォーマット ">h" はこれをビッグエンディアンの符号付きとして行います。これはほとんどのI2Cセンサーが使うバイト順で、UARTリンクで一般的なリトルエンディアンの慣例とは逆です。struct.unpack() は常にタプル(フォーマット文字1つにつき1要素)を返すため、[0] で単一の値を取り出します。

3.24.4. より低レベルのアクセス

レジスタマップ型のパターンに従わないデバイス向けに、アドレスフェーズとデータ方向を直接制御できる2つの低レベルメソッドがあります。

i2c.writeto(addr, b"\x01\x02\x03")    # write 3 bytes to addr
data = i2c.readfrom(addr, 4)          # read 4 bytes from addr

これらは、レジスタマップの便利なヘルパーがうまく当てはまらない、コマンドストリーム型のインターフェースを持つデバイス(ディスプレイや一部のリアルタイムクロック)に役立ちます。

3.24.5. ハードウェアブロックが利用できない場合のビットバンギング

machine.SoftI2C は任意のGPIOピン上で同じAPIを提供し、ソフトウェアでプロトコルをビットバンギングします。

from machine import SoftI2C, Pin

i2c = SoftI2C(scl=Pin("P5"), sda=Pin("P4"), freq=100_000)

デバイスをハードウェアI2Cブロックに配線されていないピン上に置く必要がある場合や、すべてのハードウェアブロックが使用中の場合に使います。ビットバンギングのループはハードウェアI2Cより遅く、トランザクション全体の間CPUがビジー状態のままになりますが、APIは I2C と同一なので、既存のコードはコンストラクタを変更するだけで切り替えられます。