公開日:2021/08/14
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の標準モード(〜100kHz)、高速モード(FS、400kHz)に対応する。
- ハイスピードモード(HS、〜3.4MHz)には未対応。
- 10ビットアドレスには、一部未対応とする。
- マスタ通信の10ビットアドレス指定に関しては、ソフトウェア(ドライバ)次第で対応できる可能性がある。
- ただし、未確認。手元に10ビットアドレス対応の機器が無いので、確かめられない。
- ※10ビットアドレスに対応した機器が見当たらないので、別に対応できなくても、大した問題ではないのかもしれない。
- スレーブ通信の10ビットアドレス指定には対応しない。……通常の7ビットアドレス指定が可能。
- スレーブユニットは、ゼネラルコールアドレスには応答(アクノリッジ)しない。
- マルチバスマスタは、積極的には推奨しない。
- ただし、最低限の論理は作り込んでおく。
- 「マルチバスマスタでも動くかもね。動かなかったらゴメンね」。
- I2C仕様書で規定されている、細かな電気的特性については不問とする。
- 手持ちのI2C対応機器と通信できたらOKとする。
- 普通にノイズ対策して、普通にジャンパ線で配線していれば、「まあ、動くでしょう。動かなかったらゴメンね」。
ソフトウェア(ドライバ)で、ちゃんとI2Cプロトコルに則った通信を実行すれば、普通にI2Cモジュールと通信することは可能なはず!です。ただし、スペック(電気的特性AC,DC含む)ぎりぎりで通信したり、通信信号線長が長いとか、雑音の多い(ノイジィな)環境だったりすると、通信を失敗するかもしれないのでご注意ください。
いつもの通り、「サポート無しの無保証」です。
STWSERユニットの設計
I2Cの仕様その物については何も解説せずに来ているので、細かな設計について議論しても仕方ありません。ですので、設計についてはさらっと流して、使い方(ソフトウェアの作り方)や手持ちI2C機器との通信実験などの方にウェイトを置こうと思います。
以下図に同期二線式シリアル通信STWSERユニットのブロック図を示します。
※黄色に着色した箱は、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マスタユニットのレジスタ
- stwmctl: マスタ制御レジスタ:
- 初期値: 0x0000 (フラグ値は除く)
- MRST: マスタユニットリセットビット
- 本ビットに1を書き込むと、マスタユニット全体をリセットします。本ビット自体もリセットされるので、MRST=1の状態を読み出すことはありません。
- 何らかの異常状態に陥って、デッドロックした場合の最終手段となります。
- 通常、本ビットを使用すべき場面はありません。
- BERR: バスエラーフラグ
- バスエラーが起きると、BERR=1となって、マスタ制御ステートマシンにリセットがかかり、通信を停止します。
- バスエラーが起きるのは、I2Cバス上でバス権の獲得に失敗した場合です。
- 本ビットに1を書き込むことでフラグをクリアすることが出来ます。クリアしたくない場合には、本ビットに0を書き込んでください。
- 一度BERR=1となると、その時実行していた通信は無効となります。BERRフラグをクリアしたら、最初のSTARTコンディションから通信をやり直す必要があります。
- BERR=1となると、割り込みを要求することが出来ます(MTEE,MRAEビットの説明を参照)。
- MTEF: 送信終了フラグ
- stwmdatsレジスタにデータを書き込むと、1バイト分のデータを送信します。
- 実際に送信が終了すると、MTEF=1となります。
- stwmreqrレジスタを使ってSTOPコンディションを発行した場合も、MTEF=1となります。
- MTEF=1となると、割り込みを要求することが出来ます(MTEEビットの説明を参照)。
- 本ビットに1を書き込むことでフラグをクリアすることが出来ます。クリアしたくない場合には、本ビットに0を書き込んでください。
- MTAF: 送信データ書き込み可能フラグ
- stwmdatsレジスタに書き込み可能な場合にMTAF=1となります。
- 従って、stwmdatsレジスタに書き込んだ直後はMTAF=0にクリアされます。
- stwmdatsレジスタと、ユニット内部の送信データレジスタは2段FIFOを構成するため、送信動作が始まってstwmdats → 内部送信データレジスタへ転送されるとMTAF=1に戻ります。
- 読み出し専用、書き込みは無効です。
- MRAF: 受信データ読み出し可能フラグ
- 受信データレジスタstwmdatrレジスタに受信したデータが格納されると、MRAF=1となります。
- stwmdatrレジスタを読み出すことで、MRAF=0にクリアされます。
- MRAF=1となると、割り込みを要求することが出来ます(MRAEビットの説明を参照)。
- 読み出し専用、書き込みは無効です。
- MACK: アクノリッジ状態フラグ
- マスタがスレーブに対してデータを送信すると、1バイト送信毎にスレーブからアクノリッジが返ってきます。
- 本ビットを読み出すと、最後に送信した時の、スレーブからのアクノリッジ状態が判ります。
- MACK=1だとアクノリッジ応答があった事を示しています。
- MACK=0の場合は、スレーブが応答しなかった場合等のエラー状態です。
- 読み出し専用、書き込みは無効です。
- MTEE: 送信終了割り込み許可ビット
- MTEE=1に設定してMTEF=1となると、送信終了割り込みMTERをCPUに対して要求することが出来ます。
- MTEE=1の時にバスエラーが発生して、BERR=1となった場合にも、送信終了割り込みMTERをCPUに対して要求することが出来ます。
- MTER=MTEE&(MTEF|BERR)です。
- MRAE: 受信割り込み許可ビット
- MRAE=1に設定してMRAF=1となると、受信割り込みMRARをCPUに対して要求することが出来ます。
- MRAE=1の時にバスエラーが発生して、BERR=1となった場合にも、受信割り込みMRARをCPUに対して要求することが出来ます。
- MRAR=MRAE&(MRAF|BERR)です。
- stwmdatr: マスタ受信データレジスタ
- MRAF=1の時に読み出すと、受信したデータを本レジスタの下位8ビットから読み出すことが出来ます。
- 本レジスタを読み出すことで、MRAF=0にクリアされます。MRAF=0の状態で本レジスタを読み出すことは禁止です。
- 本レジスタはリードオンリです。マスタ送信データレジスタstwmdatsと同じアドレスに割り当ててあるので、書き込みはstwmdatsレジスタへの書き込みとなります。
- MRAF=1の状態で本レジスタを読み出すことなく、次のデータを受信してしまうと、新しい受信データが上書きされて古いデータは消失します。
- stwmdats: マスタ送信データレジスタ
- 本レジスタの下位8ビットにデータを書き込むと、I2Cバスに対して8ビットデータを送信します。
- 必ずMTAF=1を確認後、本レジスタへ書き込んでください。
- 本レジスタはライトオンリです。マスタ受信データレジスタstwmdatrと同じアドレスに割り当ててあるので、読み出しはstwmdatrレジスタからの読み出しとなります。
- アドレスバイトを送信する場合には、同時にMSTAビット=1を書き込んで、STARTコンディションを発行してください(I2C仕様)。
- データバイトの送信時は、MSTA=0としなければなりません(I2C仕様)。
- MSTA: STARTコンディション発行ビット
- 本ビットに1を書き込むと、下位8ビットに書き込んだデータを送信する前に、STARTコンディションを発行します。
- 必ず、送信するデータと同時にMSTA=1を書き込んでください。
- stwmreqr: マスタ要求レジスタ
- 初期値: 0x0000
- 本レジスタのビットに1を書き込むことで、所望の動作を発動することが出来ます。
- 書き込んだコマンド(ビット値=1)は、コマンドの実行が終了するまで保持され、実行終了時に自動クリア(ビット値=0)されます。
- 本レジスタへの0書き込みでは、ビット値はクリアできません。
- STPR: STOPコンディション発行ビット
- 本ビットに1を書き込むと、STOPコンディションを発動します。
- RECVビットの方が優先順位が高いため、RECV=1とSTPR=1を同時に書き込むと、先に1バイト受信してから、STOPコンディションを発行します。
- データ送信中など、他の動作実行中はSTOPコンディションの発動が保留されます。
- STOPコンディションを発行し終わると、STPR=0に自動クリアされます。
- NACK: 受信後NACK送信ビット
- 本ビットはRECV=1を書き込んだ時だけ有効となります。
- RECV=1を書き込んでデータを受信した後、マスタがスレーブに対してアクノリッジを返す必要があります(I2C仕様)。
- この時、まだ受信要求が続く場合はNACK=0、その受信で最後の場合はNACK=1とする必要があります(I2C仕様)。
- RECV=1で要求した受信動作が完了すると、NACK=0へ自動クリアされます。
- RECV: 受信要求ビット
- 本ビットに1を書き込むと、スレーブから1バイト受信します。
- RECV=1で要求した受信動作が完了すると、RECV=0に自動クリアされます。
- また、受信するとMRAF=1となるので、stwmdatrレジスタから受信したデータを読み出してください。
- アドレスバイト送信中など、他の動作実行中は受信動作が保留されます。
- 複数データを受信する場合、最後のデータを受信した後でNACKを返す必要があるので、NACK=1と同時にRECV=1を書き込んでください(I2C仕様)。
- RECV=1で要求した受信動作が完了すると、NACKビットも同時に0へ自動クリアされます。
- stwmbaud: マスタ通信速度レジスタ
- 初期値: 0x0000
- 通信速度(クロック周波数)を設定する15ビットのレジスタです。
- 通信クロック周波数をfSCL[Hz]とすると、stwmbaud=fcpu/fSCL−1が設定値となります。fcpu=24MHzです。
- 標準モード(〜100kHz)では、fSCL=100kHzより、stwmbaud=239程度の設定値となります。
- 高速モード(400kHz)では、stwmbaud=59です。
- STWSERユニットの仕様上、stwmbaud<19では正常にクロックを生成することが出来ません。stwmbaud≧19で使ってください。
- この時のfSCL=1.2MHzが、本ユニットで生成可能なクロックの最大周波数です。
- 有効ビットは15ビットですので、最大でstwmbaud=32,767を指定可能で、この時のクロック周波数fSCL≒732Hzが一番遅い通信クロック設定となります。
STWSERスレーブユニットのレジスタ
- stwsctl: スレーブ制御レジスタ
- 初期値: 0x0000 (フラグ値は除く)
- SRST: スレーブユニットリセットビット
- 本ビットに1を書き込むと、スレーブユニット全体をリセットします。本ビット自体もリセットされるので、SRST=1の状態を読み出すことはありません。
- 何らかの異常状態に陥って、デッドロックした場合の最終手段となります。
- 通常、本ビットを使用すべき場面はありません。
- STAF: 送信データ書き込み可能フラグ
- stwsdatsレジスタに書き込み可能な場合にSTAF=1となります。
- stwsdatsレジスタと、ユニット内部の送信データレジスタは2段FIFOを構成するため、送信動作が始まってstwsdats → 内部送信データレジスタへ転送されると、STAF=1に戻ります。
- STAF=1となると、割り込みを要求することが出来ます(STAEビットの説明を参照)。
- 読み出し専用、書き込みは無効です。
- SRAF: 受信データ読み出し可能フラグ
- 受信データレジスタstwsdatrレジスタにデータを受信するとSRAF=1となります。
- マスタからのスレーブアドレスが自分のアドレスと一致しなかった場合、データを受信することは無く、従ってSRAFはセットされません。
- その他、START/STOPコンディションを検出した場合にも、SRAF=1となります。
- START/STOPコンディションについては、他のスレーブが対象であったとしても、SRAF=1となります。
- stwsdatrレジスタを読み出すことで、SRAF=0にクリアされます。
- SRAF=1となると、割り込みを要求することが出来ます(SRAEビットの説明を参照)。
- 読み出し専用、書き込みは無効です。
- SACK: アクノリッジ状態ビット
- スレーブがマスタからのデータ送信要求を受け、スレーブ → マスタへデータを送信すると、1バイト送信する毎にマスタからアクノリッジが返ってきます。
- 本ビットを読み出すと、最後に送信した時のマスタからのアクノリッジ状態が判ります。
- SACK=1だとアクノリッジ応答があった事を示しています。まだ、マスタからのデータ送信要求が続くという意思表示です(I2C仕様)。
- SACK=0の場合は、その通信でマスタからの送信要求が最後だったことを表します(I2C仕様)。
- 読み出し専用、書き込みは無効です。
- STAE: 送信可能割り込み許可ビット
- STAE=1に設定してSTAF=1となると、送信終了割り込みSTARをCPUに対して要求することが出来ます。
- STAR=STAE&STAFです。
- SRAE: 受信割り込み許可ビット
- SRAE=1に設定してSRAF=1となると、受信割り込みSRARをCPUに対して要求することが出来ます。
- SRAR=SRAE&SRAFです。
- stwsdatr: スレーブ受信データレジスタ
- SRAF=1の時に読み出すと、受信したデータを本レジスタの下位8ビットから読み出すことが出来ます。
- 本レジスタを読み出すことで、SRAF=0にクリアされます。SRAF=0の状態で本レジスタを読み出すことは禁止です。
- 本レジスタはリードオンリです。スレーブ送信データレジスタstwsdatsと同じアドレスに割り当ててあるので、書き込みはstwsdatsレジスタへの書き込みとなります。
- SRAF=1の状態で本レジスタを読み出すことなく、次のデータを受信してしまうと、新しい受信データが上書きされて古いデータは消失します。
- 本レジスタの上位ビットからは、以下の状態フラグが同時に読み出されますので、各フラグをチェックして通信状態を把握することが出来ます。
- SSTA: STARTコンディション検出フラグ
- STARTコンディションを検出した場合にSSTA=1が読み出されます。
- SSTA=1の場合の下位8ビットデータは無効です。
- SADR: アドレスバイト受信フラグ
- マスタからアドレスバイトを受信した場合にSADR=1が読み出されます。
- SADR=1の場合の下位8ビットデータは、自分のスレーブアドレス7ビット+R/Wビットです。
- スレーブアドレスが一致しなかった場合、データは受信せず、SRAFフラグもアサートされません。
- SSTP: STOPコンディション検出フラグ
- STOPコンディションを検出した場合にSSTP=1が読み出されます。
- SSTP=1の場合の下位8ビットデータは無効です。
- SSTA,SADR,SSTPの各ビットは、複数が同時に1になることはありません。いずれか1つが1となるか、あるいは全て0の場合があります。
- STARTコンディションを検出するとSSTA=1となりますが、この状態で本レジスタを読み出さなかった場合、次にアドレスバイトを受信するとSADR=1となると同時にSSTA=0にクリアされます。
- 最後に検知した状態が反映され、古い状態はクリアされます。
- マスタからの最後のデータを受信した後、直ちにSTOPコンディションが発行されると、受信データを読み出し可能な時間は限られますので、SRAR割り込みなどを使って速やかに処理してください。
- stwsdats: スレーブ送信データレジスタ
- 本レジスタにデータを書き込むと、マスタからの送信要求に対して8ビットデータを送信します。
- 必ずSTAF=1を確認後、本レジスタへ書き込んでください。
- 本レジスタはライトオンリです。スレーブ受信データレジスタstwsdatrと同じアドレスに割り当ててあるので、読み出しはstwsdatrレジスタからの読み出しとなります。
- スレーブはマスタからの要求に従って動作するしかないので、マスタからの送信要求に合わせてデータを送信できる様、備えなければなりません(I2C仕様)。
- データ送信後、マスタからのアクノリッジを確認して、まだデータ送信要求が続くかどうかを判定できるのですが、アクノリッジをstwsctlレジスタのSACKビットで確認してから本レジスタstwsdatsに書き込んでも間に合いません。
- 従って、マスタからのデータ送信要求バイト数にかからわず、STAF=1となったら、次に送信すべきデータを次々と書き込んでおかなければなりません。
- 結果として、最後に書き込んだデータは過剰書き込みとなり、行き場をなくしてしまうため、マスタからNACKを受信した時点でSTAF=1として、過剰書き込みされたデータをキャンセル(破棄)しています(ハードウェア処理)。
- 逆に、マスタからの要求バイト数よりも少ないデータしか書き込まなかった場合、足りないデータは0x00が送信されます。
- 例外として、アドレスバイト(R/Wビット=1、リードアクセス)を受信した時点で、STAF=1(送信データが空)だった場合、アドレスバイトに対するスレーブからのアクノリッジ送信を保留します。
- この時、SRAF=1となりますので、フラグのポーリングないし、割り込みによって、マスタからの送信要求(リードアクセス)であることが判った場合には、速やかに最初の送信データを本レジスタstwsdatsに書き込んでください。
- 本レジスタstwsdatsへの書き込みによってSTAF=0となり、ステートが先に進みます。
- STAF=0となるまでの間、ずっとマスタを待たせてしまうので、放置するとデッドロックしてしまいます。
- stwsadr: スレーブアドレスレジスタ
- 初期値: 0x0000
- 自分のスレーブアドレス(7ビット)を指定するレジスタです。下位7ビットだけが有効です。
- 初期値(0)の状態だとアドレス比較を行わず、マスタからの通信に対して応答することはありません。stwsadr≠0値を書き込んで、使用してください。
- stwsmsk: スレーブアドレスマスクレジスタ
- 初期値: 0x0000
- スレーブアドレスレジスタstwsadrに対して、アドレス一致検出する際のマスクビットを設定するレジスタです。下位7ビットだけが有効です。
- マスタから受信したアドレスバイト中のアドレス7ビットをMasterAddr[6:0]とすると、以下の様にアドレスの一致を判定します。
- (MasterAddr[6:0]|stwsmsk[6:0])=(stwsadr[6:0]|stwsmsk[6:0])
- 本レジスタに非ゼロ値を書き込むことで、複数のスレーブアドレスに対してアクセスを受け入れ、アクノリッジを返します。
- 例)stwsadr=0x08、stwsmsk=0x07とした場合、
- マスタからのアドレス指定が0x08〜0x0fまでの8個のアドレスに対してアクセスを受け入れます。
STWSERユニットからの割り込み要因(MTER,MRAR,STAR,SRAR)について、割り込みコントローラのI/Oレジスタに追加しておきました。
オープンドレイン端子
TD IDEを使ったデザインで、普通の入力端子、出力端子は使って来たのですが、今回のI2Cはオープンドレイン端子なので、少し勝手が違います。
TD IDEのI/O Constraint(下図)を開いてみても、「オープンドレイン」的な設定項目はありませんでした。
どうやら、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のデザインデータを以下のリンク先に登録しておきました。
同期二線式シリアルSTWSERユニットに関しては、現時点ではシミュレーションによる動作確認だけが済んでいます。今後、実機での動作検証によってバグや対策点が見つかった場合、仕様から変更する場合もあり得ます。と、言う前提付きで、バージョンはver.1.00としておきました。
なお、I2C信号線(SCL,SDA)に内蔵プルアップ抵抗を有効にして、P2,R2端子に割り当てるため、I/O制約ファイルにはMCOC_full.adcを指定してください。
以下にコンパイル(論理合成・配置配線)結果をまとめておきます。
- MCOC_TW.al (MCOC115TW0408)
- コンパイル時間: 約3.5分
- F/F数: 2,054個 (最大19,600個)
- 9KビットRAMマクロ数: 1個 (最大64個)
- 1Kバイト分のUART8N1ユニット受信バッファ用FIFOマクロで1個使用。
- 32KビットRAMマクロ数: 3個 (最大16個)
- 4Kバイト分の命令ROM用RAMマクロで1個使用。
- 8Kバイト分のデータRAM用RAMマクロで2個使用。
- DSPマクロ数: 1個 (最大29個)
- タイミング解析(STA)結果:
- Setup Slack: 7.517ns (MET)
- Hold Slack: 0.095ns (MET)
- 確認時のクリティカルパスは、CPU命令レジスタirから、Aバス、MULC16、DSPマクロ、Cバスと抜けて、バンク1汎用レジスタr6へ到達するパス(真パス)。
このデザインデータを使い、シミュレーションで動作確認しておきましょう。
シミュレーション確認
シミュレーション確認に使用するアセンブラソースを以下にリンクしておきます。
STWSERユニットテスト用アセンブラ:mcoc_stws_aws_arrr.asm
上のアセンブラソースでは、以下に示すSMBusのワードリードをSTWSERマスタユニットからキャストして、STWSERスレーブユニットで受ける通信を実行しています。
以降、以下の様に表記します。
- Write Byte: S [ADR,W] [CMD] [DAT] P
- Write Word: S [ADR,W] [CMD] [DAT.L] [DAT.H] P
- Read Byte: S [ADR,W] [CMD] R [ADR,R] [[DAT]] P
- Read Word: S [ADR,W] [CMD] R [ADR,R] [[DAT.L]] [[DAT.H]] P
- 凡例:
- S: STARTコンディション
- R: 反復STARTコンディション
- P: STOPコンディション
- [ ]: 1バイト送信データ
- [[ ]]: 1バイト受信データ
- (A)または(N): アクノリッジ応答(ACK/NACK)
- 通常、アクノリッジサイクルについては表記を省略し、必要な場合のみ表記します。
- 例)S [ADR,W] [CMD] R [ADR,R] [[DAT.L]](A) [[DAT.H]](N) P
- < >: 割り込み
- 必要に応じて、STWSERユニットで割り込みの起きるタイミングを示します。
- 例)S<SRAR1> [ADR,R]<SRAR2>(A) [[DAT]](N) P<SRAR3>
- STWSERスレーブユニットでは、STARTコンディションSを検知した時、[ADR,R]を受信した時、STOPコンディションを検出した時の、3つのタイミングでスレーブ受信割り込みSRARが発生します。
プログラム(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】
シミュレーション波形を以下図に示します。
※クリックすると拡大してご覧いただけます。
余計な信号も見えていますが、一番下の2本がI2Cの通信信号線、SCLとSDAです。STWSERマスタユニットからSTWSERスレーブユニットへSMBusのワードリードを実行したシミュレーションとなります。以下雑感です。
- STWSERマスタユニットから、I2Cの通信波形が生成されています。
- シミュレーション確認用なので、クロックスピードfSCL=1.2MHzという高速クロックに設定してあります。
- I2C通信波形と見て矛盾の無い波形が生成されています。
- バスサイクル:
- S [ADR,W](A) [CMD](A) R [ADR,R](A) [[DAT.L]](A) [[DAT.H]](N) P
- ADR=0x60
- CMD=0x5a
- DAT.L=0x3c
- DAT.H=0xc3
- アドレス0x60に設定したSTWSERスレーブユニットからも、正しいタイミングで正しい応答が返ってきています。
- マスタ→スレーブへデータを送信した場合の、スレーブ→マスタへ応答するアクノリッジにSDA=0(ACK)で応答しています。
- マスタからの送信要求に対して、2バイトのデータ[[0x3c]]、[[0xc3]]を返信しています。
- 上の波形からは読み取れませんが、内部ステートマシンの状態遷移が期待通りに動作していることや、その他内部信号の動作を詳細に確認してあります。
今回シミュレーションに使用したプログラム(mcoc_stws_aws_arrr.asm)は、論理回路の動作確認用であるため、色々なタイミングで事細かにフラグビットの確認を実行して、期待通りの動作になっていることを確認しています。このため、だいぶ煩雑な印象があると思います。
実際の応用プログラムでは、こんなに頻繁にフラグチェックする必要は無いので、もっと簡単にプログラムが組めることと期待しています。
I2C仕様書を眺めながら、「この機能が果たせてない」とか、「こんな対応も要るのか」とか、気が付いたところで対応可能だった項目については対応したつもりではあります。さすがに、「箸にも棒にもかからない🍜」みたいな状態ではないハズ。次回は、ソフトウェアを作って実際のI2C機器と通信してみたいと思います。