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関数説明
この関数は,I2Cドライバのインスタンスを生成し,そのハンドルを取得する.
ドライバのハンドルは,以降のI2Cアクセスに必要になるため,グローバル変数などに保存しておく.
DRV_HANDLE DRV_I2C_Open(
const SYS_MODULE_INDEX index,
const DRV_IO_INTENT intent
)
引数 | 型 | 説明 |
index | SYS_MODULE_INDEX | I2CモジュールのインスタンスIDを指定
Harmonyでdefine定義されている |
intent | DRV_IO_INTENT | I2Cの動作を決定するフラグ
複数のフラグを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_BUFFER_HANDLE DRV_I2C_Transmit(
DRV_HANDLE handle,
uint16_t address,
void *writeBuffer,
size_t size,
void * callbackContext
)
引数 | 型 | 説明 |
handle | DRV_HANDLE | DRV_I2C_Openで返されたドライブハンドルを指定する |
address | uint16_t | マスターデバイスの時,データを送信する先のスレーブアドレスを設定する
スレーブデバイスの時は,ダミー(例えば0x00)の値を設定する |
writeBuffer | void* | 送信したいデータの入った変数の先頭アドレス(ポインタ) |
size | size_t | 送信したいデータのサイズ(Byte単位) |
callbackContext | void* | 実装されていない
将来の拡張用
'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_BUFFER_HANDLE DRV_I2C_Receive(
DRV_HANDLE handle,
uint16_t address,
void * buffer,
size_t size,
void * callbackContext
)
引数 | 型 | 説明 |
handle | DRV_HANDLE | DRV_I2C_Openで返されたドライブハンドルを指定する |
address | uint16_t | マスターデバイスの時,データを送信する先のスレーブアドレスを設定する
スレーブデバイスの時は,ダミー(例えば0x00)の値を設定する |
buffer | void* | 受信したデータを入れる変数の先頭アドレス(ポインタ) |
size | size_t | 受信するデータのサイズ(Byte単位) |
callbackContext | void* | 実装されていない
将来の拡張用
'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_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
)
引数 | 型 | 説明 |
handle | DRV_HANDLE | DRV_I2C_Openで返されたドライブハンドルを指定する |
bufferHandle | DRV_I2C_BUFFER_HANDLE | DRV_I2C_Transmit関数などの戻り値である,バッファハンドル |
送受信関数で得られたバッファハンドルを与えると,バッファの転送状況を返す.
バッファがエラーなしで転送された場合,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に設定されていないと動作しない.

Interrupt Modeを使用しない場合は,サンプルでI2C通信完了を待つwhile文を削除し,コールバック関数かポーリングで送受信完了を確認し,ステートマシンを組んでI2C通信する必要がある.
ねこじゃらしでねこと戯れる日々で気づいたことをつらづらと。