PIC32+HarmonyでI2C通信のやり方

Harmony Ver 1.07.01で記述

前書き

HarmonyでDynamicタイプのドライバ関数が追加されたので,その使い方を解説する.
まだ頻繁にバージョンアップしているので,今後変更になる可能性が高い.
基本的なI2C通信の仕方は,以下の順序となる.

  • DRV_I2C_Initialize関数で,I2Cモジュールの初期化を行う(system_init.c内にHarmonyが自動生成している)
  • DRV_I2C_Open関数で,I2Cモジュールを開き,ハンドル取得
  • DRV_I2C_Transmit関数で,I2Cデータ送信(バッファハンドル取得)
  • DRV_I2C_Receive関数で,I2Cデータ受信(バッファハンドル取得)
  • DRV_I2C_TransferStatusGet関数で,上記バッファハンドルのステータス(通信完了など)を取得
    必要に応じて通信完了まで待機

ここで,しっかりハマるポイントがある.
DRV_I2C_Transmit関数などは,引数に送受信先のアドレスを設定し,実際に送受信を行うわけだが,I2C通信はそのアドレスは7bitまたは10bitである.
7bitアドレスの場合,8bitアドレスの上位7bitがアドレスで,下位1bitは送受信先のデータの方向を指定する(’0’=Write,’1’=Read).
DRV_I2C_Transmit関数は送信するので,7bitアドレス+’0’の8bitとなることを期待するが,実際は引数に設定された8bitすべてを送信先のアドレスとして設定してしまう.
例えば,スレーブアドレス0x20の場合,送信データは0x40となるべきである.よって,スレーブアドレスには0x40を指定するが,誤って0x41を設定すると,送信の関数であるが命令としてはスレーブからの受信(Read)となってしまい,DRV_I2C_Transmit関数は来ないデータを待ち続けてハングアップしてしまう.
DRV_I2C_Transmit関数内部で7bitマスク+1bit左シフトしてくれたほうが,このようなミスがなくせるのに・・・.

DRV_I2C_Open関数

DRV_I2C_Open関数説明

概要

この関数は,I2Cドライバのインスタンスを生成し,そのハンドルを取得する.
ドライバのハンドルは,以降のI2Cアクセスに必要になるため,グローバル変数などに保存しておく.

定義

DRV_HANDLE DRV_I2C_Open(
	const SYS_MODULE_INDEX index,
	const DRV_IO_INTENT intent
)

引数

引数説明
indexSYS_MODULE_INDEXI2CモジュールのインスタンスIDを指定
Harmonyでdefine定義されている
intentDRV_IO_INTENTI2Cの動作を決定するフラグ
複数のフラグをOR'|'で接続し指定できる

戻り値

説明
DRV_HANDLEインスタンス生成できれば,ハンドルを返す
生成できなければ,DRV_HANDLE_INVALIDを返す
もしDRV_HANDLE_INVALIDが返ってくるなら,以下の内容に問題がある可能性が高い.

  • I2CモジュールのIDが正しくないか存在しない
    (index引数の値)
  • すでにI2Cモジュールが別のドライバで開かれ,さらに排他的モードが設定されている
  • I2Cモジュールが初期化されていないか,無効化されている
    (Harmonyの生成コードでDRV_I2C_Initialize関数が実行されていない)

備考

DRV_I2C_Closeが呼ばれるまで,返されたハンドルが有効.
ドライバがブロック動作を受け付ける場合,この関数はブロック動作(処理完了まで待つ)ことがある.
I2Cモジュールへの操作は,intent引数の指示によって決まる.

使用例

DRV_HANDLE hi2c;

hi2c = DRV_I2C_Open(DRV_I2C_INDEX_0, DRV_IO_INTENT_READWRITE | DRV_IO_INTENT_NONBLOCKING);
if(hi2c == DRV_HANDLE_INVARID)
{
	/* ドライバハンドルが取得できなかった */
}

DRV_I2C_Transmit関数

DRV_I2C_Transmit関数説明

概要

自身がマスターデバイスの時,マスターデバイスからスレーブデバイスにデータを送る.
または,自身がスレーブデバイスの時,スレーブデバイスからマスターデバイスにデータを送る.

定義

DRV_I2C_BUFFER_HANDLE DRV_I2C_Transmit(
	DRV_HANDLE handle, 
	uint16_t address,
	void *writeBuffer,
	size_t size,
	void * callbackContext
)

引数

引数説明
handleDRV_HANDLEDRV_I2C_Openで返されたドライブハンドルを指定する
addressuint16_tマスターデバイスの時,データを送信する先のスレーブアドレスを設定する
スレーブデバイスの時は,ダミー(例えば0x00)の値を設定する
writeBuffervoid*送信したいデータの入った変数の先頭アドレス(ポインタ)
sizesize_t送信したいデータのサイズ(Byte単位)
callbackContextvoid*実装されていない
将来の拡張用
'NULL'を指定する

戻り値

説明
DRV_I2C_BUFFER_HANDLEバッファハンドルを返す
ハンドルが得られないときは'NULL'

備考

この関数は,使用されるとバッファハンドルが返される.バッファハンドルが無効の時は’NULL’となる.
自身がマスターデバイスで,スレーブデバイスがアドレスバイトかデータバイトでNACKを返したとき,この関数は送信の再試行を行わない.
このときマスターデバイスはSTOPシーケンスを実行し,DRV_I2C_TransferStatusGet関数は,バッファステータスをDRV_I2C_BUFFER_EVENT_ERRORとして返す.
指定されたデータサイズ(バイト数)がスレーブデバイスに送信されたときは,バッファステータスをDRV_I2C_BUFFER_EVENT_COMPLETEとして返す.

使用例

DRV_HANDLE hi2c;
DRV_I2C_BUFFER_HANDLE bhi2c;
uint8_t data[5];

hi2c = DRV_I2C_Open(DRV_I2C_INDEX_0, DRV_IO_INTENT_READWRITE | DRV_IO_INTENT_NONBLOCKING);
if(hi2c == DRV_HANDLE_INVARID)
{
	/* ドライバハンドルが取得できなかった */
}
else
{
	/* ドライバハンドル取得できたので,I2C送信する */
	bhi2c = DRV_I2C_Transmit(hi2c, 0x1e, data, 5, NULL);
	while(DRV_I2C_TransferStatusGet(hi2c, bhi2c) != DRV_I2C_BUFFER_EVENT_COMPLETE); /*I2C送信完了まで待つ*/
}

DRV_I2C_Receive関数

DRV_I2C_Receive関数説明

概要

自身がマスターデバイスの時,スレーブデバイスからマスターデバイスへデータを受ける.
または,自身がスレーブデバイスの時,マスターデバイスからスレーブデバイスへデータを受ける.

定義

DRV_I2C_BUFFER_HANDLE DRV_I2C_Receive(
	DRV_HANDLE handle, 
	uint16_t address, 
	void * buffer, 
	size_t size, 
	void * callbackContext
)

引数

引数説明
handleDRV_HANDLEDRV_I2C_Openで返されたドライブハンドルを指定する
addressuint16_tマスターデバイスの時,データを送信する先のスレーブアドレスを設定する
スレーブデバイスの時は,ダミー(例えば0x00)の値を設定する
buffervoid*受信したデータを入れる変数の先頭アドレス(ポインタ)
sizesize_t受信するデータのサイズ(Byte単位)
callbackContextvoid*実装されていない
将来の拡張用
'NULL'を指定する

戻り値

説明
DRV_I2C_BUFFER_HANDLEバッファハンドルを返す
ハンドルが得られないときは'NULL'

備考

この関数は,使用されるとバッファハンドルが返される.バッファハンドルが無効の時は’NULL’となる.
自身がマスターデバイスで,スレーブデバイスがアドレスバイトでNACKを返したとき,この関数は送信の再試行を行わない.
このときマスターデバイスはSTOPシーケンスを実行し,DRV_I2C_TransferStatusGet関数は,バッファステータスをDRV_I2C_BUFFER_EVENT_ERRORとして返す.
指定されたデータサイズ(バイト数)がスレーブデバイスから受信できたときは,バッファステータスをDRV_I2C_BUFFER_EVENT_COMPLETEとして返す.

使用例

DRV_HANDLE hi2c;
DRV_I2C_BUFFER_HANDLE bhi2c;
uint8_t data[5];

hi2c = DRV_I2C_Open(DRV_I2C_INDEX_0, DRV_IO_INTENT_READWRITE | DRV_IO_INTENT_NONBLOCKING);
if(hi2c == DRV_HANDLE_INVARID)
{
	/* ドライバハンドルが取得できなかった */
}
else
{
	/* ドライバハンドル取得できたので,I2C送信する */
	bhi2c = DRV_I2C_Receive(hi2c, 0x1e, data, 5, NULL);
	while(DRV_I2C_TransferStatusGet(hi2c, bhi2c) != DRV_I2C_BUFFER_EVENT_COMPLETE); /*I2C受信完了まで待つ*/
}

DRV_I2C_TransferStatusGet関数

DRV_I2C_TransferStatusGet関数説明

概要

この関数は,DRV_I2C_Transmit関数などで得られたバッファハンドルから,そのバッファの転送状態(ステータス)を取得することができる.
戻り値がDRV_I2C_BUFFER_EVENT_COMPLETEの時は,データが正常に送受信されたことを意味し,DRV_I2C_BUFFER_EVENT_ERRORの時はデータ送受信に異常があったことを意味する.

定義

DRV_I2C_BUFFER_EVENT DRV_I2C_TransferStatusGet(
	DRV_HANDLE handle,
	DRV_I2C_BUFFER_HANDLE bufferHandle
)

引数

引数説明
handleDRV_HANDLEDRV_I2C_Openで返されたドライブハンドルを指定する
bufferHandleDRV_I2C_BUFFER_HANDLEDRV_I2C_Transmit関数などの戻り値である,バッファハンドル

戻り値

説明
DRV_I2C_BUFFER_EVENT現在の転送の状態を示すステータス

備考

送受信関数で得られたバッファハンドルを与えると,バッファの転送状況を返す.
バッファがエラーなしで転送された場合,DRV_I2C_BUFFER_EVENT_COMPLETEが返される.
エラーが存在する場合,DRV_I2C_BUFFER_EVENT_ERRORが返される.

使用例

DRV_HANDLE hi2c;
DRV_I2C_BUFFER_HANDLE bhi2c;
uint8_t data[5];

hi2c = DRV_I2C_Open(DRV_I2C_INDEX_0, DRV_IO_INTENT_READWRITE | DRV_IO_INTENT_NONBLOCKING);
if (hi2c == DRV_HANDLE_INVARID)
{
	/* ドライバハンドルが取得できなかった */
}
else
{
	/* ドライバハンドル取得できたので,I2C送信する */
	bhi2c = DRV_I2C_Receive(hi2c, 0x1e, data, 5, NULL);
	while (DRV_I2C_TransferStatusGet(hi2c, bhi2c) != DRV_I2C_BUFFER_EVENT_COMPLETE); /*I2C受信完了まで待つ*/
}

注意

ここで紹介したプログラム記述例は,HarmonyにおけるI2Cの設定で,Interrupts Modeに設定されていないと動作しない.
I2c_settings
Interrupt Modeを使用しない場合は,サンプルでI2C通信完了を待つwhile文を削除し,コールバック関数かポーリングで送受信完了を確認し,ステートマシンを組んでI2C通信する必要がある.


コメントを残す

メールアドレスが公開されることはありません。

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください