Software PKG で SCIFAn を半2重通信したい (RZ/A2M)

JiGoRo です。 かふぇルネの皆様へ、教えてください。

RZ/A2Mでソフトウェアをコーディングしてます。

Software Package v4 を FreeRTOS上にて使わせていただいておりますが、SCIFAn のUARTを用いて、半二重通信を実装しようとして困ってます。

困っている点は、次のステップの実装です:

 1)データを送信する (ここはOK)

 2)データ送信完了後、特定のGPIOポートの状態を1から0に反転させる (*)

 3)2に続いてデータを受信する

ここで、3については、1と同時に受信開始しても良いので、動作上は実装できるのですが、2のデータ送信後にポート状態を変更したいという動作が実装できていません。

具体的な目的は、RS485の半二重通信制御となります。 外付けICのRS485 I/Fチップの送受信モードを切り替えたいのです。

現時点は DMAは併用しておりません。 併用したほうが簡単に実装できるのであれば、DMAを使うことは何ら問題ございません。

Smart Configuratorにて、r_scifa/ にコード生成されたものがありますが、送信完了のAPI(もしくはコールバック関数)が見当たらず、上記2の実装が難航しています。

どなたか、ご存じの方、ご教授いただけると助かります。

  • JiGoRoさん、こんにちは。NoMaYです。#「RZ/A2MのDMACについて教えてください」に続いて2度目ですね。

    スマートコンフィグレータで「送信完了のAPI(もしくはコールバック関数)が見当たらず」とはどういうことなのかなぁ、と思い、始めてRZスマートコンフィグレータを触ってみたのですが、これって、スマートコンフィグレータの皮を被ったFITコンフィグレータみたいなものなのですね。

    生成されるコードは、RL78のコード生成機能やRXスマートコンフィグレータのコード生成機能と全然違う発想によるコードであって、予想通りFITライブラリの発想によるコードっぽいですね。

    ソースコードを追ってみたのですが、基本的に、それは出来ません、というもののようでした。(もしくは、R_SCIFA_GetTXEIState()でポーリングして下さい、という設計思想かも知れませんが、今回の用途では、いつタスク切り替えが起きてどれだけ遅延するか分からないポーリング方法は不向きな気がします。)

    ただ、r_scifa_hld_prv.cに以下のコードがあったのですが、このtransmit endのコードを真似て、独自の送信終了割り込みハンドラを上書き登録することで、割り込みで送信終了処理を実行出来る可能性があるかも、と思いました。

    generate/sc_drivers/r_scifa/src/hld/r_scifa_hld_prv.c

    /**
     * @brief setup_interrupts
     * @return None
     */
    static int_t setup_interrupts(int_t channel, const scifa_config_t *p_cfg)
    {
        if ((channel > R_PRV_SCIF_LAST_CHANNEL) || (channel < 0))
        {
            return DRV_ERROR;
        }

        /* Set up interrupts */
        switch (channel)
        {
            case 0: /* channel 0 */
            {
                /* transmit */
                R_INTC_RegistIntFunc(INTC_ID_SCIFA_TXI0, TXI_handler);
                R_INTC_Enable(INTC_ID_SCIFA_TXI0);
                R_INTC_SetPriority(INTC_ID_SCIFA_TXI0, p_cfg->txi_priority);

                /* transmit end */
                R_INTC_RegistIntFunc(INTC_ID_SCIFA_TEI0DRI0, TXEI_handler);
                R_INTC_Enable(INTC_ID_SCIFA_TEI0DRI0);
                R_INTC_SetPriority(INTC_ID_SCIFA_TEI0DRI0, p_cfg->tei_dri_priority);

                /* receive */
                R_INTC_RegistIntFunc(INTC_ID_SCIFA_RXI0, RXI_handler);
                R_INTC_Enable(INTC_ID_SCIFA_RXI0);
                R_INTC_SetPriority(INTC_ID_SCIFA_RXI0, p_cfg->rxi_priority);
                break;
            }

            case 1: /* channel 1 */
            {
                途中省略
            }

            case 2: /* channel 2 */
            {
                途中省略
            }

            case 3: /* channel 3 */
            {
                途中省略
            }

            case 4: /* channel 4 */
            {
                途中省略
            }
        }

        return (DRV_SUCCESS);
    }
    /******************************************************************************
     * End of Function setup_interrupts
     ******************************************************************************/

    なお、デフォルトのTXEI_handlerは以下の通りで、独自の送信終了割り込みハンドラでも同じ様にR_SCIFA_StopTransmit()を呼び出してやれば良さそうな気がします。

    generate/sc_drivers/r_scifa/src/hld/r_scifa_hld_prv.c

    /**
     * @brief TXEI_handler handles TXEI interrupts
     *
     * @param[in] sense: Not used, but required by current INTC driver
     *                   Expected to remove following new INTC driver
     *
     * @return None
     */
    static void TXEI_handler(uint32_t sense)
    {
        (void) sense;
        int_t channel;                              /* Channel search index */
        volatile int_t active_int = -1;             /* Active Interrupt channel */

        for (channel = 0; channel < SCIFA_CFG_LLD_NUM_CHANNELS; channel++)
        {
            if (R_SCIFA_GetTXEIState(channel))
            {
                active_int = channel;
                break;
            }
        }

        if (active_int >= 0)
        {
            /* clears transmit and transmit end interrupt enable flags */
            R_SCIFA_StopTransmit(active_int);
        }
    }
    /******************************************************************************
     * End of Function TXEI_handler
     ******************************************************************************/

    以下、こちらで触ってみたRZスマートコンフィグレータのSCIFAの設定画面の画面コピーです。



    [追記]

    注意点として、送信側も8Kバイトのリングバッファ構造になっていますが、リングバッファフルの時にタスク切り替えが起きるようになっていて、この間に望まれない送信終了割り込みが発生する危険性がありますので、リングバッファフルが起きないように送信するようにする必要がありそうです。(一度に送信するデータは8Kバイト以下とし、送信毎に送信終了を待つ、ようにすればよいと思います。)

    generate/sc_drivers/r_scifa/src/hld/r_scifa_hld_prv.c

    /**
     * @brief start_tx starts data transmission
     *
     * @param[in] channel Channel to send data.
     *
     * @param[in] p_buffer Location of  data to send.
     *
     * @param[in] ui_count Length of data to send.
     *
     * @retval  0 Success
     * @retval -1 Fail
     */
    static void start_tx(int_t channel, const uint8_t *p_buffer, uint32_t ui_count)
    {
        size_t free_space;
        uint32_t i;

        /* we mustn't be interrupted while manipulating the buffer */
        R_COMPILER_DisableInterrupts();

        /* find out how much free space there is in the buffer */
        free_space = cbFree(gs_ch_ctrl[channel].p_tx_cbuff);

        /* copy as much of the data as possible into the transmit buffer */
        for (i = 0; (i < ui_count) && (i < free_space); i++)
        {
            cbPut(gs_ch_ctrl[channel].p_tx_cbuff, p_buffer[i]);
        }

        /* we're done with the buffer */
        R_COMPILER_EnableInterrupts();

        /* set SCR.TIE bit to start transmission */
        R_SCIFA_StartTXI(channel);

        /* put the remaining data into the buffer as space becomes available */
        while (i < ui_count)
        {
            /* we mustn't be interrupted while manipulating the buffer */
            R_COMPILER_DisableInterrupts();

            free_space = cbFree(gs_ch_ctrl[channel].p_tx_cbuff);

            if (free_space > 0)
            {
                /* fill up the free space */
                while ((i < ui_count) && (free_space > 0))
                {
                    cbPut(gs_ch_ctrl[channel].p_tx_cbuff, p_buffer[i]);
                    i++;
                    free_space--;
                }

                /* we're done with the buffer */
                R_COMPILER_EnableInterrupts();

                /* just in case the buffer emptied completely */
                R_SCIFA_StartTXI(channel);
            }
            else
            {
                /* we're done with the buffer */
                R_COMPILER_EnableInterrupts();

                /* hand over CPU cycles to other tasks while we wait */
                R_OS_TaskSleep(5);
            }
        }
    }
    /******************************************************************************
     * End of Function start_tx
     ******************************************************************************/

     

  • NoMaYさん、ありがとうございます。 大変助かります。
    TXEI_handler() での処理で実装するか、乗っ取るか検討してみます。
    RXマイコンなどの Smart Configurator のコード生成だと、コールバック関数があり、それを作っておけば、再度 コード生成しなおしても消えない仕掛けがあったのですが、RZマイコンの場合、ご指摘の通りFITのようなので、そのあたりのケアが緩い感じがしました。
    8kバイト以上のパケットは想定していないので、今回の場合は良さそうです。
    もし、超えそうならループバッファを大きくする方向で検討でしょうかね。
    いろいろ情報ありがとうございました。
  • JiGoRoさん、こんにちは。NoMaYです。

    これは拙いかな、と気になるところがありました。送信リングバッファに充填する時にずっと割り込み禁止にしてしまっているようですね。最大8Kバイトを処理する時間が割り込み禁止になりそうなのですが、送信割り込み動作も禁止されるので、この期間中([追記] 正しくは、この期間が終了して、割り込み禁止が解除された時)に、自チャンネルや他チャンネルで望まれない送信終了割り込みが発生する危険性があるような気がしてきました。そして、送信終了割り込みで、送信を止める動作をすると思われるR_SCIFA_StopTransmit()を呼び出していますので、以後、送信動作が破綻してしまうかも知れない気がしてきました。(自分の印象ですが、もともとの処理が果たして正しいのか常に気にしながら作業した方が良いかも知れない、という気がします。)

    generate/sc_drivers/r_scifa/src/hld/r_scifa_hld_prv.c

    /**
     * @brief start_tx starts data transmission
     *
     * @param[in] channel Channel to send data.
     *
     * @param[in] p_buffer Location of  data to send.
     *
     * @param[in] ui_count Length of data to send.
     *
     * @retval  0 Success
     * @retval -1 Fail
     */
    static void start_tx(int_t channel, const uint8_t *p_buffer, uint32_t ui_count)
    {
        size_t free_space;
        uint32_t i;

        /* we mustn't be interrupted while manipulating the buffer */
        R_COMPILER_DisableInterrupts();

        /* find out how much free space there is in the buffer */
        free_space = cbFree(gs_ch_ctrl[channel].p_tx_cbuff);

        /* copy as much of the data as possible into the transmit buffer */
        for (i = 0; (i < ui_count) && (i < free_space); i++)
        {
            cbPut(gs_ch_ctrl[channel].p_tx_cbuff, p_buffer[i]);
        }

        /* we're done with the buffer */
        R_COMPILER_EnableInterrupts();

        /* set SCR.TIE bit to start transmission */
        R_SCIFA_StartTXI(channel);

        /* put the remaining data into the buffer as space becomes available */
        while (i < ui_count)
        {
            /* we mustn't be interrupted while manipulating the buffer */
            R_COMPILER_DisableInterrupts();

            free_space = cbFree(gs_ch_ctrl[channel].p_tx_cbuff);

            if (free_space > 0)
            {
                /* fill up the free space */
                while ((i < ui_count) && (free_space > 0))
                {
                    cbPut(gs_ch_ctrl[channel].p_tx_cbuff, p_buffer[i]);
                    i++;
                    free_space--;
                }

                /* we're done with the buffer */
                R_COMPILER_EnableInterrupts();

                /* just in case the buffer emptied completely */
                R_SCIFA_StartTXI(channel);
            }
            else
            {
                /* we're done with the buffer */
                R_COMPILER_EnableInterrupts();

                /* hand over CPU cycles to other tasks while we wait */
                R_OS_TaskSleep(5);
            }
        }
    }
    /******************************************************************************
     * End of Function start_tx
     ******************************************************************************/

    上と関連して、これは拙いかな、という、もうひとつの例ですが、TIとTEIを同時にイネーブルするのはそもそもアカンのではないかな、という気がします。(RXスマートコンフィグレータが生成したコードでは、送信バッファエンプティ割り込み(TI)で最終データ送信処理をした後に送信終了割り込み(TEI)をイネーブルするようになっていた筈です。)

    generate/sc_drivers/r_scifa/src/lld/r_rza2_scifa_lld.c

    /**
     * @brief R_SCIFA_StartTX enable transmit and transmit interrupts
     *
     * @param[in] channel to start transmit
     *
     * @retval  0 DRV_SUCCESS
     * @retval -1 DRV_ERROR
     */
    int_t R_SCIFA_StartTX (int_t channel)
    {
        if ((channel > R_PRV_SCIF_LAST_CHANNEL) || (channel < 0))
        {
            return (DRV_ERROR);
        }

        RZA_IO_RegWrite_16((volatile uint16_t *) &gsp_scifa[channel]->SCR.WORD,
                                                 1, SCIFA_SCR_TE_SHIFT, SCIFA_SCR_TE);

        /* enable interrupts to trigger DMA */
        RZA_IO_RegWrite_16((volatile uint16_t *) &gsp_scifa[channel]->SCR.WORD,
                                                 1, SCIFA_SCR_TIE_SHIFT, SCIFA_SCR_TIE);


        /* enable transmit end interrupt */
        RZA_IO_RegWrite_16((volatile uint16_t *) &gsp_scifa[channel]->SCR.WORD,
                                                 1, SCIFA_SCR_TEIE_SHIFT, SCIFA_SCR_TEIE);


        return (DRV_SUCCESS);
    }
    /******************************************************************************
     * End of Function R_SCIFA_StartTX
     ******************************************************************************/