こんにちは、ユキと申します。
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に書き込んで良いというように読み取れました。
私の理解は正しいのでしょうか。そして、何故このような動作をするのでしょうか。ご助言をいただけると嬉しいです。
足りない情報がございましたら、おっしゃってください。
よろしくお願いいたします。
チョコさん
お返事ありがとうございます。
最後に送信完了割り込みが来ることを期待して割り込みタイミングを書き換えるコードになっているのですが、送信完了割り込みが発生しない場合があるということですね。
ご助言を基に、残り転送データ数が0の時にSSRレジスタをポーリングで見て、TSFビットが0になったらデータを読み出して抜けるようにしてみましたが、まだオーバーランエラーが発生しております。以下が変更後の割り込み処理のコードです。間違いがあると思うのですが、ご指摘いただけないでしょうか。
static void __near r_csi21_interrupt(void){ volatile uint8_t err_type; err_type = (uint8_t)(SSR11 & _0001_SAU_OVERRUN_ERROR); SIR11 = (uint16_t)err_type; if (1U == err_type) { r_csi21_callback_error(err_type); /* overrun error occurs */ } else { 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--; } else { r_csi21_callback_sendend(); /* complete send */ *gp_csi21_rx_address = SIO21; gp_csi21_rx_address++; while ((SSR11 & _0040_SAU_UNDER_EXECUTE) != 0U) { NOP(); } *gp_csi21_rx_address = SIO21; r_csi21_callback_receiveend(); /* complete receive */ } }}
チョコさん 多くのご助言ありがとうございます。 ■ソースコードについて データの読み出しを間に合わせるために、読み出しを処理の頭でやるようにしたということですね。 しかし、これでもオーバーランエラーは解消しませんでした。 データの読み出し(*gp_csi21_rx_address = SIO21;)の場所を変えて、 if (1U == err_type) { r_csi21_callback_error(err_type); /* overrun error occurs */ } else { if (g_csi21_tx_count > 0U) { *gp_csi21_rx_address = SIO21; ← ここでデータを読み出す if (g_csi21_tx_count != (g_csi21_send_length - 1U)) { gp_csi21_rx_address++; } SIO21 = *gp_csi21_tx_address; gp_csi21_tx_address++; g_csi21_tx_count--; } ...(以下省略) こちらのようにするとエラーが発生しなくなります。 しかし、これは単にSDRへのデータの取り込みとデータの読み出しのタイミングが 絶妙に噛み合っただけということですよね。 ちなみに、レジスタバンクは既に使用しております。 ■追加のコメントについて ・割り込みの優先度を高にする ・CSI21以外の割り込み処理の先頭でEI()を実行する を試してみましたが、それでも駄目でした。DMAを使うしかないでしょうか……
チョコさん そうですよね…実際のプロジェクトはちょっと公開できないので、 いただいたヒントを基にもう少しこちらで調べてみます。色々とご助言くださり、ありがとうございました。何か分かりましたら、こちらに投稿させていただきます。
使っているのはCC-RL78です。 気になるコードに展開される可能性というのには当てはまらなさそうですね。
お世話になっております。少し分かったことがあるので報告いたします。
*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);}