Raspberry PiベアメタルプログラミングでI2C制御

i2c-bm.png

MicroPythonに実装する前のテストとして、Raspberry PiのベアメタルでのI2C操作を試してみました。
今回のコードは以下に置いてあります。

bare_matal_rpi_zero/i2c at master · boochow/bare_matal_rpi_zero

今回いきなりMicroPythonに実装せずに単体でテストプログラムを書いてみたのは、なぜかネット上にあまりベアメタルでのI2Cの例が見当たらなかったためです。
唯一見つかったのは、アセンブラで書かれたこちらのものでした。

ddle/RaspberryPi

これを起点にCで書いてみましたが、簡単には動かず、ロジアナで信号を観察しながらの作業となりました。

I2Cデバイスには、以前動作確認できた実績のあるOLEDディスプレイを使い、サンプルとして汎用性を持たせるためにディスプレイそのものの操作ではなく、I2Cバスをスキャンしてデバイスを探すものを作ってみました。
処理内容としてはRaspbian上でi2cdetectコマンドを使う場合と同じようなものです。

●I2C概要

I2Cの規格を簡単にまとめると以下のようになります。
より詳しい解説は以下が分かりやすいです。NXPによる仕様書兼マニュアルはこちらにあります。

電子工作室

・クロックとデータの2本の信号でバスを構成する。バスにぶら下がるデバイスはマスタとスレーブがある。
・クロックは数百KHz程度までと、それほど高速ではない。
・デバイスは7bitの固定アドレスを持つ。0~7と120~127は予約されている。(色々なI2Cデバイスのアドレスの一覧表
・拡張として10bitのアドレス体系もあり。

・データ(SDA)、クロック(SCL)はプルアップ。SCL=HのときSDA=H→Lで通信開始。
・SCL=HのときSDAを読み取る。送受信は8bit単位。
・最初に送付先アドレスを送る。アドレスが7bitの場合、8bit目が通信の方向を表す。
・8bit目が0なら、続く通信はマスタからスレーブへ送信するデータ。1なら、マスタがスレーブから受信するデータ。
・送信側は、8ビットのデータを送ったあとでSDAをプルアップし制御を受信側へ渡す。9ビット目のクロックで受信側がSDAをLにしたらACK。
・9ビット目のクロックのあと、SCLがL→Hとなる。そのあとでSDAがL→Hになれば通信終了。

0x3Cに送信要求し、ACKが返ってきた場合

i2c-ack.png

0x3Dに送信要求し、応答が無かった場合

i2c-nack.png

●Raspberry PiのI2Cハードウェア

Raspberry PiのI2Cコントローラは「BSC(Broadcom Serial Controller )」という名称になっています。
Peripheral Manualの3章(P.28~)に解説があります。
概要は以下の通りです。

・マスタにしかなれない。速度は400Kbpsまで(デフォルトは100Kbps)。
・ハードウェアとしてはBSC0、BSC1、BSC2の3つが用意されている。BSC2はHDMIで使われているため、ユーザ利用は不可。また、BSC0はID_SC(GPIO0)、ID_SD(GPIO1)に割り当てられており、Raspberry Pi3では内部利用されている。

・利用できるGPIOピン(SDA, SCLの順)は以下の通り。
 BSC0: 0, 1(ALT0)、28, 29(ALT0)、44, 45(ALT1)
 BSC1: 2, 3(ALT0)、44, 45(ALT2)

・以下のレジスタがある:
(制御関係)Control、Status
(通信関係)SlaveAddress、DataLength、FIFO
(通信パラメータ)ClockDivider、DataDelay、ClockStretchTimeOut

●実装

大した処理はしていないので、コードを見てもらえばいいのですが、作成中にひっかかった点としては以下のようなものがありました。

・FIFO ClearとStartはどちらもControlレジスタだが、同時にオンにするとうまく動かない。
・通信開始前にステータスレジスタのビットを適切にクリアしておく。また、readやwriteからエラーで戻る場合も、ステータスレジスタの当該ビットはクリアしておく。
・当然ながらGPIOピンを使用する前にALT0に設定しておかなくてはならない。

とりあえず動くようになったので、次はMicroPythonに組み込んでみます。

コメント