I2Cの使い方 1
シリアル通信の種類
2点間でデータ通信をする方法は、古くから考えられており、シリアル通信とパラレル通信とに大別される。
今回のターゲットであるシリアル通信では、データを1ビットづつ順に送り出すことで通信を行う。
シリアル通信は、I2Cなどの同期通信と、RS-232Cなどの非同期通信とに分けられる。
同期通信の場合、データ用の信号線と、クロック用の信号線とが存在する。送信側はクロック線を変化させながらデータ線にデータを乗せる。受信側はクロックの変化を監視し、適切なタイミングでデータ線を読み込む。
図の例では、送信側はデータを更新すると同時にクロックを立ち上げる。また、データの1ビット幅の中心で、クロックを立ち下げる。
受信側は、クロックの立下りでデータ線を読み込む。
非同期通信の場合は、クロック信号を持たない。そのため、送信側と受信側とで通信速度をあらかじめ決めておかなければならない。受信側はデータの1ビット幅の中心付近でデータ線を読み込む。
図の[START]、[STOP]は、データ転送の始まりと終わりを示すもので、シリアル通信ではよく用いられる。
I2Cの仕組み
I2Cとは、Inter Integrated Circuitの略で、同期シリアル通信の一種であり、次のような特徴がある。
- データ線(SDA)とクロック線(SCL)の2線式のシリアル通信である。
- バスで構成されており、1つのマスタデバイス(通常はマイコンなど)と、複数のスレーブデバイス(メモリやセンサなど)が存在する。
- 各スレーブには個別のアドレスが割り振られており、特定のスレーブデバイスとの通信が可能。
図のように、I2Cはバス構成になっており、1つのバスに1つのマスタデバイスと、複数のスレーブデバイスが存在できる。(複数のマスタデバイスが存在するマルチマスタモードもあるが、今回は省略する)
通信に必要な信号線はSDAとSCLの2本である。
- [S]は開始条件(Start condition)と呼ばれ、データ転送の開始を示す特別な状態である。
- [SLA]はスレーブアドレスで、通信する相手(スレーブ)のアドレスである。
- [R/W]は送受信の方向を示す。0の時マスタからスレーブへ(ライト)、1の時スレーブからマスタへ(リード)、データ転送が行われる。
- [A]はアクノリッジであり、受信したデバイスが発行する。マスタが発行する場合もあるし、スレーブが発行する場合もある。正常に受信できた場合は0、異常時は1を返答する。
- [DATA]は転送するデータである。R/Wビットの設定によりマスタが送信したり、スレーブが送信したりする。
- [P]は停止条件(Stop condition)と呼ばれ、データ転送の終了を示す特別な状態である。
I2Cでは、データ8bit+ACK=9bitが通信の基本単位である(SLA7bit + R/W + ACKでやはり9bitである)。8bitを送り、受け取った側がACKを返す、の繰り返しである。
[DATA]+[A]の部分は繰り返すことが可能で、連続リードや連続ライトとして応用ができる。
[DATA]や[A]の部分は[R/W]ビットの値により、送受信の向きが変化するので注意が必要である。
ここに示した通信フォーマットは基本的なものであり、今回のターゲットであるEEPROMでは、拡張したフォーマットが用いられる。これについては必要になった段階で説明する。
SH7085のI2Cレジスタ
SH7085には、I2C通信を行う機能モジュール(I2C2)が搭載されており、内蔵レジスタを操作することによりI2C通信が可能である。
・STBCR3(スタンバイコントロールレジスタ3)
他の機能モジュールと同様に、I2Cモジュールも初期状態ではクロック供給が止められている。
iodefine.hでは次のように定義されている。
struct st_stb { /* struct STB */ : union { /* STBCR3 */ unsigned char BYTE; /* Byte Access */ struct { /* Bit Access */ unsigned char _IIC2:1; /* IIC2 */ unsigned char _SCIF:1; /* SCIF */ unsigned char _SCI2:1; /* SCI2 */ unsigned char _SCI1:1; /* SCI1 */ unsigned char _SCI0:1; /* SCI0 */ unsigned char _SSU :1; /* SSU */ } BIT; } CR3; : }
HEWでは、次のように記述する。
STB.CR3.BIT._IIC2 = 0; /* I2Cモジュールスタンバイ解除 */
・ICCR1(I2Cバスコントロールレジスタ1)
I2Cの動作/停止、送信/受信、転送クロックの選択を行うレジスタである。
転送クロックの選択は、接続するデバイスによって決まる。今回の接続相手であるEEPROMは、400[kHz]が上限である。Pφ=20[MHz]であるから、1/50の時に400[kHz]となる。1/50という設定はできないので1/64を選択する。
MST, TRSビットで、動作モードと送信/受信の設定を行う。今回はSH7085をマスタ、EEPROMをスレーブとして使用する。送信/受信はその都度切り替える。
・ICCR2(I2Cバスコントロールレジスタ2)
I2C通信の開始条件/停止条件の発行などを行うことができる。
iodefine.hでは次のように定義されている。
struct st_iic2 { /* struct IIC2 */ : union { /* ICCR2 */ unsigned char BYTE; /* Byte Access */ struct { /* Bit Access */ unsigned char BBSY :1; /* BBSY */ unsigned char SCP :1; /* SCP */ unsigned char SDAO :1; /* SDAO */ unsigned char SDAOP :1; /* SDAOP */ unsigned char SCLO :1; /* SCLO */ unsigned char :1; /* */ unsigned char IICRST:1; /* IICRST */ } BIT; /* */ } ICCR2; /* */ : }
このレジスタを操作するには、注意事項がある。
・開始条件/停止条件を発行する場合は、BBSYとSCPの2ビットを同時に書き込まなければならない。
停止条件の発行は
IIC2.ICCR2.BYTE &= 0x3f; // 停止条件を発行
のようにする。
開始条件の場合、制限を厳密に守るには次のように記述する。
IIC2.ICCR2.BYTE = (IIC2.ICCR2.BYTE & 0xbf) | 0x80;
・ICIER(I2Cバスインタラプトイネーブルレジスタ)
I2C割り込みを設定したり、ACK(アクノリッジ)の送受信に関するレジスタである。今回は割り込みを使用しないので、本レジスタはACKビットの操作で使用する。
[ACKBR]は、受信したACKビットを格納している。ACKビットは受信側において、正常に受信できたかどうかを送信側に返答するビットである。
ACK=0は相手が正常に受信できたことを示している。ACK=1の場合は相手が受信に失敗したことを示している。
[ACKBT]は、正常に受信できたかどうかを、スレーブデバイスに返答するビットを設定する。正常に受信できたときは0を、失敗した時は1を設定しておくと、ACKを送信するタイミングにおいてこのビットの内容を送信する。
・ICSR(I2Cバスステータスレジスタ)
各種ステータスの確認を行うレジスタである。
TDREとTENDは似ているが、次のように区別して使用する。
- 送信データを書き込むと、TDRE、TENDともにクリアされる。
- 次の送信データを受け付けられるようになるとTDRE=1になる。この時はまだTEND=0である(データ送信は実行中)。
- 次の送信データを書き込むと再びTDRE=0になる。
- 次の送信データを受け付け可能になると、TDRE=1になる。
- 次の送信データを書き込まずにいると、いずれ全ビットの送出が完了し、TEND=1になる。
TDREは次の送信データ受付可能、TENDは送信が完了したことを示す。
・ICDRT(I2Cバス送信データレジスタ)
送信データを格納する8ビットのレジスタである。次のデータを受け付け可能になるとTDRE=1となる。この時に次の送信データを書き込むことにより、連続した送信が可能になる。
・ICDRR(I2Cバス受信データレジスタ)
受信データが格納される8ビットのレジスタである。
EEPROMの通信仕様
今回のI2C通信の相手は、MEMEs上のEEPROMである。すでに説明したが、I2C転送クロックの上限値は400kHzである。
このデバイスは8ピンの小型のメモリであるが、512Kbitsの容量がある。バイト単位で言えば64Kバイトである。よって16ビットのアドレス空間が必要なことがわかる。
I2Cの通信フォーマットにおいて、SLA(スレーブアドレス)があるが、これはデバイスの識別に使用されるもので、EEPROM内のメモリアドレスを示すものではない。メモリアドレスの指定には、I2Cフォーマットの[DATA]部分を使用する。[DATA]を2バイト分使用して、16bitのアドレス指定としている。
シリアルEEPROMであるので、通常のメモリと比較してアクセスに時間がかかる。これは、アドレス指定やデータのやり取りがシリアルなので当然のことであるが、今回使用するデバイスには一括してリード/ライトできる機能が備わっている。
リードする際は、一度のアドレス指定で連続した領域をリードし続けることができる。
ライトの時は内蔵のページバッファを使用して、最大128バイトのデータを一括書き込みすることができる。
このような機能を使うことにより、高速アクセスが可能になる。
・EEPROM書き込み
まず、EEPROMへのデータ書き込みについて説明する。
MEMEsに搭載のEEPROMでは、SLA(スレーブアドレス)は固定であり、1010000の7ビットである。これにリード/ライトの識別ビットR/W=0を加えて8ビットのコントロールバイトとして扱う。
EEPROMへの書き込みの場合は、コントロールバイト=0xA0となる。
続いて[DATA]を送る。[DATA]といっても、実際はメモリアドレスを示したりしている。メモリアドレス上位8bit、メモリアドレス下位8bit、書き込みデータ8bitを順に送る。
これらの動作を、上で説明したSH7085のレジスタを操作しながら
[開始条件発行] → [ICDRTに0xA0を書き込む] → [ACKを確認] → [ICDRTにメモリアドレス上位8bitを書き込む] → …
のように行う。
実際は、SH7085の各種フラグを確認しながら、確実なタイミングでのレジスタ操作が必要になる。例えば、開始条件を発行するにも、ICCR2レジスタを操作するだけでは誤動作してしまう場合がある。そのため、発行前に確認するべきフラグや、発行後に確認するべきフラグなどSH7085のマニュアルに詳しく記述されている。
今回は、複雑な手順については説明を省略した。演習においては詳しく説明してあるか、既に作りこんであるので心配しなくてよい。
・EEPROMページ書き込み
今回のターゲットEEPROMは、ページライト機能を持っている。これは連続した領域にデータをまとめて書き込みをする機能である。この場合、メモリアドレスを指定する手間が省けるので、書き込み処理が高速になる。
ページサイズは128バイトである。
EEPROM書き込みの図で、[DATA]+[A]部分を繰り返すだけでページライトになるが、ページ境界を越えての連続書き込みはできないので注意が必要である。
・EEPROM読み出し
続いて、データの読み出し動作を説明する。
読み出しの場合、アドレスは[マスタ]→[スレーブ]と送られ、データは[マスタ]←[スレーブ]のように逆向きに送られるので注意してほしい。
[S]→[ControlByte=0xA0]→[A]→[アドレス上位8bit]→[A]→[アドレス下位8bit]→[A]
の、メモリアドレス指定までは、書き込みの時と同じである。
ここで、再び開始条件[S]を発行することによりメモリアドレス指定を保持したまま、データの向きを変更して、データを読み出す。停止条件[P]を発行せずに開始条件[S]を発行していることに注意してほしい。上のメモリアドレス指定に続けて
[S]→[ControlByte=0xA1]→[A]→[読み出しデータ]→[A]→・・・→[読み出しデータ]→[A]→[P]
の手順で通信を行う。データ読み出しなのでR/W=1となり、ControlByte = 0xA1になる。
読み出しの場合、アドレス指定(マスタ→スレーブ)と読み出しデータ(マスタ←スレーブ)とでは向きが逆になるので、上のように途中で開始条件[S]を再発行してデータ方向を切り替えるという手順をとる。
[読み出しデータ]と[A]の部分は、繰り返しが可能である(一度だけでも良い)。書き込みと違って、ページ境界に関係なく連続読み出しが可能である。
以上で、EEPROMのリード/ライトが可能になる。これらの手順はEEPROMの仕様として定められている。異なるデバイスと通信する場合は、デバイスの通信仕様を調べて、適切な手順で通信しなければならない。
I2C接続のEEPROMは通常のメモリと比較して、アクセスする手順が複雑であり、高速アクセスも苦手であるという欠点がある。しかし、信号線が少ないために部品を小型化でき、基板上での占有面積も小さい、センサなど各種デバイスも含め少ない信号線で接続できる、などの利点も多いため、よく使われている。
演習
ワークスペース
I2C_1.zip
をダウンロードし、展開する。
・演習1:I2C_11.cの未完成部分を完成せよ。
EEPROMに対して1バイトのリード/ライトを行う。未完成の関数は2つある。
・I2C_start()関数は、開始条件を発行する関数である。
- 開始条件を発行する
- ICSRのTDREがセットされたことを確認して終了
の手順で行う。
・I2C_stop()関数は、停止条件を発行する関数である。
- ICSRのSTOPビットをクリアする
- 停止条件を発行する
- ICSRのSTOPビットがセットされたことを確認して終了
の手順で行う。
・演習2:I2C_11.cに次のような機能を加えよ。
Htermからコマンドを発行し、EEPROMを読み書きできるようにする。実現するコマンドはR, W, Dの3つである。
1.Rコマンド:EEPROMから1バイト読み出し、Htermに表示する。
R[スペース][アドレス]のフォーマットで、アドレスは16進数で指定する。Htermへの表示も16進数とする。
2.Wコマンド:EEPROMに1バイト書き込む。
W[スペース][アドレス][スペース][データ]のフォーマットで、アドレスとデータは16進数で指定する。
3.Dコマンド:EEPROMから連続した64バイトを読み出し、Htermに表示する。
D[スペース][アドレス]のフォーマットで、アドレスは16進数で指定する。
表示は
[アドレス] : [データ][スペース][データ]…
とし、1行に8バイト分表示する。
I2C_EEread_byte()関数を、複数回呼び出すことで実現する。(この演習ではI2Cの連続読み出し機能は使用していない)