ネタ記事です。
プロローグ
ある日、Twitter のタイムラインを眺めると、とあるツイートを発端に I2C くんが誹謗中傷されていた...
(以下は見た順・記憶順です。必ずしも時系列的には正しい順番に並んではいません)。
- I2C を Ethernet ケーブルで引き回すときは SDA と SCL を隣り合わせにせず、間に電源を挟むと良い
- え??基板外に!?
- 某車載デバイスの通信方式が I2C
- しかもそれはセンサ。基板外に大きく出ちゃうよ...
- CAN ではいかんのか??車業界
- VGA や HDMI には I2C が使われている
- これ結構カルチャーショックでした
- 基板内で 1m 引き回したら納入先で動かなかった
- オープンドレインをそんなに引き回したら波形が...
- 謎の I2C リピータ販売中
- いやそんなに引き回すなって...
- そもそも各社で規格がちょっとずつ異なる
- 何その悪夢
...とまあおそらくエンジニアと思われる方々が苦悩を語っておりました。あまりにも多くの方々が苦労話をするもんですから、ジャブを連続で決められて I2C くんはボッコボコでした。かわいそうに。僕は大嫌いです。あれエラー処理とかクソ面倒臭いんだよ。
そこで、普段ならマイコンのペリフェラルを使って実装されるだけに留まってしまう I2C くんを気遣って、マイコンを使わず波形をポチポチ手打ちすることで心のこもった通信をプレゼントしようと言う試みとなっています。
やったこと
ズバリ、プッシュスイッチを使った I2C 波形の手打ちです。作者は I2C の詳しい仕様(General Call とか)には詳しくないので、話半分で御覧ください。
ただこれ、I2C と言ってもデータが双方向に行き来するものは面倒臭いですので、ターゲットはデータ送信だけで済む秋月の I2C キャラクタ LED とします。
I2C通信について簡単に
復習しましょう。
オープンドレイン接続
まずはオープンドレイン接続についてです。I2C は通信線をマスターとスレーブ間の通信で双方向に使用します。とすると、この High/Low 出力を通常の CMOS(push-pull) で行ってしまうと信号の衝突が起こりうるわけです。すなわち、何らかの不具合によってマスターが High、スレーブが Low を出してしまうと、この信号線を介してショートされるわけです。これではまずい。
ということで、以下に示すオープンドレインの登場です。この回路はスイッチング素子とプルアップ抵抗で出来ています。
これが各信号線にあって、通常はスイッチング素子がマスター及びスレーブの中にあります。プルアップ抵抗はマイコン内蔵のものを用いることは出来ますが、通常は外付けします。
ここで仮にマスターとスレーブ両方がスイッチオンを要求したとしましょう。通常であれば出力同士がぶつかるので(必ずしもショートではないが)危険な状態です。すると、オープンドレインの回路構成上、以下のような状態になります。
太矢印のように電流が流れますが、途中に抵抗器があるおかげでショートせずに済みます。いいですね。ワイヤード OR(Wired OR)と言われたりもしますよね。
ただ、この接続法にはデメリットもあって、電流のドライブ能力が弱いため、ノイズ等に弱いと僕は認識しています。電流パスは必ず抵抗を介するため、どうしても分布定数の影響を強く受けそうな感じがしています。
スタート・ストップコンディションとACK
I2C では通信開始および終了の合図として特定の波形を送ります。ちょっと図を作るのが面倒になってきちゃったので言葉で説明しますが、おおむね次のような感じです。
- 通信していない状態: SDA, SCL ともに High としておく
- スタートコンディション: SDA を High -> Low とした状態で SCL を High -> Low にする
- ストップコンディション: SCL を Low -> High とした状態で SDA を Low -> High にする
- SDA の値は SCL が Low の時にしか変更してはならない
最後が地味に忘れがちです。もし SCL が High の状態で SDA をパタパタさせると、スレーブがストップコンディションかな?と誤認します。
コツは波形を間違えたら、即座にストップコンディションを発行することです。こうすることで意図せず通信を防げます。
また、通信が正常に完了したかを示す ACK 信号があります。I2C は MSB から SCL の立ち上がりに従って 1byte(8bit) 送信するごとに、スレーブから ACK を待ちます。もし有効なデータ受信が完了したときは、スレーブは Low を SDA に返します。すなわち、以下の点に注意です。
- まず 8bit 分データを送る
- 送ったら、SCL を Low の状態で SDA を High にし、スレーブに解放する
- SCL を High にすると(9クロック目送信)、SDA に ACK 信号が来ている。SDA が Low のときは ACK、High のときは NAK(データ受信失敗)。NAK のときはストップコンディションを発行しその通信をやり直す。
これで、波形を手打ちした時にちゃんと送受信が出来たかを確認することが可能です。
波形の例
では具体例を示します。以下は例の LCD 液晶にスタートコンディション発行後、スレーブアドレスを送る際のタイミングチャートです。
こんな感じで波形をパタパタさせます。
また、I2C 液晶のデータシートをご覧になれば分かりますが、実際の波形はまず 0x7c というスレーブアドレスを送出し、ACK 取得後に続いてコマンド/データを送信します。一通り終わったら最後にストップコンディションを発行して締めます。
以下が最後のデータ(ここでは 0x01 とする)を送出してストップコンディションを発行する際のタイミングチャートです。
ではやってみよう
上級者の方にとっては退屈な内容だったかもですが、実際の動作を御覧ください。地味に多数の RT & いいねを頂いております。助かる。
TLではI2Cの話題で盛り上がっていたので、秋月キャラクタLCD液晶を手打ちI2Cで制御しました。
表示されている文字は汚いですが、許して下さいください...動画は最後の'k'を打ち込むところです。 pic.twitter.com/p21YIc64Gt
— なんとなく活動記録。 (@report_ntnk) February 17, 2021
...言葉汚くてすみませんねぇ。
また、この動画では最後の最後に間違いがあって、
- ACK を受け取るべきクロックで SDA を解放せず、Low としてしまっている
- ストップコンディションを発行していない
というものがあります。興味のある方はよくご覧になってみてください。
回路図
で、これを実現する回路はどうするかです。やることは単純で以下に示しておきますね。
図中の 10k ohm の抵抗器は LCD 基板内部で実装されています。ですので実際には外付けしていません。
この回路の特徴は以下です。
- スイッチを押すと Low が出力される。逆に話すと High。
- High → Low は一瞬だが、Low → High は RC 回路により、充電時間がかかる
- 時定数は RC = 200msec。
- High のときは完全には 3.3V にならない。
- 動作確認用の LED があるため。
- 多分これ結構ヤバいと思います(正常動作が期待できないという点で)
- でもまあ動いてるからヨシ!
ポイント
着目すべき点について。
うわっ...私のクロック周波数...遅すぎ!?
色々な方から「こんなにクロック遅くても動くんだ」とのコメントを(空中で)頂いております。実際のところ I2C はこんなふざけた劇遅動作のために存在するのではなく、ある程度の速度を持ったクロック周波数のためにあるのだと思います(それはそう)。
それで、僕自身その辺はよくわからないということになるのですが、ここで LCD のデータシートを御覧ください。
なんと、最小の SCL 周波数が DC と書かれているではありませんか!!実はこの奇妙な特性表を思い出して、今回のような試みをしようと考えたのでした。
AQM1608はストップコンディションを待たずして受け付ける
今回使用した秋月 LCD では、デバイス側から ACK を送出すると同時に、受信したデータを反映する動作を行うと分かりました。他のデバイスがどうなのかは知りませんが(だって手打ちする人いませんものね)、今回のデバイスに関してはそうであったと報告しておきます。
最後に
SPI の方が楽です。
ちなみにSPIはもっと簡単です
(使用したICはSPI DACのMCP4922) pic.twitter.com/J2XjpaweAO— なんとなく活動記録。 (@report_ntnk) February 18, 2021
そして...
手動でI2C信号作るとかやはりあの人はマイコンそのものなのでは???
— ぱわぷろ@路頭 (@ss_sholaw) February 18, 2021
は???じゃなくて、ありがとうございます!!!!