こんにちは、ユキと申します。
RL78/G13のCSIで、バッファ空き割込みを使った連続送受信をマスタとして行うときのことについてご教授いただけないでしょうか。コンパイラはCC-RL、IDEはe2studioを使用しております。
現在、コード生成を使い、CSI21を以下のような設定で動作させようとしています。
転送モード:連続転送モードデータ長:8ビットデータ転送方向:MSBデータ送受信タイミング:タイプ4転送レート: クロック・モード:内部クロック(マスタ) ボー・レート:2000000bps通信完了割り込み優先順位:低コールバック機能設定:すべて有効
コード生成を実行した上で、自分のコードでR_CSI21_Send_Receive()を呼び出しているのですが、以下のような現象が起きています。
・1バイトのデータを送信した時は通信が成功する・2バイト以上のデータを送信するとオーバーランエラーが発生する・シングル転送モードを使った場合は、2バイト以上のデータを送信してもオーバーランエラーは発生しない
そして、r_cg_serial_user.cのr_csi21_interrupt()の、
if (g_csi21_tx_count > 0U) { if (g_csi21_tx_count != (g_csi21_send_length - 1U)) { *gp_csi21_rx_address = SIO21; gp_csi21_rx_address++; } SIO21 = *gp_csi21_tx_address; gp_csi21_tx_address++; g_csi21_tx_count--; }上記のコードを以下のように書き換えてみると、オーバーランエラーが発生しなくなりました。
if (g_csi21_tx_count > 0U)
{
if (g_csi21_tx_count != (g_csi21_send_length - 1U))
*gp_csi21_rx_address = SIO21;
gp_csi21_rx_address++;
}
SIO21 = *gp_csi21_tx_address;
gp_csi21_tx_address++;
g_csi21_tx_count--;
if (g_csi21_tx_count > 0U) { *gp_csi21_rx_address = SIO21; gp_csi21_rx_address++; SIO21 = *gp_csi21_tx_address; gp_csi21_tx_address++; g_csi21_tx_count--;}
期待通りの動作をするようになったものの、何故こうなるのか理解できておりません。上記はコード生成でできるコードなので、私の使い方が間違っている気がするのですが……
バッファオーバーランが起きる時、送信データの1バイト目を書き込んだ後の最初のバッファ空き割込みで、実際にはSDRにデータが存在するような動作をしているように見えます。
しかし、ハードウェアマニュアルを読むと、送信データの1バイト目を書き込んだ後の最初のバッファ空き割込みでは、データを受信していないし、バッファ空き割込みなのだからSDRは空いている。何もせずに2バイト目のデータをSDRに書き込んで良いというように読み取れました。
私の理解は正しいのでしょうか。そして、何故このような動作をするのでしょうか。ご助言をいただけると嬉しいです。
足りない情報がございましたら、おっしゃってください。
よろしくお願いいたします。
チョコさん
お世話になっております。少し分かったことがあるので報告いたします。
*gp_csi21_rx_address = SIO21;を割り込み処理関数の先頭に持って行ったのでは、オーバーランエラーを解消できませんでした。
これが何故か不思議で調べてみたところ、どうやら以下のような状況であるようです。
つまり、以下のうち3.と4.の間に送信するデータをSDRに書き込まなければなりませんが、これが間に合っておらずタイミングが4.の後になってしまっていると考えています。
転送速度が2Mbpsですから、1バイト転送するのに掛かる時間は4usですよね。IOポートとオシロを使って、SDRに最初の送信データが入ってから割り込み処理関数に入るまでの時間を測ったところ4us掛かっており、次の送信データをSDRに書くまでにはさらに1us程掛かっていることが分かりました。これでは確かに厳しい気がします。
CPUのクロックは高速オンチップオシレータクロックで低速メインモード、周波数は8MHzですが、この辺りは関係するでしょうか?マニュアルの「最大転送レート」から、SCIの転送速度はfCLK/4まで設定可能だと理解したのですが……。
fujitaさん
ご助言ありがとうございます。仕事用のPCで作成したプロジェクトのアップロードにはどうしても慎重になってしまいまして……考えてみます。
追加で報告です。
ユーザーマニュアルp.553のマスタ送受信(連続送受信モード時)のフローチャートを見直していると、「SIOp(=SDRmn[7:0])に送信データを書き込み」の前に割り込み許可していることに気付きました。
しかし、コード生成でできたr_cg_serial.cのR_CSI21_Send_Receive関数では、送信データを書き込んでからCSIMK21を0にしています。
そこで、送信データを書き込む前にCSIMK21を0にするように変更したところ、オーバーランエラーが発生せずに送受信できるようになりました。
コード生成でできるコードでわざわざCSIMK21を1にしているのに、こんなことをしていいのかは気になりますが……
Before
MD_STATUS R_CSI21_Send_Receive(uint8_t * const tx_buf, uint16_t tx_num, uint8_t * const rx_buf){ MD_STATUS status = MD_OK; if (tx_num < 1U) { status = MD_ARGERROR; } else { g_csi21_send_length = tx_num; /* send data length */ g_csi21_tx_count = tx_num; /* send data count */ gp_csi21_tx_address = tx_buf; /* send buffer pointer */ gp_csi21_rx_address = rx_buf; /* receive buffer pointer */ SMR11 |= _0001_SAU_BUFFER_EMPTY; CSIMK21 = 1U; /* disable INTCSI21 interrupt */ if (0U != gp_csi21_tx_address) { SIO21 = *gp_csi21_tx_address; /* started by writing data to SDR[7:0] */ gp_csi21_tx_address++; } else { SIO21 = 0xFFU; } g_csi21_tx_count--; CSIMK21 = 0U; /* enable INTCSI21 interrupt */ } return (status);}After
MD_STATUS R_CSI21_Send_Receive(uint8_t * const tx_buf, uint16_t tx_num, uint8_t * const rx_buf){ MD_STATUS status = MD_OK; if (tx_num < 1U) { status = MD_ARGERROR; } else { g_csi21_send_length = tx_num; /* send data length */ g_csi21_tx_count = tx_num; /* send data count */ gp_csi21_tx_address = tx_buf; /* send buffer pointer */ gp_csi21_rx_address = rx_buf; /* receive buffer pointer */ SMR11 |= _0001_SAU_BUFFER_EMPTY; CSIMK21 = 0U; /* enable INTCSI21 interrupt */ if (0U != gp_csi21_tx_address) { SIO21 = *gp_csi21_tx_address; /* started by writing data to SDR[7:0] */ gp_csi21_tx_address++; } else { SIO21 = 0xFFU; } g_csi21_tx_count--; } return (status);}
ユキさん、こんにちは。NoMaYと申します。チョコさんも、こんにちは。横から失礼します。ユキさんはコンパイラの最適化レベルを何に設定してコンパイルされていますか? ユキさんも書かれているように> 転送速度が2Mbpsですから、1バイト転送するのに掛かる時間は4usですよね。> CPUのクロックは高速オンチップオシレータクロックで低速メインモード、周波数は8MHzですが、ですので、1命令=1クロックとしても8命令/us × 4us = 32命令ですね。他方、チョコさんの場合は以下になりますね。> RL78/G12(24MHzの動作クロック)のCSI00をタイプ4に設定して,6Mbpsの転送速度で送信して正常に送信できています24命令/us × 1s/6Mbps × 8bit = 32命令両者同じで間に合っても良さそうですが、いかんせん32命令分しかありません。コンパイラの最適化レベルの違いで、間に合う/間に合わないが変わることもあり得そう、な気がしました。(割り込み発生時のスタックへのPUSHや復帰時のスタックからのPOPもありますので、実際は、もっと少ない命令しか実行することが出来ない、と思います。)また、32命令という少なさで処理が間に合う/間に合わないを試行錯誤する場合は、生成されたコードをアセンブラレベルで確認しながらやらないと、うまくいかないような気がします。(と言うか、もうアセンブラ記述しないと(インラインアセンブラでも可ですが)、コンパイラの生成コードが変わった時に動かなくなったりしないか、やっぱり気になってしまいます。)