RL78G10をI2Cのマスターとして設定する場合は特に大きな問題は出ず解説を書くまでも
無いと思っていました。今度はスレーブとして利用しようと思いコード生成で設定したら
・自局より小さいアドレスに勝手に反応する
・エラーが出るとバスを占拠して何もできなくなる
・ACKが正しく検出できない事がある
等不可解な現象に遭遇しました。
今回はこれら現象を記録しておきます。
対策は次回の対策案編を参照ください。
関連:
・
I2Cとは・
コード生成(準備編)・
コード生成(UART編)・
スレーブ対策案編基本的な話
RL78G10にはI2C向けに使える端子としてIIC系(SCL,SDA)とIICA系(SCLA,SDAA)の
2系統があります。で、IIC系はスレーブとして設定できないらしく、スレーブ
として使いたい場合は必然的にIICA系を利用する必要があります。
秋月で購入可能なRL78 R5F10Y16(10pin)はI2Cをスレーブに設定できません。
ということで今回は主にRL78 R5F10Y47(16pin)の内容となります。
コード生成の準備が出来たら コード生成 > 周辺機能 > シリアル・インターフェースIICAから
転送モードをスレーブに設定してコード生成します。
スレーブ設定でコード生成される中身
r_cg_iica.cの中身
void R_IICA0_Create(
void);
void R_IICA0_Stop(
void);
void R_IICA0_Slave_Send(uint8_t * const tx_buf, uint16_t tx_num);void R_IICA0_Slave_Receive(uint8_t * const rx_buf, uint16_t rx_num);この辺は基本的に
UARTとほぼ同じなので省略します。
r_cg_iica_user.cの中身
static void __near r_iica0_interrupt(
void)
割り込み時に実行されます。
static void iica0_slavehandler(void)割り込み時にスレーブ設定だと実行されます。
I2C関連の割り込みは
何であれこの関数が実行されるようです。
static void r_iica0_callback_slave_error(MD_STATUS flag)エラー発生時に実行されます。
static void r_iica0_callback_slave_receiveend(void)受信完了時(
受信バッファが全て埋まった時)に実行されます。
static void r_iica0_callback_slave_sendend(void)送信完了時(
送信バッファを全て送信しきった時)に実行されます。
謎現象
さて、本題
R_IICA0_Slave_Sendと
R_IICA0_Slave_Receiveさえ設定すればあとは
ちょっと編集するだけで使えるようになるつもりでした。
ところが不可解な現象が発生して中々使える様になりません。
しばらく
2chのオスロスコープとにらめっこしてました。
なお、開発環境は
・CS+ for CC V8.04.00 [09 Jun 2020]
・統合開発環境 フレームワーク:V9.04.00.06 [12 May 2020]
・RL78ビルド・ツールCC-RL用プラグイン:V8.02.00.00 [25 Mar 2019]
・RL78/G10コードライブラリ:V1.05.03.02 [20 Nov 2019]
となっております。
以下、遭遇した謎現象
どう考えてもおかしい
・
自局のアドレスより小さいアドレスに反応してエラーになる使いづらい/なんかおかしい
・コード生成で指定する自局アドレスが送受信同時設定なのに8bit表記
・
基本的にエラー状態になると有無を言わさずバスを占拠(クロックストレッチ)する・スレーブからの送信時にマスターがデータ毎にACKを返さないとエラーになる
・マスターからのACKを正しく受け取れない
・送信受信のバッファ数と送受信数が一致してないと処理が中途半端になる
1つずつ現象を説明します。
<自局のアドレスより小さいアドレスに反応してエラーになる>
とっても危険な仕様(バグ?)
I2C通信ではバスに複数のデバイスがつなっがっており、マスターからアドレスを指定
されたデバイスのみが通信を行います。したがって、マスターが指定したアドレスと
自局のアドレスが一致しない場合は通常反応させません。RL78G10の場合、
マスターが
指定したアドレスが
自局のアドレスより小さい場合
何故か反応してエラーになります。
ラズパイで利用可能なI2Cのデバイスサーチコマンド
sudo i2cdetect -y 1はアドレスの
小さい方から順に通信テストを行い、反応のあったアドレスを列挙しますが、RL78G10は
自局より小さなアドレス対して反応するので
自局のアドレスが指定するまでひたすらエラーを吐き続けます。これがバス占拠問題と合わさって色々やらかしてくれます。
<コード生成で指定する自局アドレスが送受信同時設定なのに8bit表記>
比較的どうでもいい内容です。
I2Cの説明で書きましたがI2Cのアドレスは
・
共通アドレス7bit+読書判定で表記
・
書込み用アドレス8bitと
読取り用アドレス8itを
別々に表記の2パターンあります。
BXM055のマニュアルは7bit表記になっており、DRV8830の秋月取説では読書分けた
8bitの表記になっています。
RL78G10のコード生成では設定の際にアドレスを1つだけ割り振りますが、
1つだけなのに8bit表記なっています。初期設定値16(0x10)の場合、書込みは16(0x10)ですが、
読取りは17(0x11)を使います。7bit表記では8(0x08)に相当します。
<基本的にエラー状態になると有無を言わさずバスを占拠する>
I2Cの説明で少し書きましたが、基本的にクロックはマスターから送信されるのに
対して、スレーブがクロックに0を送信することでマスターに待機させるクロック
ストレッチ機能があります。クロックストレッチ中は通信中断のためにバスを占拠
するので、マスターから送信されるクロックが無視されるのでマスターからの指令は
何処にも伝わらなくなります。
RL78G10のI2Cでは
通信にエラーが発生すると有無を言わさずクロックストレッチする様になっています。バスが占拠されるのでマスターがこの状態を解除すること
はできません。
上記の小さいアドレスに反応する仕様と合わさると
・マスターが
自局と関係ないアドレスを指定する
・
自局と関係ないけど
エラーなのでバスを占拠する
・バスが占拠されるのでマスターとその他のデバイスの通信まで阻害する
と非常に迷惑な挙動になります。
従って、ラズパイの
sudo i2cdetect -y 1を実行すると
1つ目のアドレスが指定された時点でバスが占拠されてしまい、
デバイスは何も検出されません。
エラーだとクロックに0を送信し続けますが、通信が中途半端な状態だとデータに
0を送信し続ける場合もあります。
<スレーブからの送信時にマスターがデータ毎にACKを返さないとエラーになる>
スレーブからマスターに向けてデータを送信する場合、マスターは
1Byte受信する度に受信できたことを通知するために
ACKを返信します。ラズパイと通信すると
受信確認のACKが正しく検出できない様で、とりあえず1Byte分はデータを受け取れる
のですが、1Byte目を送信した時点でエラー→バス占拠となり以降の通信ができな
くなります。エラーを解除しても一旦エラーになったら通信を最初からやり直す
ことしかできそうにありません。
<マスターからのACKを正しく受け取れない>
これはラズパイのI2Cとの相性の問題の様です。
ラズパイをマスターにしてマスター受信時に
ラズパイ側はACKを返信しているのです
が、
RL78がACKを受け取れていない様です。
<送信受信のバッファ数と送受信数が一致してないと処理が中途半端になる>
コードされる受信完了処理と送信完了処理はそれぞれ
受信バッファが埋まった時と
送信バッファを使い切った時にだけ実行されます。
とりあえず送受信バッファを使い切った場合の処理として
・
r_iica0_callback_slave_receiveendへ
受信完了処理を追加・
r_iica0_callback_slave_sendendへ
送信完了処理を追加とします。
送受信完了処理の内容は
・受信したデータを別の場所へコピー
・受信バッファをクリア
・先頭から受信できるように受信再設定(
R_IICA0_Slave_Receive)
・先頭から送信できるように送信再設定(
R_IICA0_Slave_Send)
などとします。
この設定だけでは送受信バッファを使い切らない状態でマスターが通信を終了すると
中途半端な状態になって、次に通信を行う際にバッファの途中から通信が再開します。
この仕様は例えばこんな感じの動作になります
・スレーブとして
最大受信バッファを
8Byteに設定
・マスターから
2Byte送信→2Byte分の送信完了しても受信完了関数は実行されない
・再度マスターから
2Byte送信→上書きされずに
4Byte分溜まる・
合計8Byte分受信すると受信完了完了関数が実行される
送信の場合も同様で、マスターが2Byte分でクロックを止めると次回は3Byte目から
送信が開始されます。
そのままの処理だと
どこで受信が終わったのか分からないため
2Byteが4回送られて
きたの
8Byteが1回で送られてきたのか判別できません。通信間隔が長い場合は
バスの状態を逐次監視して
バスが空いたら通信終了判定を行えばいいのですが、連続して
複数のデータを受信するとどうしようもありません。
さらに送信処理では
・スレーブとして
最大送信バッファを
8Byteに設定
・マスターから
2Byteだけ受信・マスターが通信終了
とするとI2Cの
仕様上ありえる(NACK)のに
エラーになります。
通信によってデータ数が変わるのにこの仕様はあんまりです。
次回はこれらの対策案を検討します。