DMACAを利用したSCI 非同期通信で、2回目の受信ができません

お世話になっております。B.Ishiiと申します。

FITモジュールのDMACAを利用したSCI 非同期通信で、2回目の受信ができず、悩んでいます。
恐らく、私の使い方が間違っているのだと思うのですが、ご助言いただけるとありがたいです。

状況といたしまして、
・1回目の受信はできます
・送信側はDMACAを使ってうまく動作しています
・DMACAを利用しない設定では受信できます(下記のソースコードとは別のもので確認しました)

ソースコードは、以下のような感じです。
(とりあえず動作を見たいため、エラーハンドリングはしていません)

 char cpRecvData[sizeof("ABCDE")] = {0}; // "ABCDE"が送信されることは分かっている

while (1)
{
sci_err_t stSciErr = R_SCI_Receive(stSciHdl, (uint8_t *)cpRecvData, sizeof(cpRecvData));

if (stSciErr == SCI_SUCCESS)
{
break;
}

vTaskDelay(pdMS_TO_TICKS(1));
}

while (1)
{
dmaca_stat_t dmac_status;

dmaca_return_t ret =
R_DMACA_Control(DMACA_CH1, DMACA_CMD_STATUS_GET, &dmac_status);
if (ret == DMACA_SUCCESS && dmac_status.dtif_stat == true)
{
break;
}

vTaskDelay(pdMS_TO_TICKS(1));
}

デバッガで調べたところ、2回目の受信で、R_SCI_Receive()ループは抜けるのですが、R_DMACA_Control()ループは、dmac_status.dtif_stat == trueにならず、抜けられません。
cpRecvDataへの"ABCDE"格納もされていません。

■環境
・RX72N
・FreeRTOS(with IoT Libraries)
 Ver. afr-v202012.00-rx-1.0.1
・r_dmaca_rx
 Ver. 2.90
・r_sci_rx
 Ver. 4.40

Parents
  • B.Ishiiさん、こんにちは。NoMaYです。

    どうしてDMA制御関数を呼び出しているのだろうかと咄嗟に思ったのですが、もしかして、DMA/DTCを使った場合にR_SCI_Control()では送信完了や受信完了を知る方法が無い、ということでの苦肉の策なのでしょうか?

  • こんにちは。NoMaYさん

    > R_SCI_Control()では送信完了や受信完了を知る方法が無い、ということでの苦肉の策なのでしょうか?

    はい、R_SCI_Control()によるDMA転送完了を知る方法を見つけられないでいます。

    • R_SCI_Control(SCI_CMD_RX_Q_BYTES_AVAIL_TO_READ) → ×
       DMA設定では、適切な結果を得られませんでした。
       (r_sci_rxの制限事項に書かれているように、受信時、中間バッファにBYTEQを使わないので、そのBYTEQの読み込み可能なバイト数を取得できないようです。
    • R_SCI_Control(SCI_CMD_CHECK_XFER_DONE) → ×
       調歩同期式モードでは、このパラメータは使用可能となっておらず、ダメもとでやってみましたが、やはり引数不正(SCI_ERR_INVALID_ARG)で返ってきました。

    他に、R_SCI_Open()で設定されるコールバック関数で、SCI_EVT_RX_DONEイベントを監視してみました。
    1回目は、期待通り(1回)受信できていました。
    2回目は、何故か複数回受信していました。
     (しかし、dmac_status.dtif_statはtrueにならない)
     (「複数回受信」は、SCI_SUCCESSが返ってくるまで、受信前から複数回R_SCI_Receive()を呼び出しているのですが、その影響ですかね)

  • B.Ishiiさん、こんにちは。NoMaYです。

    やはりそういうことでしたか。ところで以下の使い方では駄目な気がするのです。受信未完了状態では呼び出しをスキップする、という処理が必要だと思うのです。いや、それより前に、R_SCI_Receive()でSCI_SUCCESSが返って来ない、という状況の詳細を伺った方がいいのかな、とも思いました。

    > 2回目は、何故か複数回受信していました。

    > (「複数回受信」は、SCI_SUCCESSが返ってくるまで、受信前から複数回R_SCI_Receive()を呼び出しているのですが、その影響ですかね)

    [追記]

    後で調べてみようと思ったことですけれども、、、

    ・ DMA/DTCを使うモードで受信タイムアウトが発生したら、どうやって受信動作を解除するのか?R_SCI_Close()を呼び出せば、自動的に全ての後片付けをしてくれるのだろうか?

  • こんにちは。NoMaYさん

    > R_SCI_Receive()でSCI_SUCCESSが返って来ない、という状況の詳細を伺った方がいいのかな、

    2回目の受信時は、しばらくR_SCI_Receive() = 7(SCI_ERR_XCVR_BUSY)が返ってきて、その後SCI_SUCCESSが返ってきます。
    おっしゃる通り、(1回目の受信で、dmac_status.dtif_stat == trueを検出していますが、)受信未完了状態と読み取れる戻り値です。
    なお、1回目の受信時は、ループ初回のR_SCI_Receive()呼び出しでSCI_SUCCESSが返ってきます。

  • B.Ishiiさん、こんにちは。NoMaYです。

    そういうことですか。2回目の受信の1回目のR_SCI_Receive()でSCI_ERR_XCVR_BUSYというエラーが返るのですね。そこでドキュメントを確認してみると、そもそも調歩同期式モードの場合には返らない筈のエラーのようですね。(予感的には、これ自体はドキュメントの誤記かな、という気がしますけれども。)

    そして、何度かリトライを続けていると、いずれR_SCI_Receive()でSCI_SUCCESSが返る、ということなのですね。

    r_sci_rx Ver. 4.40 ですよね。少しこちらでソースを追ってみようかと思います。

    その前に、ひとつ確認したいのですけれども、現象が出る今回は、送受信ともDMAですか?(現状のr_sci_rxでは送信と受信でやり方を同じにしておかないといけない、ということになっていましたので。)


    R_SCI_Receive()

    調歩同期式モードで、RXI 割り込みによってセットされたデータをキューから取得します。その他のモードでは、送信、または受信中でなければ、受信処理を行います。

    。。。途中省略。。。

    Return Values
    [SCI_SUCCESS] /* 要求バイト数のデータがp_dst に配置されました(調歩同期式)。受信初期化処理が完了しました(SSPI/クロック同期式)。
    [SCI_ERR_NULL_PTR] /* “hdl”がNULL です。
    [SCI_ERR_BAD_MODE] /* 指定されたモードはサポートされていません。
    [SCI_ERR_INSUFFICIENT_DATA] /* 受信キューに十分なデータがありません(調歩同期式)。
    [SCI_ERR_XCVR_BUSY] /* チャネルは現在使用中です(SSPI/クロック同期式)。

    。。。以後省略。。。


    [追記]

    すみません、私のメモです。

    ・ 送信であれば、DMA/DTC転送自体が終わっても、送信動作自体は、DMA/DTC転送が終わった直後から、あるいはその時点で送信中の送信動作が終わった後で、さらにそれ以上のFIFOがあればもっと後で、行われることもある。

    ・他方で、受信であれば、DMA/DTC転送自体が終わった=受信動作自体も終わっている、ということの筈である。

    ・ 受信オーバーランが変な風に認識されている可能性は???

    ・ DMA/DTCを使う機能は、割と最近追加された機能だと記憶しているのだが、ドキュメントが追い付いていない?

  • NoMaYさん、こんにちは。

    その前に、ひとつ確認したいのですけれども、現象が出る今回は、送受信ともDMAですか?(現状のr_sci_rxでは送信と受信でやり方を同じにしておかないといけない、ということになっていましたので。)

    はい、送信側もセットでDMA設定にしています。
    NoMaYさんが別スレッドで回答されてましたが、r_sci_rxの制限事項ですよね。

  • B.Ishiiさん、こんにちは。NoMaYです。

    r_sci_rx Ver. 4.40のソースを追ってみると、DMA転送終了割り込みでDMA転送チャンネルをクローズしています。ですので、B.Ishiiさんのやり方の、御自身がR_DMACA_Control()を呼び出すのは、そもそも危険かなと思います。なぜなら、DMA転送終了割り込みでDMA転送チャンネルがクローズされた後、クローズされたDMA転送チャンネルに対してR_DMACA_Control()を呼び出してしまう、そういう可能性があると思ったからです。

    static void sci_dmac_rx_handler(sci_hdl_t const hdl)
    {
        volatile sci_fifo_ctrl_t  *p_ctrl;

        sci_cb_args_t              args;


    #if (RX_DTC_DMACA_ENABLE & 0x02)
        if (SCI_DMACA_ENABLE == hdl->rom->dtc_dmaca_rx_enable)
        {
            if(4 == hdl->rom->dmaca_rx_channel || 5 == hdl->rom->dmaca_rx_channel || 6 == hdl->rom->dmaca_rx_channel || 7 == hdl->rom->dmaca_rx_channel)
            {
                dmaca_stat_t   stat_dmaca;
                R_DMACA_Control(hdl->rom->dmaca_rx_channel, DMACA_CMD_DTIF_STATUS_CLR, &stat_dmaca);
            }

            R_DMACA_Int_Disable(hdl->rom->dmaca_rx_channel);
            R_DMACA_Close(hdl->rom->dmaca_rx_channel);
        }
    #endif
        p_ctrl = &hdl->queue[hdl->qindex_int_rx];

     
    ですので、まず処理を以下のように変更して動作するかどうか見て頂けませんか?

    (1) コード生成機能でSCI受信をする場合の定型手段の通り(以下の手順も定型手段ですが)、受信終了フラグを自前でひとつ定義する
    (2) R_SCI_Receive()の呼び出し前に(1)のフラグをクリアする
    (3) R_SCI_Receive()のコールバック関数内で(1)のフラグをセットする
    (4) R_SCI_Receive()の呼び出し後は(1)のフラグをチェックして、受信終了となったかどうかを調べる、という判定をする

    ただ、既に以下の情報を頂いていますので、まだ何かありそうな予感はしていますけれども、ひとまず上のようにしてみて頂けませんか?

    > 他に、R_SCI_Open()で設定されるコールバック関数で、SCI_EVT_RX_DONEイベントを監視してみました。
    > 1回目は、期待通り(1回)受信できていました。
    > 2回目は、何故か複数回受信していました。

  • NoMaYさん、こんにちは。

    > ですので、まず処理を以下のように変更して動作するかどうか見て頂けませんか?

    変更して動作を見ました。
    2回目の受信で、転送完了確認ループは抜けるようになりましたが、受信バッファcpRecvDataは""(初期値のまま空)でした。
    そして、3回目の受信で、R_SCI_Receive()ループを抜けなくなりました。

    変更後のソースコードは、以下のような感じです。

    static volatile bool m_bRxDone;

    // R_SCI_Open()で設定した割り込み関数
    static void SciCallback(void *vpArgs)
    {
    sci_cb_args_t *stpArgs = (sci_cb_args_t *)vpArgs;

    switch (stpArgs->event)
    {
    case SCI_EVT_RX_DONE:
    m_bRxDone = true;
    break;
    default:
    break;
    }
    }

    // タスクから定期的に呼ばれる関数
    {
    char cpRecvData[sizeof("ABCDE")] = {0}; // "ABCDE"が来ることは分かっている

    m_bRxDone = false;

    // R_SCI_Receive()ループ
    while (1)
    {
    sci_err_t stSciErr = R_SCI_Receive(
    stSciHdl, (uint8_t *)cpRecvData, sizeof(cpRecvData));

    if (stSciErr == SCI_SUCCESS)
    {
    break;
    }

    printf("R_SCI_Receive() = %d\n", stSciErr);
    vTaskDelay(pdMS_TO_TICKS(100));
    }

    // 転送完了確認ループ
    while (m_bRxDone == false)
    {
    vTaskDelay(pdMS_TO_TICKS(100));
    }

    printf("Rx: \"%s\"\n", cpRecvData);
    }

    ログは、以下のような感じです。

    Rx: "ABCDE"
    R_SCI_Receive() = 7
    R_SCI_Receive() = 7
    ...(上のログを繰り返し)
    Rx: ""
    R_SCI_Receive() = 7
    R_SCI_Receive() = 7
    ...(上のログを繰り返し)
  • B.Ishiiさん、こんにちは。NoMaYです。

    まず、sizeof("ABCDE") の値として 5 を期待されていると思いますが 6 です。そのせいで、期待した動作になっていなかった、ということはないでしょか?

Reply
  • B.Ishiiさん、こんにちは。NoMaYです。

    まず、sizeof("ABCDE") の値として 5 を期待されていると思いますが 6 です。そのせいで、期待した動作になっていなかった、ということはないでしょか?

Children