マイコン・メモ
TWI(I2C)インタフェースの使い方
ホームページに戻るトップページに戻る前ページに戻る次のページ最後のページ
AVRマイコン ATmega328P のTWI(I2C)インタフェースの基本的な使い方を、これまで製作で使用したものを基に紹介します。
TWIインタフェースを持っている機種であれば、若干の修正で他のAVRマイコンでも使えるハズです。ただし未確認。
1.I2Cバスインタフェースと通信フォーマット

AVRマイコンでは、I2Cバス(IICバス=Inter IC Bus)をTWI(Two-Wire Serial Interface)と表記しています。
I2Cバスインタフェースの概要と通信フォーマットについては こちら で説明しているので参照してください。

2.I2Cバス制御レジスタ

AVRマイコンのTWI(I2C)制御レジスタを下記に示します(スレーブ用は除く)。
レジスタTWBR, TWCR
レジスタTWSR, TWDR
注意:上記は概略です、詳細はAVRマイコンのデータシートで確認してください。

3.プログラム手順

AVRマイコンをマスター側とした場合のスレーブデバイスとの通信手順を、下図にまとめました。
ただ、ちょっとわかりにくいので、詳細は次項のフローチャートとプログラム例をご覧ください。
I2C通信の概要フロー図

4.制御プログラム例

下記に、AVRマイコン側をマスターとした、I2Cバスインタフェースの制御プログラム例を示します。8ビットまたは16ビットアドレスでスレーブデバイスにアクセスする関数もそれぞれ用意してあります。
システムクロック8MHzのATmega328P用に作ったものですが、そのままあるいは若干の修正で、ほかのAVRマイコンで使用できると思います。

I2Cインタフェースの制御フロー図(I2C_ProgramFlow.pdf)

I2Cインタフェースの制御プログラムのソースファイル(TWI_I2C.c)

I2Cインタフェースの制御プログラムのヘッダファイル(TWI_I2C.h)
注意:必ずしも正常にコンパイルおよび正常に動作することを保証するものではありません。
AVRマイコンの入門書や書籍を見ると、割り込みを使用しない、いわゆるポーリング方式で動作する例が、多くであげられているようですが、ここに示すのは割り込みを使用しているものです。
ポーリング方式に比べてプログラムが若干複雑になりますが、CPUの使用効率があがります。ただし、通信が終了するまで何もしないで待っているだけのプログラムでは効率は上がりません。
この例では、通信速度100Kbpsまたは400Kbpsのどちらかで使用することができます。ソースプログラムの26行にあるSCL_HZの宣言値を100または400にしてコンパイルすることで切り換えることができます。
その他の速度を使用したい場合や、使用の都度通信速度を切り換えたいような場合は、プログラムを修正する必要があります。
■独自型宣言など
BYTE → unsigned char
WORD → unsigned short
BOOL → int
FALSE = 0
 TRUE = 1
 NULL = 0
これらは、ヘッダファイル"hdwareInfo.h"で宣言していますが、このプログラム例には含まれていません。

5.関数の説明

前記ソースファイルにある関数について、使用方法を説明します。

(1)TWI(I2C)インタフェース初期化

    void Init_I2C(void)

TWI(I2C)インタフェースの初期化を行います。
マイコンの起動後に、割り込みマスク状態で一度だけ呼ぶ必要があります。
割り込みマスクは、メインループに入るまでに解除してください。

(2)定周期割り込み処理

    void Timer01mS_I2C(void)

関数TranceiveData_I2Cで、タイムアウトを監視するための時間計測を行います。
1mS周期で定周期割り込み処理(割り込みマスク状態)から呼ぶ必要があります。
プログラム例には、定周期割り込みを発生する部分は含まれていません。

(3)スレーブアドレスの設定

    void SetSlaveAddr_I2C(BYTE bySlaveAddr)

スレーブデバイスのアドレスをセットします。
以後のI2C通信は、ここでセットしたアドレスのデバイスに対して行われます。
引数bySlaveAddrは、スレーブデバイスのアドレスで、1バイトデータです。
下図は通信フォーマットの先頭部分で、このようにスレーブアドレスはb0を'0'とした値(下図の例では0xA2)として渡します。b0側に詰める必要はありません。
なお、以降で紹介するリード/ライト関数を使用する前に、最低1回はスレーブデバイスのアドレスを設定しなければなりません。
I2C通信の先頭部分、スレーブアドレスのビット
(4)送信してレスポンスを受信する

    BOOL TranceiveData_I2C(I2C_TXRX* TxRxParam)

コマンドを送信し、レスポンスを受信します。以降で紹介するリード/ライト関数群は、この関数TranceiveData_I2Cを呼んで通信を行っています。
引数の型I2C_TXRXは、ヘッダファイル"TWI_I2C.h"で次のように定義されています。

typedef struct {
   BYTE    bySndDataSz1;
   BYTE    bySndDataSz2;
   BYTE    byRcvDataSz;
   const BYTE* pSndData1;
   const BYTE* pSndData2;
   BYTE*   pRcvBuff;
} I2C_TXRX;

/* 1ブロック目の送信データのバイト数 */
/* 2ブロック目の送信データのバイト数 */
/* 受信データのバイト数 */
/* 1ブロック目の送信データがあるアドレス */
/* 2ブロック目の送信データがあるアドレス */
/* 受信データを格納するアドレス */

「1ブロック目の送信データ」とは、RTCのレジスタ番号やEEPROMのアドレスなど、書き込みまたは読み込みで最初にデバイスに与える、スレーブデバイスがデータのアドレスと解釈する部分です。スレーブデバイスのアドレスではありません。
「2ブロック目の送信データ」とは、デバイスに書き込むデータ本体です。書き込み時のみ必要なので、読み込みのみ行うときは bySndDataSz2=0 とします。
「受信データ」とはデバイスから読み込むデータのことで、そのバイト数は  byRcvDataSz で与えます。 書き込みのみ行うときは byRcvDataSz=0 とします。
bySndDataSz2≠0 かつ byRcvDataSz≠0 で、送信データと受信データの両方が指定された場合は先に送信が行われます。その場合、対象のデータアドレスの変化に注意が必要です。
これらI2C_TXRX型引数の使い方は、以降に示す関数のソースリストを参照してください。
関数の戻り値がFALSEだった場合は、タイムアウトなど何かしらの通信異常があったことを示します。なお、このプログラム例では100mS以内に正常終了しないとタイムアウトとして戻ります。
注意:
・指定された送信および受信が完了する、または通信異常が発生するまで、この関数から戻りません。
・一般にRTCやEEPROMなどのスレーブデバイスは、書き込みあるいは読み込みによって、指定されたデータアドレスが自動的に+1されるようになっています。したがって、複数バイトの送信データおよび受信データは、1ブロック目で指定したアドレスから+1ずつ順になったものです。
・送信データはデータメモリ(RAM)中にある必要があります。AVRマイコンは、データがプログラムメモリ(フラッシュROM)中にある場合は、通常のデータのように読み出すことができません。読み出しには専用関数(マクロ)が必要ですが、この関数TranceiveData_I2Cは対応していません。

(5)1バイトデータリード

    BOOL ReadByte_A8_I2C(BYTE byRdAddr, BYTE *pRxBuff)
    BOOL ReadByte_A16_I2C(WORD wRdAddr, BYTE *pRxBuff)

スレーブデバイスから1バイトだけデータを読み込みます。
引数byRdAddr,wRdAddrは、レジスタ番号やアドレスなど、デバイスのデータアドレス(読み込む位置)を指定します。
前者はbyRdAddrが8ビットで、RTCや小容量EEPROMなどを想定しています。後者はwRdAddrが16ビットで、大容量EEPROMなどを想定しています。
引数pRxBuffは、読み込んだデータを格納するアドレスです。
これらは、RTCやEEPROM用に限定したものではないので、同じフォーマットであれば他のスレーブデバイスでも使用できます。以降で紹介するライト関数も同様です。
関数の戻り値がFALSEだった場合は、何かしらの通信異常があったことを示します。

(6)ページデータリード

    BOOL ReadPage_A8_I2C(BYTE byRdAddr, BYTE bySize, BYTE *pRxBuff)
    BOOL ReadPage_A16_I2C(WORD wRdAddr, BYTE bySize, BYTE *pRxBuff)

スレーブデバイスから引数bySizeで指定したバイト数だけデータを読み込みます。
引数byRdAddr,wRdAddrは、レジスタ番号やアドレスなど、デバイスのデータアドレス(読み込む位置)を指定します。
前者はbyRdAddrが8ビットで、RTCや小容量EEPROMなどを想定しています。後者はwRdAddrが16ビットで、大容量EEPROMなどを想定しています。複数バイト読み込む場合、そのデータアドレスは順に+1されるものとします。
引数pRxBuffは、読み込んだデータを格納するアドレスです。
これらは、RTCやEEPROM用に限定したものではないので、同じフォーマットであれば他のスレーブデバイスでも使用できます。次に紹介するライト関数も同様です。
関数の戻り値がFALSEだった場合は、何かしらの通信異常があったことを示します。

(7)1バイトデータライト

    BOOL WriteByte_A8_I2C(BYTE byWrAddr, const BYTE *pTxData)
    BOOL WriteByte_A16_I2C(WORD wWrAddr, const BYTE *pTxData)

スレーブデバイスに1バイトのデータを書き込みます。
引数byWrAddr,wWrAddrは、レジスタ番号やアドレスなど、デバイスのデータアドレス(書き込む位置)を指定します。
前記のリード関数の場合と同じように、前者は8ビット、後者は16ビットのアドレス指定となります。
引数pTxDataは、書き込むデータが格納されているアドレスです。
関数の戻り値がFALSEだった場合は、何かしらの通信異常があったことを示します。

(8)ページデータライト

    BOOL WritePage_A8_I2C(BYTE byWrAddr, BYTE bySize, const BYTE *pTxData)
    BOOL WritePage_A16_I2C(WORD wWrAddr, BYTE bySize, const BYTE *pTxData)

スレーブデバイスに引数bySizeで指定したバイト数のデータを書き込みます。
引数byWrAddr,wWrAddrは、レジスタ番号やアドレスなど、デバイスのデータアドレス(書き込む位置)を指定します。
前記のリード関数の場合と同じように、前者は8ビット、後者は16ビットのアドレス指定となります。複数バイト書き込む場合、そのデータアドレスは順に+1されるものとします。
引数pTxDataは、書き込むデータが格納されているアドレスです。
関数の戻り値がFALSEだった場合は、何かしらの通信異常があったことを示します。


備考:
1.このプログラム例では、スレーブデバイスと通信する前に関数SetSlaveAddr_I2Cを呼ぶ必要があります。それによりスレーブアドレスが設定され、以降の通信はそのデバイスと行われます。複数のスレーブデバイスがある場合は、通信相手が変わる都度に関数SetSlaveAddr_I2Cでスレーブアドレスを切り換えます。
2.BYTE型やWORD型などの独自変数型は別のヘッダファイルで定義していますが、ここでは説明を省略します。
       BYTE型 → unsigned char型
       WORD型 → unsigned short型
       BOOL型 → int型
       FALSE = 0
       TRUE = 1

注意:
・このプログラム例は、送信完了や受信完了を何もせずに待っている構造なので、割り込みを使用しているといっても実行効率が上がるわけではありません。
・EEPROMなど、デバイスによっては決められたバイト数のページをまたがってリード/ライトできない場合があります。使用する場合はデバイスの仕様を確認してください。

ホームページに戻るトップページに戻る前ページに戻る次のページ最後のページ
■■■ 注 意 ■■■
1.ここに掲載された内容(写真,図,表などを含む)の、全てまたは一部を無断で使用しないでください。
2.ここで紹介した手法(構造や仕組み,回路,プログラムなどを含む)を使用したことにより、問題や不利益が発生したとしても一切関知しません。
3.ここで紹介したプログラム例は、正常なコンパイルおよび正常な動作を保証するものではありません。