公開日:2021/08/14
title.png

17th Season


#17-5 I2Cを使いたいの

 これまでArduino DIYで使ってきた通信仕様には、UARTの他にもSPIやI2Cがありました。特定のシリアル通信仕様で通信する外部センサなどのモジュールが存在するので、Moscoviumマイコンで電子工作するにも、これらの通信ユニットを揃えておきたいと思います。

 SPIは本HPでも度々使ってきたような記憶があったのだけど、改めて見直してみると、手持ちのモジュールではあまり使われていませんでした。使用頻度が高いというイメージがあったのは、SDカードI/Fとしての使用があったからでしょう。

  手持ちのSPI通信モジュール等:

 SDカードはFATを実装しないと実質使えないので除外して、MAC無しで使い難く使用頻度の低いENC28J60も除外すると、SPIで通信するモジュールは8×8 LEDくらいな物でした。しかも、SPIのプロトコルは単純で簡単なので、何ならソフトウェアSPIでも十分成立します。


 問題は、I2C。I2Cのプロトコルは複雑で、ソフトウェアで実装するのはかなり難しいのです。基本的にはI2C通信可能なI/Oユニットが必要となります。

  手持ちのI2Cモジュール等:

I2C通信ユニット

I2C通信仕様

 I2Cの通信仕様はちょっと複雑で難しいのですが、ここから先は「I2Cの通信仕様については理解している」ことを前提として進めます。筆者にも、I2C通信仕様について解説する程の理解力があるかどうか不確かなため、です(1^^;)。

 I2Cの仕様については、以下リンクより検索してみてください。

  I2C通信仕様について検索する(別ウインドウが開きます)

 制定当初はオランダのフィリップス社が提唱した通信仕様ですが、社名が変わってNXPセミコンダクターズ社と、なっています。従って、NXPとI2Cをキーワードに探すと、最新のI2C仕様書が見つかるはずです。

※凄くポピュラな通信仕様故に、何度もアップデートされ、拡張されています。お陰で、最新の仕様書には余計な情報が沢山盛り込まれていて、返って理解し難いと思います。所謂「後方互換性」を維持しているので、最初に読む資料としては、古めの仕様書の方が読み易いかもしれません。


 仕様書を探して読んでみると解りますが、なかなか複雑で難しい仕様です。正直なところ、NXP社のI2C仕様に「準拠」したユニットを設計するのは相当骨が折れると思います。今回の目的は、「NXP社I2C仕様に準拠したユニットを作る」事ではなく、「手持ちのI2C通信に対応したモジュールとの通信を確立したい」だけなので、次節を目標仕様とします。

今回設計するユニットの目標仕様

 I2C通信可能なI/OユニットをMoscovium用に設計します。

 ただし、I2Cの通信仕様は相当複雑なので、恐らく、「完全な準拠」は難しいと思われます。よって、手持ちのモジュールと通信できたらOKとします。
 「なんちゃって」なので、ユニット名は「STWSER(Synchronous Two Wire Serial、同期二線式シリアル通信)ユニット」とします。

 「I2Cではないけれど、I2Cモジュールと通信できるかもよ。通信できたら良いね」ってことです。

 以下、その他の目標仕様をまとめておきます。

  STWSERユニット目標仕様

 ソフトウェア(ドライバ)で、ちゃんとI2Cプロトコルに則った通信を実行すれば、普通にI2Cモジュールと通信することは可能なはず!です。ただし、スペック(電気的特性AC,DC含む)ぎりぎりで通信したり、通信信号線長が長いとか、雑音の多い(ノイジィな)環境だったりすると、通信を失敗するかもしれないのでご注意ください。

 いつもの通り、「サポート無しの無保証」です。

STWSERユニットの設計

 I2Cの仕様その物については何も解説せずに来ているので、細かな設計について議論しても仕方ありません。ですので、設計についてはさらっと流して、使い方(ソフトウェアの作り方)や手持ちI2C機器との通信実験などの方にウェイトを置こうと思います。

 以下図に同期二線式シリアル通信STWSERユニットのブロック図を示します。

a175_stwser.png

※黄色に着色した箱は、CPUからアクセス可能なI/Oレジスタです。

 上図の通り、STWSERユニットの内部には、マスタ通信を司るSTWMSTユニットと、スレーブ通信を司るSTWSLVユニットが完全に独立したユニットとして存在しています。
 特にコンパイルオプション等は用意しませんが、各ユーザが、マスタユニットだけを複数インスタンスしたりしてカスタマイズすることは可能な構成にしてあります。

 以下表にI/Oレジスタ一覧を示し、各レジスタの説明を加えます。

Addr Name R/W Bit:15〜0 Memo
0xf0a0 stwmctl RW MR
ST
BE
RR
MT
EF
MT
AF
MR
AF
MA
CK
MT
EE
MR
AE
master control
0xf0a2 stwmdatr R MDATR[7:0] master receive data
stwmdats W MS
TA
MDATS[7:0] master send data
0xf0a4 stwmreqr RW ST
PR
NA
CK
RE
CV
master request data
0xf0a6 stwmbaud RW BAUD[14:0] master clock speed
0xf0a8 stwsctl RW SR
ST
ST
AF
SR
AF
SA
CK
ST
AE
SR
AE
slave control
0xf0aa stwsdatr R SS
TA
SA
DR
SS
TP
SDATR[7:0] slave receive data
stwsdats W SDATS[7:0] slave send data
0xf0ac stwsadr RW SADR[6:0] slave address
0xf0ae stwsmsk RW SMSK[6:0] slave address mask

※前半の0xf0a0〜0xf0a7までがSTWSERマスタユニットのレジスタで、後半の0xf0a8〜0xf0afまでがSTWSERスレーブユニットのレジスタです。

 BUSC出力のセレクト信号はbcs_stws_nです。上表の範囲でアサートされるので、STWSERユニット内では、アドレスbadr[3:0]を復号してレジスタを特定しています。このI/Oユニットはバイトアクセス禁止です。必ずワードアクセスしてください。

STWSERマスタユニットのレジスタ

STWSERスレーブユニットのレジスタ

 STWSERユニットからの割り込み要因(MTER,MRAR,STAR,SRAR)について、割り込みコントローラのI/Oレジスタに追加しておきました。

オープンドレイン端子

 TD IDEを使ったデザインで、普通の入力端子、出力端子は使って来たのですが、今回のI2Cはオープンドレイン端子なので、少し勝手が違います。
 TD IDEのI/O Constraint(下図)を開いてみても、「オープンドレイン」的な設定項目はありませんでした。

a151_iocons.png


 どうやら、RTLで普通にオープンドレイン端子を記述(下コード、mcoc115tw0408.vから抜粋)してコンパイル(論理合成・配置配線)すると、自動的に最適なI/Oバッファを推定してくれるようです。

  // i/o buffer
  assign stws_scl=(stws_scl_d)? 1'bz: stws_scl_d;
  assign stws_sda=(stws_sda_d)? 1'bz: stws_sda_d;


 オープンドレイン端子は、LOWレベルへのシンクしか実行しないので、上の様なコードとなります。I2CのSCL,SDA信号は双方向の信号なので、上コードのstws_scl,stws_sda信号を入力側に引き込んでおくと、これもまた自動的に適切なI/Oバッファを推定してくれるようです。……賢いのね……(1^^;)。
 また、TD IDEのI/O Constraintでは、デフォルトで入力端子をプルアップする設定になっています。入力端子をフローティングにしておくのは不安ですし、ましてI2C信号となればプルアップ必須なので好都合です。デフォルトのままプルアップ有効で使います。

※外部に十分なプルアップ抵抗が接続されていて、FPGAボードのプルアップを無効化したい場合には、IO Constraintの画面でプルアップ設定(PullType)をNONEに設定することが出来ます。


 シミュレーションする時は、テストベンチ(test.v)にて、I2CのSCL,SDA端子に対して「プルアップ」の指定が必要となります(↓)。

  tri1 stws_scl;
  tri1 stws_sda;


 tri1で配線を宣言しておくと、宣言した信号が1'bz(ハイインピーダンス)になった時、"1"レベルにしてくれます。「プルアップ」ですね。逆に高抵抗接地したい場合は、tri0とします。

デザインデータ

 同期二線式シリアルユニットSTWSERと、そのI/Oユニットを搭載したマイコンMCOC115TW0408のデザインデータを以下のリンク先に登録しておきました。

btn_moscovium.png

 同期二線式シリアルSTWSERユニットに関しては、現時点ではシミュレーションによる動作確認だけが済んでいます。今後、実機での動作検証によってバグや対策点が見つかった場合、仕様から変更する場合もあり得ます。と、言う前提付きで、バージョンはver.1.00としておきました。

 なお、I2C信号線(SCL,SDA)に内蔵プルアップ抵抗を有効にして、P2,R2端子に割り当てるため、I/O制約ファイルにはMCOC_full.adcを指定してください。

 以下にコンパイル(論理合成・配置配線)結果をまとめておきます。

 このデザインデータを使い、シミュレーションで動作確認しておきましょう。

シミュレーション確認

 シミュレーション確認に使用するアセンブラソースを以下にリンクしておきます。

  STWSERユニットテスト用アセンブラ:mcoc_stws_aws_arrr.asm

 上のアセンブラソースでは、以下に示すSMBusのワードリードをSTWSERマスタユニットからキャストして、STWSERスレーブユニットで受ける通信を実行しています。

ar81_smbus.png


 以降、以下の様に表記します。


 プログラム(mcoc_stws_aws_arrr.asm)をアセンブルして、シミュレーションしてみましょう。

  実験サンプル:【 】内はCygwinシェルウインドウからの実行コマンド
  【iverilog -f test.lst ; if ( ! $status ) vvp a.out】
    :
  VCD info: dumpfile test.vcd opened for output.
  Test Pass

  【gtkwave test.vcd】


 シミュレーション波形を以下図に示します。

a175_i2cwf.png

※クリックすると拡大してご覧いただけます。

 余計な信号も見えていますが、一番下の2本がI2Cの通信信号線、SCLとSDAです。STWSERマスタユニットからSTWSERスレーブユニットへSMBusのワードリードを実行したシミュレーションとなります。以下雑感です。

 今回シミュレーションに使用したプログラム(mcoc_stws_aws_arrr.asm)は、論理回路の動作確認用であるため、色々なタイミングで事細かにフラグビットの確認を実行して、期待通りの動作になっていることを確認しています。このため、だいぶ煩雑な印象があると思います。
 実際の応用プログラムでは、こんなに頻繁にフラグチェックする必要は無いので、もっと簡単にプログラムが組めることと期待しています。


 I2C仕様書を眺めながら、「この機能が果たせてない」とか、「こんな対応も要るのか」とか、気が付いたところで対応可能だった項目については対応したつもりではあります。さすがに、「箸にも棒にもかからない🍜」みたいな状態ではないハズ。次回は、ソフトウェアを作って実際のI2C機器と通信してみたいと思います。


btn_arduino.png

btn_prev.png btn_next.png

home