RL78 FreeRTOS APIを特別なおまじない記述無しで割り込みルーチンから呼び出せるようにしてみた(CC-RL/GNURL78)

こんにちは。NoMaYです。

別スレッド『Amazon AWSのFreeRTOS Kernel Developer GuideのサンプルコードをRenesas RX SimulatorのDebug Consoleで試せるようにしてみた』で気付いたことですが、FreeRTOSでは、ポートレイヤーの実装に内蔵周辺のソフトウェア割り込み機能(これはCPUのソフトウェア割り込み命令のことでは無いです)を使用することで、素朴に簡単に割り込みルーチンからFreeRTOS APIを呼び出せるようになることに気付きました。そこで、FreeRTOS v10.2.1 RL78ポートレイヤーをそのように改変してみました。

以下、プロジェクトのファイル一式です。(CC-RL版はCS+ V8.01/e2 stuiod v7.40+CC-RL V1.02でビルド(e2 studio用.project/.cproject等を同梱))(GNURL78版はe2 studio v7.40+GNURL78 2019q2(4.9.2.201902)でビルド)(共にzipファイルをe2 studioに直接インポート可能) プロジェクト構造は極力RX版と同じにしてあります。([追記] すみません。以下に含まれるMAPファイルが別のものでした。タスク側のクリティカルセクションが以前のままのものでした。この投稿の末尾に追加したMOTファイルのzipファイルには訂正版を入れてあります。)

sim_rl78_freertos_full_demo_ccrl_c_csplus_20190705.zip    628KB
sim_rl78_freertos_full_demo_gnurl78_c_e2v740_20190705.zip    582KB

そのスレッドに投稿していたRL78ポートレイヤー(これもFreeRTOS v10.2.1 RL78ポートレイヤーを改変したものです)では、割り込みルーチンからFreeRTOS APIを呼び出すには以下のような特別なおまじないを記述する必要がありました。(以下はCC-RLの場合のものですがGNURL78でも同様です。)

#pragma interrupt r_intc3_interrupt(vect=INTP3)
/* Start user code for pragma. Do not edit comment generated here */
R_PRAGMA_FREERTOS_INTERRUPT(r_intc3_interrupt)
#define r_intc3_interrupt _r_intc3_interrupt
/* End user code. Do not edit comment generated here */

そのスレッドにはRXポートレイヤーの改変についても投稿していたのですが、その時、最初に書いたことに気付きました。RL78の内蔵周辺には専用のソフトウェア割り込み機能はありませんが、以下のハードウェアマニュアルに記載されている通り、プログラムで割り込み要求フラグをセットすると割り込みを発生させることが出来ますので、実質的に任意の空き割り込みをソフトウェア割り込み機能として使うことが出来ます。(今回は、とりあえず、ウォッチドッグタイマのオーバフロー時間の75%到達のインターバル割り込みを使用してみました。)

RL78/G13 ユーザーズマニュアル ハードウェア編からの抜粋
www.renesas.com/ja-jp/doc/products/mpumcu/doc/rl78/r01uh0146jj0340_rl78g13.pdf



なお、今回のRL78ポートレイヤーの改変で、先ほどの特別なおまじないを記述する必要は無くなりますが、割り込みルーチンからFreeRTOS APIを呼び出してブロック解除待ちタスクをブロック解除する場合のタスク切り替えの遅延時間が以下のRenesas RL78 SimulatorのSimulator GUIの画面コピーのように数十クロックほど(32MHz動作では600nsほど)延びます。(以下はCC-RLの場合のものですがGNURL78でも同様(ただし数百nsほど長い)です。) ちなみに、タスク側のクリティカルセクション(その区間では割り込み禁止となる)への出入りによるタイミングへの影響を避ける為、今回はクリティカルセクションとして扱うことしていませんので、以下の画面コピーでは別スレッドの画面コピーよりもポート出力トグルの速さが速くなっています。

今回の改変前:


今回の改変後:


以下、今回のRL78ポートレイヤの改変内容です。コメントの訂正や不要になったルーチンの削除は省略しています。(以下はCC-RLの場合のものですがGNURL78でも同様です。)

src/FreeRTOS/Source/portable/Renesas/RL78/portmacro.h
今回の改変前:

/* Task utilities. */
#define portYIELD() __brk()
#define portYIELD_FROM_ISR( xHigherPriorityTaskWoken ) if( xHigherPriorityTaskWoken ) vTaskSwitchContext()
#define portNOP()   __nop()

今回の改変後: 赤文字行を追加/変更

#include "iodefine.h"
/* Task utilities. */
#define portYIELD() do{ WDTIIF = 1; } while (0)
#define portYIELD_FROM_ISR( xHigherPriorityTaskWoken )  if( xHigherPriorityTaskWoken ) portYIELD()
#define portNOP()   __nop()

src/FreeRTOS/Source/portable/Renesas/RL78/port.c
今回の改変後: 赤文字行を追加

BaseType_t xPortStartScheduler( void )
{
    /* Setup the hardware to generate the tick.  Interrupts are disabled when
    this function is called. */
    configSETUP_TICK_INTERRUPT();

    /* Setup the hardware to generate the yield interrupt. */
    WDTIPR1 = 1;
    WDTIPR0 = 1;
    WDTIMK = 0;

    /* Restore the context of the first task that is going to run. */
    vPortStartFirstTask();

    /* Execution should not reach here as the tasks are now running! */
    return pdTRUE;
}

src/FreeRTOS/Source/portable/Renesas/RL78/portasm.asm
今回の改変前:

_vPortYield:
    portSAVE_CONTEXT                ; Save the context of the current task.
    call@      _vTaskSwitchContext  ; Call the scheduler to select the next task.
    portRESTORE_CONTEXT             ; Restore the context of the next task to run.
    retb
    _vPortYield      .VECTOR    0x7E

今回の改変後: 赤文字行を変更

_vPortYield:
    portSAVE_CONTEXT                ; Save the context of the current task.
    call@      _vTaskSwitchContext  ; Call the scheduler to select the next task.
    portRESTORE_CONTEXT             ; Restore the context of the next task to run.
    reti
    _vPortYield      .VECTOR    0x04

以下、今回のタスク側のプログラムです。先ほど書いた通り、今回は各タスクのポート出力をトグルさせる部分をクリティカルセクションとして扱うことしていません。(なお、src/frtos_config/FreeRTOSConfig.hの#define FREERTOS_USER_MAIN 0の行で0→1の変更を行うことで以下が実行されるようになります。)

src/user_main.c

void main_task(void *pvParameters)
{
    (void) pvParameters;

    while (1)
    {
        P1_bit.no6 = !P1_bit.no6;
    }
}

void second_task(void *pvParameters)
{
    (void) pvParameters;

    while (1)
    {
        P1_bit.no5 = !P1_bit.no5;
    }
}

void third_task(void *pvParameters)
{
    (void) pvParameters;

    while (1)
    {
        P1_bit.no4 = !P1_bit.no4;
    }
}

void intp3_task(void *pvParameters)
{
    (void) pvParameters;

    R_INTC3_Start();

    LED_INIT();
    while (1)
    {
        xSemaphoreTake( xSemaphoreINTP3, portMAX_DELAY );
        LED_BIT = !LED_BIT;
    }
}


[追記]

MOTファイル

sim_rl78_freertos_full_demo_ccrl_c_csplus_20190705_mot.zip    2019/07/12追加
sim_rl78_freertos_full_demo_gnurl78_c_e2v740_20190705_mot.zip    2019/07/24追加

  • こんにちは。NoMaYです。

    UART受信にチョコさんのリングバッファのソースコードを使用する版をデバッグしないといけないな、と思いつつ、ちょっと好奇心から、FreeRTOSのタスク通知という機能を試したところ、このスレッドのRL78ポートレイヤの実装では動作しないことが分かりました。(修正方法を考え中です。)

    タスク通知 - AWS » ドキュメント » FreeRTOS Kernel » 開発者ガイド »
    docs.aws.amazon.com/ja_jp/freertos-kernel/latest/dg/task-notification.html

    それで、動作しない原因ですけれども、FreeRTOSのタスク通知のAPI関数であるulTaskNotifyTake()という関数は以下の通りですが、2つある赤文字部分のどちらかでタスク切り替え処理に移行しなければならないコードになっています。もともとのRL78ポートレイヤはBRK命令を使っていて、上側(A)の赤文字部分でタスク切り替え処理に移行します。それに対し、このスレッドで私が改造したRL78ポートレイヤはソフトウェア割り込み(もどき)機能を使いますが、下側(B)のちょうどtaskEXIT_CRITICAL()とtaskENTER_CRITICAL()の隙間の赤文字部分でタスク切り替え処理に移行しなければならないのでした。

    ところが、下側(B)の部分の隙間はアセンブラレベルではEI命令とDI命令の隙間になっていて、RL78の仕様上、ここでは割り込みが受け付けられないことになっています。(下記のユーザーズマニュアル ハードウェア編の画面コピーを参照) その為、ソフトウェア割り込み(もどき)でタスク切り替え処理に移行することが出来ず、ulTaskNotifyTake()の上側のクリティカルセクションと下側のクリティカルセクションを続けて実行してしまい(そして両方のクリティカルセクションの実行後にタスク切り替え処理に移行することになり)、タスク通知の機能が動作しなくなっていました。

    これが、いわゆる、生兵法は大怪我のもと、というものでしょうかね、、、(修正方法を考え中です。)

    src/FreeRTOS/Source/task.c

    #if( configUSE_TASK_NOTIFICATIONS == 1 )

        uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait )
        {
        uint32_t ulReturn;

            taskENTER_CRITICAL();
            {
                /* Only block if the notification count is not already non-zero. */
                if( pxCurrentTCB->ulNotifiedValue == 0UL )
                {
                    /* Mark this task as waiting for a notification. */
                    pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION;

                    if( xTicksToWait > ( TickType_t ) 0 )
                    {
                        prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
                        traceTASK_NOTIFY_TAKE_BLOCK();

                        /* All ports are written to allow a yield in a critical
                        section (some will yield immediately, others wait until the
                        critical section exits) - but it is not something that
                        application code should ever do. */
                        portYIELD_WITHIN_API(); ← (A)
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            taskEXIT_CRITICAL(); ← この行の最後の命令は EI命令 です
            ちょうど此処の隙間 ← (B)
            taskENTER_CRITICAL(); ← この行の最初の命令は DI命令 です
            {
                traceTASK_NOTIFY_TAKE();
                ulReturn = pxCurrentTCB->ulNotifiedValue;

                if( ulReturn != 0UL )
                {
                    if( xClearCountOnExit != pdFALSE )
                    {
                        pxCurrentTCB->ulNotifiedValue = 0UL;
                    }
                    else
                    {
                        pxCurrentTCB->ulNotifiedValue = ulReturn - ( uint32_t ) 1;
                    }
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }

                pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
            }
            taskEXIT_CRITICAL();

            return ulReturn;
        }

    #endif /* configUSE_TASK_NOTIFICATIONS */

    以下、RL78/G14のユーザーズマニュアルのハードウェア編の画面コピーです。

    www.renesas.com/jp/ja/search/keyword-search.html#genre=document&q=r01uh0186
    r01uh0186jj0330-rl78g14.pdf

    EI命令実行直後は割り込みを受け付けない


    他の割り込みによるRETI命令実行直後は割り込みを受け付けない(1命令実行後に受け付ける)のも気掛かり

     

  • こんにちは。NoMaYです。

    FreeRTOSのタスク通知ですが、なにか手軽そうですね。先日投稿したSample Programではセマフォを使用していたのですが、タスク通知を使用する方法に書き換え中です。ただ、この機能はFreeRTOSや同じコードベースの商用版のOpenRTOSやAPI互換の商用機能安全版のSafeRTOSに特有なものなのかな、みたいな感じがしますので、いわゆるOS Abstraction layerとかでは対応されないものなのかも、という気もします。

    また、このスレッドの前に投稿していた以下のスレッドで、RL78/G13ですが、UART受信をDMAで行う際に受信エラーを返せるようにしたFreeRTOSサンプルプログラムを投稿した際に遭遇していた、予測外の割り込みによる誤動作に対しては、以下のように似たような小技で対処が出来るような感じになっていました。

    Amazon AWSのFreeRTOS Kernel Developer GuideのサンプルコードをRenesas RX SimulatorのDebug Consoleで試せるようにしてみた
    「ダミーのブロック解除待ちを入れることでも対処することが出来ました」
    japan.renesasrulz.com/cafe_rene/f/forum21/5790/amazon-aws-freertos-kernel-developer-guide-renesas-rx-simulator-debug-console/32254#32254

    タスク通知 - AWS » ドキュメント » FreeRTOS Kernel » 開発者ガイド »
    周辺機器デバイスドライバで使用されるタスク通知: UARTの例 ← なお送信の例なので関数名やコメントは受信の例へ脳内変換が必要です、、、
    docs.aws.amazon.com/ja_jp/freertos-kernel/latest/dg/task-notification.html#task-notifications-used-in-peripheral-device-drivers-uart-example

    BaseType_t xUART_Send( xUART *pxUARTInstance, uint8_t *pucDataSource, size_t uxLength )
    {
        BaseType_t xReturn;

        /* Save the handle of the task that called this function. The book text
        contains notes as to whether the following line needs to be protected
        by a critical section or not. */
        pxUARTInstance->xTaskToNotify = xTaskGetCurrentTaskHandle();

        /* Ensure the calling task does not already have a notification pending
        by calling  ulTaskNotifyTake() with the xClearCountOnExit parameter set
        to pdTRUE, and a block time of 0  (don't block). */
        ulTaskNotifyTake( pdTRUE, 0 );

        /* Start the transmission. */
        UART_low_level_send( pxUARTInstance, pucDataSource, uxLength );

        /* Block until notified that the transmission is complete.
        If the notification is received, then xReturn will be set to 1
        because the ISR will have incremented this task's notification value to 1 (pdTRUE).
        If the operation times out, then xReturn will be 0 (pdFALSE)
        because this task's notification value will not have been changed
        since it was cleared to 0 above.
        If the ISR executes between the calls to UART_low_level_send()
        and the call to ulTaskNotifyTake(), then the event will be latched
        in the task's notification value, and the call to ulTaskNotifyTake()
        will return immediately.*/
        xReturn = ( BaseType_t ) ulTaskNotifyTake( pdTRUE, pxUARTInstance->xTxTimeout );

        return xReturn;
    }

    Amazon AWSのFreeRTOS Kernel Developer GuideのサンプルコードをRenesas RX SimulatorのDebug Consoleで試せるようにしてみた
    「受信エラー割り込みが複数回発生しないようにするやり方でも対処してみました」
    apan.renesasrulz.com/cafe_rene/f/forum21/5790/amazon-aws-freertos-kernel-developer-guide-renesas-rx-simulator-debug-console/32271#32271

    www.freertos.org » Kernel » Developer Docs » Direct To Task Notifications » ...As Binary Semaphore
    RTOS Task Notifications
    Used As Light Weight Binary Semaphores ← なお送信の例なので関数名やコメントは受信の例へ脳内変換が必要です、、、
    www.freertos.org/RTOS_Task_Notification_As_Binary_Semaphore.html

    void StartTransmission( uint8_t *pcData, size_t xDataLength )
    {
        /* At this point xTaskToNotify should be NULL as no transmission
        is in progress.  A mutex can be used to guard access to the
        peripheral if necessary. */
        configASSERT( xTaskToNotify == NULL );

        /* Store the handle of the calling task. */
        xTaskToNotify = xTaskGetCurrentTaskHandle();

        /* Start the transmission - an interrupt is generated when the
        transmission is complete. */
        vStartTransmit( pcData, xDatalength );
    }
    void vTransmitEndISR( void )
    {
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;

        /* At this point xTaskToNotify should not be NULL as a transmission was
        in progress. */
        configASSERT( xTaskToNotify != NULL ); → ここを以下にすれば割り込みが複数回発生しても誤動作しない
      if (xTaskToNotify != NULL)
      {

        /* Notify the task that the transmission is complete. */
        vTaskNotifyGiveFromISR( xTaskToNotify, &xHigherPriorityTaskWoken );

        /* There are no transmissions in progress, so no tasks to notify. */
        xTaskToNotify = NULL;

        /* If xHigherPriorityTaskWoken is now set to pdTRUE then a context switch
        should be performed to ensure the interrupt returns directly to the highest
        priority task.  The macro used for this purpose is dependent on the port in
        use and may be called portEND_SWITCHING_ISR(). */
        portYIELD_FROM_ISR( xHigherPriorityTaskWoken );

      }
    }

    [参考]

    FreeRTOSとOpenRTOS/SafeRTOS
    www.freertos.org/a00114.html#commercial

    RTOS Resources and Tutorials
    www.highintegritysystems.com/rtos/rtos-tutorials/

    [追記]

    ちなみに、タスク通知が動作しなかった件の、このスレッドのポートレイヤの問題ですが、当面は、以下のようにすることで対処しようと考えています。ただ、RTOSのカーネル/ポートレイヤで、このようなループ終了条件が自明とはちょっと言いにくいようなループは嫌われるのかも、とも感じていますので、何とか改善したい気持ちはあります。(RTOSのドキュメントで、deterministic、とか、決定論的、とか、そういう言葉を見掛けますので、そういうものなのかも、と感じているのです。)

    src/FreeRTOS/Source/portable/Renesas/RL78/portmacro.h
    src/FreeRTOS/Source/portable/GCC/RL78/portmacro.h

    #define portEXIT_CRITICAL()                                                     \
    {                                                                               \
    extern volatile uint16_t usCriticalNesting;                                     \
                                                                                    \
        if( usCriticalNesting > portNO_CRITICAL_SECTION_NESTING )                   \
        {                                                                           \
            /* Decrement the nesting count as we are leaving a critical section. */ \
            usCriticalNesting--;                                                    \
                                                                                    \
            /* If the nesting level has reached zero then interrupts should be */   \
            /* re-enabled. */                                                       \
            if( usCriticalNesting == portNO_CRITICAL_SECTION_NESTING )              \
            {                                                                       \
                portENABLE_INTERRUPTS();                                            \
                /* This NOP prevents infinite DTC pending caused by BT WDTIIF $$. */\
                /* (Moreover, such DTC pending causes infinite interrupt pending. */\
                /* Furthermore, such interrupt pending prevents task switching. */  \
                /* So, in case of without this NOP, task switching never occur */   \
                /* if DTC occurs just here.)(This is in case of CC-RL. In case of */\
                /* GNURL78, more codes are generated so this situation does not */  \
                /* happen but NOP is inserted to be the same operation as CC-RL.) */\

                do{ portNOP(); }while (WDTIIF != 0);                                \
            }                                                                       \
        }                                                                           \
    }
     
  • こんにちは。NoMaYです。

    RL78のコード生成機能で生成されたUART受信処理は以下のようなコードになるのですが、RXスマートコンフィグレータで生成されたコードとは異なり(受信処理中では無い時に受信割り込みが発生したというソフトウェアオーバーランと呼ばれるエラーの検出/対処を可能にする為?)、受信処理完了後も受信割り込みが許可された状態になっています。神経質な話ですが、次の受信処理開始時に受信割り込みで使用する変数群を変更する際に以下の(1)~(3)の途中で受信割り込みが発生してしまった場合、前の受信処理の受信バッファの(末尾+1)番地に書き込んだり、その受信バッファがスタックに確保されていた場合にはもう無関係になってしまった場所に書き込んだり、といったことが起きてしまうように思います。

    この(1)~(3)の区間は受信割り込みを一時禁止(受信開始時点なのでクリティカルセクションにするほどでは無いと思いました)するようにした方が安全だと思います。(ただし、禁止しなかった場合、この区間で他タスクへミリ秒オーダーで行ってしまうかもしれず、(末尾+1)番地よりもずっと多く書き込んでしまったりすることもあるかもしれないと思いました。) (デバッグしていて気付いた訳ではなくて、2ヶ月ほどコードと睨めっこしたあげくに思ったことです。)

    r_cg_serial_user.c

    void r_uart3_interrupt_receive(void)
    {
        volatile uint8_t rx_data;
        volatile uint8_t err_type;
        
        err_type = (uint8_t)(SSR13 & 0x0007U);
        SIR13 = (uint16_t)err_type;

        if (err_type != 0U)
        {
            r_uart3_callback_error(err_type);
        }
        
        rx_data = RXD3;

        if (g_uart3_rx_length > g_uart3_rx_count)
        {
            *gp_uart3_rx_address = rx_data;
            gp_uart3_rx_address++;
            g_uart3_rx_count++;

            if (g_uart3_rx_length == g_uart3_rx_count)
            {
                r_uart3_callback_receiveend();
            }
        }
        else
        {
            r_uart3_callback_softwareoverrun(rx_data);
        }
    }
    static void r_uart3_callback_receiveend(void)
    {
        /* Start user code. Do not edit comment generated here */
        /* End user code. Do not edit comment generated here */
    }
    static void r_uart3_callback_softwareoverrun(uint16_t rx_data)
    {
        /* Start user code. Do not edit comment generated here */
        /* End user code. Do not edit comment generated here */
    }
    static void r_uart3_callback_error(uint8_t err_type)
    {
        /* Start user code. Do not edit comment generated here */
        /* End user code. Do not edit comment generated here */
    }

    r_cg_serial.c

    MD_STATUS R_UART3_Receive(uint8_t * const rx_buf, uint16_t rx_num)
    {
        MD_STATUS status = MD_OK;

        if (rx_num < 1U)
        {
            status = MD_ARGERROR;
        }
        else
        {
            g_uart3_rx_count = 0U; - (1)
            g_uart3_rx_length = rx_num; - (2)
            gp_uart3_rx_address = rx_buf; - (3)
        }

        return (status);
    }

    こうすれば安全になると思われるコード: 受信割り込みを一時禁止する(受信開始時点なのでクリティカルセクションにするほどでは無いと思いました)

            SRMK3 = 1U;    /* disable INTSR3 interrupt */
            g_uart3_rx_count = 0U;
            g_uart3_rx_length = rx_num;
            gp_uart3_rx_address = rx_buf;
            SRMK3 = 0U;    /* enable INTSR3 interrupt */

     

  • こんにちは。NoMaYです。#4連投の1つ目です。

    このスレッドで以前に投稿した、RX-TBの自作のFreeRTOS Sample ProgramをRL78/G14 Fast Prototyping Boardへ移植したSample Programですが、今回、UART送受信を割り込みのみで行う版とDTCで行う版を作成してみました。また、今回、セマフォ(バイナリセマフォ)を使用しないで、FreeRTOSのタスク通知を使用する方法で作成してみました。(FreeRTOSのタスク通知が、なにか手軽そう、という印象だったからです。ただ、やってみると、手軽さに関しては、事前にセマフォ(バイナリセマフォ)を作成する手間が減った分ぐらいだった気がするなぁ、という感じで、ちょっと思い込み過ぎていたかなぁ、という気もしました。)

    それから、今回、FreeRTOSのAPI関数をタスク側ソースではなくコード生成側ソースで呼ぶような作りにして、タスク側では以下の3つの関数で送受信処理を実行するようにしたのですが、(C)のデバッグ用にSample Programの動作に以下の(3'')の動作を追加しました。

    (A) U_UART3_Receive_Wait
    (B) U_UART3_Send_Wait
    (C) U_UART3_Send_Receive_Wait ← 注: 受信処理を起動してから送信処理を実行します

    (1) FPBボードのLED0を500ms毎にOn/Offする(FreeRTOS APIのvTaskDelay()を使用)
    (2) FPBボードのSW1押下毎にLED1をOn/Offする(タスクでタスク通知待ちして割り込みでタスク通知発行)
    (3) FPBボードのUART3で3文字受信毎に3文字エコーバックする(タスクでタスク通知待ちして割り込みで3文字毎にタスク通知発行)
    (3') 受信エラーを検出したらLED1を200ms毎にOn/OffさせるようにしてSW1が押下されたら受信エラー状態をクリアする
       検出する受信エラー:パリティエラー、フレーミングエラー、オーバーランエラー、タイムアウト(60秒)
    (3'') CPUリセット後と受信エラー状態クリア後に"Enter characters"の文字列+CR+LFを送信する

    以下、プロジェクトのファイル一式です。(CC-RL版はCS+ V8.03/e2 studio v7.6.0+CC-RL V1.02でビルド(e2 studio用.project/.cproject等を同梱)(当方特有の事情でCC-RL V1.02を使用))(GNURL78版はe2 studio v7.6.0+GNURL78 2019q4(4.9.2.201904)でビルド)(共にzipファイルをe2 studioに直接インポート可能) プロジェクト構造は極力RX-TBの自作のFreeRTOSプロジェクトもどきSample Programに似せてあります。

    rl78g14fpb_freertos_sampleprog1and2_ccrl_c_csplus_20200204.zip (*1)    805KB
    rl78g14fpb_freertos_sampleprog1and2_gnurl78_c_e2v760_20200204.zip (*1,*2)   738KB

    含まれるプロジェクト
    sampleprog1: UART送受信をDTCで行う版
    sampleprog2: UART送受信を割り込みのみで行う版

    *1:実機用のデバッガプロパティ設定(CS+)や.launchファイル設定(e2 studio)は当方特有の事情でオンボードエミュレータが使えない為に未確認です。(ひと通りは設定してあります。)

    *2:リンカスクリプトには別スレッド「GNURL78でconst領域/Mirror領域をちょっと安全に使えるようにlinker scriptのASSERT()で小技(TIPS)を考えてみた」に投稿した内容を反映してあります。

    この後、2投目~4投目は以下の通りです。

    2投目

    ・FreeRTOSのタスク通知のAPI関数への自作ラッパー関数
    ・LED1タスクのソースとコード生成された外部割込み処理のユーザ記述部のソース
    (今回はFreeRTOSのAPI関数(のラッパー関数)をタスク側ソースではなくコード生成側ソースで呼ぶようにしています)

    3投目

    ・UART送受信を割り込みのみで行う版のCONIOタスクのソースとコード生成されたシリアル処理のユーザ記述部のソース
    (今回はFreeRTOSのAPI関数(のラッパー関数)をタスク側ソースではなくコード生成側ソースで呼ぶようにしています)
    (なお、受信完了で受信割り込みを止めるようにしています)

    4投目

    ・UART送受信をDTCで行う版のCONIOタスクのソースとコード生成されたシリアル処理のユーザ記述部のソース
    (今回はFreeRTOSのAPI関数(のラッパー関数)をタスク側ソースではなくコード生成側ソースで呼ぶようにしています)
    (なお、受信完了で受信割り込みを止めるようにしています)

    以下、画面コピーです。(といっても、動作に追加した"Enter characters"がTeraTermやe2 studioのTerminalビューに表示されているところのみ、ですが。)



     

  • こんにちは。NoMaYです。#4連投の2つ目です。

    今回、先日このスレッドに投稿したFreeRTOSのタスク通知のAPI関数を使用する時の小技に関して、以下のラッパー関数を自作してみました。(小技に関する部分を赤文字にしています。)

    src/frtos_startup/freertos_start.c

    void vTaskNotifyGiveFromISR_R_Helper(TaskHandle_t *pxTask)
    {
        if (NULL != *pxTask)[
        {
            BaseType_t sHigherPriorityTaskWoken = pdFALSE;

            /* Notify the task that the interrupt/callback is complete. */
            vTaskNotifyGiveFromISR( *pxTask, &sHigherPriorityTaskWoken );

            /* There are no interrupt/callback in progress, so no tasks to notify. */
            *pxTask = NULL;

            portYIELD_FROM_ISR( sHigherPriorityTaskWoken );
        }

    } /* End of function vTaskNotifyGiveFromISR_R_Helper() */
     
    TaskHandle_t xTaskGetCurrentTaskHandle_R_Helper(void)
    {
        /* Ensure the calling task does not already have a notification pending. */
        ulTaskNotifyTake( pdTRUE, 0 );

        /* Return the handle of the calling task. */
        return xTaskGetCurrentTaskHandle();

    } /* End of function xTaskGetCurrentTaskHandle_R_Helper() */

    なお、併せて、API関数呼び出しの簡略化の為に(といっても引数を1つ省略する為だけなのですが)、以下のラッパー関数も作成しています。

    src/frtos_startup/freertos_start.c

    uint32_t ulTaskNotifyTake_R_Helper(TickType_t xTicksToWait)
    {
        /* Wait to be notified that the interrupt/callback is complete. */
        return ulTaskNotifyTake( pdTRUE, xTicksToWait );

    } /* End of function ulTaskNotifyTake_R_Helper() */

    LED1タスクのソースは以下の通りです。今回はFreeRTOSのAPI関数(のラッパー関数)をタスク側ソースではなくコード生成側ソースで呼ぶようにしています。(コード生成側ソースの関数を呼び出している箇所を青文字にしています。)

    src/frtos_skeleton/task_LED1.c

    void task_LED1(void * pvParameters)
    {
    /* ...略... */

        INTERNAL_NOT_USED( pvParameters );

        R_INTC0_Start();

        LED1 = LED_OFF;
        for (;;)
        {
            U_INTC0_Wait();

            /* Remove mechanical switch chattering */
            vTaskDelay( SW1_REMOVE_CHATTERING_PERIOD_MS );

            if (SW1_PUSH == SW1)
            {
                if (true == g_task_CONIO_error)
                {
                    /* Clear error */
                    g_task_CONIO_error = false;
                }
                else
                {
                    /* Toggle LED1 */
                    LED1 = ~LED1;
                }
            }
        }

    /* ...略... */
    }

    コード生成された外部割込み処理のユーザ記述部のソースは以下の通りです。(FreeRTOSのAPI関数(のラッパー関数)を呼び出している箇所を赤文字にしています。)

    src/r_cg_intc.c

    /* Start user code for adding. Do not edit comment generated here */
    ...略...
    void U_INTC0_Wait(void)
    {
        g_intc0_task = xTaskGetCurrentTaskHandle_R_Helper();

        /* Wait for a notification from the interrupt/callback */
        ulTaskNotifyTake_R_Helper( portMAX_DELAY );[1行
    }
    /* End user code. Do not edit comment generated here */

    src/r_cg_intc_user.c

    void r_intc0_interrupt(void)
    {
        /* Start user code. Do not edit comment generated here */

        vTaskNotifyGiveFromISR_R_Helper( &g_intc0_task );

        /* End user code. Do not edit comment generated here */
    }

     

  • こんにちは。NoMaYです。#4連投の3つ目です。

    割り込みのみ版のCONIOタスクのソースは以下の通りです。今回はFreeRTOSのAPI関数(のラッパー関数)をタスク側ソースではなくコード生成側ソースで呼ぶようにしています。(コード生成側ソースの関数を呼び出している箇所を青文字にしています。)

    freertos_sampleprog2_gnurl78_c/src/frtos_skeleton/task_CONIO.c

    void task_CONIO(void * pvParameters)
    {
    /* ...略... */

        volatile uint8_t recv_buff[CON_RECV_DEMO_SIZE + 1];
        volatile uint8_t err_type;
        MD_STATUS status;
        uint32_t cnt;

        INTERNAL_NOT_USED( pvParameters );

        U_UART3_Start();

        cnt = 0;
        for (;;)
        {
            memset( (char *)recv_buff, 0, CON_RECV_DEMO_SIZE + 1 );

            if (0 == cnt)
            {
                status = U_UART3_Send_Receive_Wait(
                             (uint8_t *)CON_START_MESSAGE, sizeof(CON_START_MESSAGE),
                             recv_buff, CON_RECV_DEMO_SIZE, &err_type, CON_RECV_TIMEOUT_MS
                         );
           }
            else
            {
                status = U_UART3_Receive_Wait(
                             recv_buff, CON_RECV_DEMO_SIZE, &err_type, CON_RECV_TIMEOUT_MS
                         );

            }
            cnt++;

            nop();  /* for breakpoint, check timing chart on Simulator GUI */

            if (MD_OK == status)
            {
                taskENTER_CRITICAL();
                vPrintString( "Received Message: " );
                vPrintString( (char *)recv_buff );
                vPrintString( "\n" );
                taskEXIT_CRITICAL();
            }
            else
            {
                if (MD_RECV_TIMEOUT == status)
                {
                    vPrintString( "Recv Timeout Error\n" );
                }
                else if (MD_RECV_ERROR == status)
                {
                    if (SCI_EVT_FRAMING_ERR & err_type)
                    {
                        vPrintString( "Framing Error\n" );
                    }
                    if (SCI_EVT_PARITY_ERR & err_type)
                    {
                        vPrintString( "Parity Error\n" );
                    }
                    if (SCI_EVT_OVFL_ERR & err_type)
                    {
                        vPrintString( "Overrun Error\n" );
                    }
                }
                else if (MD_SEND_ERROR == status)
                {
                    vPrintString( "Send Error\n" );
                }

                g_task_CONIO_error = true;
                LED1 = LED_ON;
                while (true == g_task_CONIO_error)
                {
                    vTaskDelay( LED1_ERROR_BLINK_FREQUENCY_MS );
                    LED1 = ~LED1;
                }
                LED1 = LED_OFF;

                U_UART3_Receive_ClearError(); /* if not called, will be called internally */

                cnt = 0;
                continue;
            }

            status = U_UART3_Send_Wait( (uint8_t *)recv_buff, CON_RECV_DEMO_SIZE );

            nop();  /* for breakpoint, check timing chart on Simulator GUI */
        }

    /* ...略... */
    }

    コード生成されたシリアル処理のユーザ記述部(抜粋)のソースは以下の通りです。(FreeRTOSのAPI関数(のラッパー関数)を呼び出している箇所を赤文字にしています。) (なお、受信完了で受信割り込みを止めるようにしています。)

    freertos_sampleprog2_gnurl78_c/src/r_cg_serial.c

    /* Start user code for adding. Do not edit comment generated here */
    ...略...
    void U_UART3_Start(void)
    {
        R_UART3_Start();
        U_UART3_Receive_Stop();
    }
    MD_STATUS U_UART3_Receive_Wait(volatile uint8_t * rx_buf, uint16_t rx_num, 
        volatile uint8_t * p_err_type, TickType_t rx_wait)
    {
        MD_STATUS status = MD_OK;

        if (rx_num < 1U)
        {
            status = MD_ARGERROR;
        }
        else
        {
            if (0U != g_uart3_rx_error_type)
            {
                U_UART3_Receive_ClearError();
            }

            g_uart3_rx_task = xTaskGetCurrentTaskHandle_R_Helper();
            U_UART3_Receive( rx_buf, rx_num );

            /* Wait for a notification from the interrupt/callback */
            ulTaskNotifyTake_R_Helper( rx_wait );

            status = U_UART3_Receive_Complete( p_err_type );

            if (MD_RECV_TIMEOUT == status)
            {
                U_UART3_Receive_Stop();
                g_uart3_rx_task = NULL;

                /* Clear an unhandled notification from timeout till stop */
                ulTaskNotifyTake_R_Helper( 0 );
            }
        }

        return status;
    }

    static void U_UART3_Receive(volatile uint8_t * rx_buf, uint16_t rx_num)
    {
        /* This function should be called in SRMK3==1 */
        g_uart3_rx_ready_flag = false;
        R_UART3_Receive( (uint8_t *)rx_buf, rx_num );
        SRMK3 = 0U;        /* enable INTSR3 interrupt */
    }
    MD_STATUS U_UART3_Send_Wait(uint8_t * tx_buf, uint16_t tx_num)
    {
        MD_STATUS status = MD_OK;

        if (tx_num < 1U)
        {
            status = MD_ARGERROR;
        }
        else
        {
            g_uart3_tx_task = xTaskGetCurrentTaskHandle_R_Helper();
            U_UART3_Send( tx_buf, tx_num );

            /* Wait for a notification from the interrupt/callback */
            ulTaskNotifyTake_R_Helper( portMAX_DELAY );[1
        }

        return status;
    }
    MD_STATUS U_UART3_Send_Receive_Wait(uint8_t * tx_buf, uint16_t tx_num, 
        volatile uint8_t * rx_buf, uint16_t rx_num,
        volatile uint8_t * p_err_type, TickType_t txrx_wait)
    {
        MD_STATUS status;

        if (tx_num < 1U || rx_num < 1U)
        {
            status = MD_ARGERROR;
        }
        else
        {
            if (0U != g_uart3_rx_error_type)
            {
                U_UART3_Receive_ClearError();
            }

            g_uart3_rx_task = xTaskGetCurrentTaskHandle_R_Helper();
            U_UART3_Receive( rx_buf, rx_num );

            g_uart3_tx_task = NULL;
            R_UART3_Send( tx_buf, tx_num );

            /* Wait for a notification from the interrupt/callback */
            ulTaskNotifyTake_R_Helper( txrx_wait );

            if (false == g_uart3_tx_ready_flag)
            {
                U_UART3_Send_Stop();
            }

            status = U_UART3_Receive_Complete( p_err_type );

            if (MD_RECV_TIMEOUT == status)
            {
                U_UART3_Receive_Stop();
                g_uart3_rx_task = NULL;

                /* Clear an unhandled notification from timeout till stop */
                ulTaskNotifyTake_R_Helper( 0 );
            }

            if (false == g_uart3_tx_ready_flag)
            {
                *p_err_type = 0U;
                status = MD_SEND_ERROR;
            }
        }

        return status;
    }
    /* End user code. Do not edit comment generated here */

    freertos_sampleprog2_gnurl78_c/src/r_cg_serial_user.c (以下では関数の順番を入れ替えています)

    static void r_uart3_callback_error(uint8_t err_type)
    {
        /* Start user code. Do not edit comment generated here */

        U_UART3_Receive_Stop();
        g_uart3_rx_error_type = err_type;

        vTaskNotifyGiveFromISR_R_Helper( &g_uart3_rx_task );

        /* End user code. Do not edit comment generated here */
    }
    static void r_uart3_callback_receiveend(void)
    {
        /* Start user code. Do not edit comment generated here */

        if (0U == g_uart3_rx_error_type)
        {
            U_UART3_Receive_Stop();
            g_uart3_rx_ready_flag = true;

            vTaskNotifyGiveFromISR_R_Helper( &g_uart3_rx_task );
        }

        /* End user code. Do not edit comment generated here */
    }
    static void r_uart3_callback_sendend(void)
    {
        /* Start user code. Do not edit comment generated here */
    #if 0 /* for debugging about MD_SEND_ERROR */
        return;
    #endif

        U_UART3_Send_Stop();
        g_uart3_tx_ready_flag = true;

        vTaskNotifyGiveFromISR_R_Helper( &g_uart3_tx_task );

        /* End user code. Do not edit comment generated here */
    }

     

  • こんにちは。NoMaYです。#4連投の4つ目です。

    DTC版のCONIOタスクのソースは以下の通りです。今回はFreeRTOSのAPI関数(のラッパー関数)をタスク側ソースではなくコード生成側ソースで呼ぶようにしています。(コード生成側ソースの関数を呼び出している箇所を青文字にしています。また、割り込みのみ版に対してDTC版に特有の部分を赤文字にしています。)

    freertos_sampleprog1_gnurl78_c/src/frtos_skeleton/task_CONIO.c

    void task_CONIO(void * pvParameters)
    {
    /* ...略... */

        volatile uint8_t recv_buff[CON_RECV_DEMO_SIZE + 1];
        volatile uint8_t err_type;
        MD_STATUS status;
        uint32_t cnt;

        INTERNAL_NOT_USED( pvParameters );

        U_UART3_Using_DTC_Start();

        cnt = 0;
        for (;;)
        {
            memset( (char *)recv_buff, 0, CON_RECV_DEMO_SIZE + 1 );

            if (0 == cnt)
            {
                status = U_UART3_Send_Receive_Wait(
                             (uint8_t *)CON_START_MESSAGE, sizeof(CON_START_MESSAGE),
                             recv_buff, CON_RECV_DEMO_SIZE, &err_type, CON_RECV_TIMEOUT_MS
                         );

            }
            else
            {
                status = U_UART3_Receive_Wait(
                             recv_buff, CON_RECV_DEMO_SIZE, &err_type, CON_RECV_TIMEOUT_MS
                         );
            }
            cnt++;

            nop();  /* for breakpoint, check timing chart on Simulator GUI */

            if (MD_OK == status)
            {
                taskENTER_CRITICAL();
                vPrintString( "Received Message: " );
                vPrintString( (char *)recv_buff );
                vPrintString( "\n" );
                taskEXIT_CRITICAL();
            }
            else
            {
                if (MD_RECV_TIMEOUT == status)
                {
                    vPrintString( "Recv Timeout Error\n" );
                }
                else if (MD_RECV_ERROR == status)
                {
                    if (SCI_EVT_RXDTC_SWOR & err_type)
                    {
                        vPrintString( "DTC Software Overrun Error\n" );
                    }
                    if (SCI_EVT_FRAMING_ERR & err_type)
                    {
                        vPrintString( "Framing Error\n" );
                    }
                    if (SCI_EVT_PARITY_ERR & err_type)
                    {
                        vPrintString( "Parity Error\n" );
                    }
                    if (SCI_EVT_OVFL_ERR & err_type)
                    {
                        vPrintString( "Overrun Error\n" );
                    }
                }
                else if (MD_SEND_ERROR == status)
                {
                    vPrintString( "Send Error\n" );
                }

                g_task_CONIO_error = true;
                LED1 = LED_ON;
                while (true == g_task_CONIO_error)
                {
                    vTaskDelay( LED1_ERROR_BLINK_FREQUENCY_MS );
                    LED1 = ~LED1;
                }
                LED1 = LED_OFF;

                U_UART3_Receive_ClearError(); /* if not called, will be called internally */

                cnt = 0;
                continue;
            }

            status = U_UART3_Send_Wait( (uint8_t *)recv_buff, CON_RECV_DEMO_SIZE );

            nop();  /* for breakpoint, check timing chart on Simulator GUI */
        }

    /* ...略... */
    }

    コード生成されたシリアル処理のユーザ記述部(抜粋)のソースは以下の通りです。(FreeRTOSのAPI関数(のラッパー関数)を呼び出している箇所を赤文字にしています。) (なお、受信完了で受信割り込みを止めるようにしています。)

    freertos_sampleprog1_gnurl78_c/src/r_cg_serial.c

    /* Start user code for adding. Do not edit comment generated here */
    ...略...
    void U_UART3_Using_DTC_Start(void)
    {
        R_UART3_Start();
        U_UART3_Receive_Stop();
    }
    MD_STATUS U_UART3_Receive_Wait(volatile uint8_t * rx_buf, uint16_t rx_num, 
        volatile uint8_t * p_err_type, TickType_t rx_wait)
    {
        MD_STATUS status = MD_OK;

        if (rx_num < 1U || 255U < rx_num)
        {
            status = MD_ARGERROR;
        }
        else
        {
            if (0U != g_uart3_rx_error_type)
            {
                U_UART3_Receive_ClearError();
            }

            g_uart3_rx_task = xTaskGetCurrentTaskHandle_R_Helper();
            U_UART3_Receive( rx_buf, rx_num );

            /* Wait for a notification from the interrupt/callback */
            ulTaskNotifyTake_R_Helper( rx_wait );

            status = U_UART3_Receive_Complete( p_err_type );

            if (MD_RECV_TIMEOUT == status)
            {
                U_UART3_Receive_Stop();
                g_uart3_rx_task = NULL;

                /* Clear an unhandled notification from timeout till stop */
                ulTaskNotifyTake_R_Helper( 0 );
            }
        }

        return status;
    }

    static void U_UART3_Receive(volatile uint8_t * rx_buf, uint16_t rx_num)
    {
        /* This function should be called in SRMK3==1 */
        g_uart3_rx_ready_flag = false;
        gp_uart3_rx_address = &g_uart3_rx_error_data;
        U_DTCD0_UserInit( rx_buf, rx_num );
        SRMK3 = 0U;        /* enable INTSR3 interrupt */
        SREMK3 = 0U;       /* enable INTSRE3 interrupt */
    #if 0 /* for debugging about SCI_EVT_RXDTC_SWOR */
        SRIF3 = 1U; /* ATTENTION: signal from SAU to DTC cannot be masked */
    #endif
        R_DTCD0_Start();
    }
    MD_STATUS U_UART3_Send_Wait(uint8_t * tx_buf, uint16_t tx_num)
    {
        MD_STATUS status = MD_OK;

        if (tx_num < 1U || 256U < tx_num)
        {
            status = MD_ARGERROR;
        }
        else
        {
            g_uart3_tx_task = xTaskGetCurrentTaskHandle_R_Helper();
            U_UART3_Send( tx_buf, tx_num );

            /* Wait for a notification from the interrupt/callback */
            ulTaskNotifyTake_R_Helper( portMAX_DELAY );
        }

        return status;
    }
    MD_STATUS U_UART3_Send_Receive_Wait(uint8_t * tx_buf, uint16_t tx_num, 
        volatile uint8_t * rx_buf, uint16_t rx_num,
        volatile uint8_t * p_err_type, TickType_t txrx_wait)
    {
        MD_STATUS status;

        if (tx_num < 1U || 256U < tx_num || rx_num < 1U || 255U < rx_num)
        {
            status = MD_ARGERROR;
        }
        else
        {
            if (0U != g_uart3_rx_error_type)
            {
                U_UART3_Receive_ClearError();
            }

            g_uart3_rx_task = xTaskGetCurrentTaskHandle_R_Helper();
            U_UART3_Receive( rx_buf, rx_num );

            g_uart3_tx_task = NULL;
            U_UART3_Send( tx_buf, tx_num );

            /* Wait for a notification from the interrupt/callback */
            ulTaskNotifyTake_R_Helper( txrx_wait );

            if (false == g_uart3_tx_ready_flag)
            {
                U_UART3_Send_Stop();
            }

            status = U_UART3_Receive_Complete( p_err_type );

            if (MD_RECV_TIMEOUT == status)
            {
                U_UART3_Receive_Stop();
                g_uart3_rx_task = NULL;

                /* Clear an unhandled notification from timeout till stop */
                ulTaskNotifyTake_R_Helper( 0 );
            }

            if (false == g_uart3_tx_ready_flag && SCI_EVT_RXDTC_SWOR != g_uart3_rx_error_type)
            {
                *p_err_type = 0U;
                status = MD_SEND_ERROR;
            }
        }

        return status;
    }
    /* End user code. Do not edit comment generated here */

    freertos_sampleprog1_gnurl78_c/src/r_cg_serial_user.c (以下では関数の順番を入れ替えています)

    static void r_uart3_callback_error(uint8_t err_type)
    {
        /* Start user code. Do not edit comment generated here */

        U_UART3_Receive_Stop();
        g_uart3_rx_error_type = err_type;

        vTaskNotifyGiveFromISR_R_Helper( &g_uart3_rx_task );

        /* End user code. Do not edit comment generated here */
    }
    static void r_uart3_callback_receiveend(void)
    {
        /* Start user code. Do not edit comment generated here */

        if (0U == g_uart3_rx_error_type)
        {
            U_UART3_Receive_Stop();
            g_uart3_rx_ready_flag = true;

            vTaskNotifyGiveFromISR_R_Helper( &g_uart3_rx_task );
        }

        /* End user code. Do not edit comment generated here */
    }
    static void r_uart3_callback_sendend(void)
    {
        /* Start user code. Do not edit comment generated here */
    #if 0 /* for debugging about MD_SEND_ERROR */
        return;
    #endif

        U_UART3_Send_Stop();
        g_uart3_tx_ready_flag = true;

        vTaskNotifyGiveFromISR_R_Helper( &g_uart3_tx_task );

        /* End user code. Do not edit comment generated here */
    }

    なお、DTC版では、以前の投稿と同様に、CC-RL/GNURL78の最適化オプションで未使用の変数/関数を削除する最適化を実施することを前提にして、マクロの小技を使用して、割り込み処理ルーチンを差し替えています。(以下では以前の投稿には無かった処理を追加した部分を赤文字にしています。)

    freertos_sampleprog1_gnurl78_c/src/r_cg_serial_user.c

    /* Start user code for global. Do not edit comment generated here */
    ...略...
    R_CG_DEFAULT_ISR_UNUSED(r_uart3_interrupt_receive)
    R_CG_DEFAULT_ISR_UNUSED(r_uart3_interrupt_send)
    #define r_uart3_interrupt_receive r_uart3_interrupt_receive_UNUSED
    #define r_uart3_interrupt_send r_uart3_interrupt_send_UNUSED
    /* End user code. Do not edit comment generated here */

    void r_uart3_interrupt_receive(void) ← コード生成機能が生成した割り込み関数(使用しない)
    {
    ...略...
    }

    void r_uart3_interrupt_send(void) ← コード生成機能が生成した割り込み関数(使用しない)
    {
    ...略...
    }

    ...略...

    /* Start user code for adding. Do not edit comment generated here */
    #undef r_uart3_interrupt_receive
    #undef r_uart3_interrupt_send
    R_CG_REDEFINE_ISR(r_uart3_interrupt_receive)
    R_CG_REDEFINE_ISR(r_uart3_interrupt_send)

    void r_uart3_interrupt_receive(void)
    {
        uint8_t err_type;

        /* For the case that INTSR3 is accepted prior to INTSRE3. */
        err_type = (uint8_t)(SSR13 & 0x0007U);
        SIR13 = (uint16_t)err_type;

        if (U_DTCD0_Is_Operating() || (0U < U_DTCD0_Get_Count()))
        {
            /* Unfortunately INTSR3 is accepted before DTC0 is enabled. */
            err_type = SCI_EVT_RXDTC_SWOR;
        }

        if (err_type != 0U)
        {
            r_uart3_callback_error(err_type);
        }

        r_uart3_callback_receiveend();
    }

    void r_uart3_interrupt_send(void)
    {
        /* DTC has finished data transfer operation. (i.e. The last data has been
         * written to SDR register.) But the data may stay in either SDR register
         * or shift register.
         */

        /* It is preferred that next interrupt will be a transmission end interrupt
         * (i.e. not only SDR register but also shift register become empty) than
         * a SDR register empty interrupt.
         */
        SMR12 &= ~_0001_SAU_BUFFER_EMPTY;

        /* In normal case of interrupt, UART has just finished data transmission
         * operation at 2nd interrupt. But in abnormal case of after long or very
         * long time pending of data transfer completion interrupt, though at 1st
         * interrupt, SDR register may already become empty or, moreover, shift
         * register may already have finished data transmission operation. In the
         * latter case, it is not clear whether data transmission end interrupt
         * will occur or not, so the following check is useful.
         */
        if ((SSR12 & _0040_SAU_UNDER_EXECUTE) == 0U)
        {
            /* UART has finished data transmission operation. Note that one more
             * interrupt may occur by above change of SMR register setting unless
             * transmission interrupt becomes disabled. (In this code, it becomes
             * disabled in the following callback function.)
             */
            r_uart3_callback_sendend();
        }
        else
        {
        /* If ((SSR12 & _0040_SAU_UNDER_EXECUTE) == 0U) changes from false to true
         * just here, changing SMR register setting at the following timing may
         * lose data transmission end interrupt.
         *
         *     SMR12 &= ~_0001_SAU_BUFFER_EMPTY;
         */
        }
    }
    /* End user code. Do not edit comment generated here */

    [追記]

    なお、4連投の1つ目は、こちらです。

  • こんにちは。NoMaYです。

    以下、別スレッド『e2 studio v7.5.0のFreeRTOS ProjectでVisual Expression+Renesas RX Simulator/TB-RX65Nで試せるSample Programを作ってみた』へ投稿した内容の転記です。そもそもは、こちらのスレッドが発端でしたので、、、

    RL78/G14 Fast Prototyping Boardで、同じようなSample Programを、チョコさんのリングバッファのソースコードを使って作ろうとしていたところ、RTOS使用時は、そのソースの通常処理側と割り込み処理側の排他制御のやり方を変えないと拙い、ということに気付きました。そして、それはFITでも同様な筈なのでは、と気付きました。(FITには、以前からFreeRTOSパッケージがありますが、その時点から対策されていなかったのでは無いでしょうか、、、)

    RTOS未使用時は大丈夫だけれどもRTOS使用時は拙い排他制御のやり方(通常処理側と割り込み処理側の排他制御)

    ・個別の割り込みマスクフラグを使用して割り込み一旦禁止と割り込み再度許可を行う

    理由

    ・RTOSによるタスク切り替えが上記による割り込み禁止期間中に発生すると禁止期間がミリ秒オーダーになってしまう(タスク切り替えにより暫くの間は他タスクに行ってしまうから)

    対応

    ・RTOS側で用意されているクリティカルセクションへの出入り関数(FreeRTOSならtaskENTER_CRITICAL()とtaskEXIT_CRITICAL()など)を使用するように手を加える

    具体的には、チョコさんのリングバッファのソースコードですと、以下のような置き換えをする必要があります。(以下の場所以外にもあります。なお、RL78のFreeRTOSでは両出入り関数はDIとEIに帰着されるので、以下のように単純化してあります。他マイコンなどでは、可能性として、割り込み優先順位を操作することで、クリティカルセクション内でも通信割り込みを受け付けられるようにする実装も有り得ると思います。その場合は、以下のような単なる単なる置き換えにならないこともあるかと思います。)

    RL78のUARTへのリングバッファ追加/UART_3/UART0.c

    /*******************************************************************************
    * Function Name: get_blk
    * Description  : リングバッファから指定されたデータを読み出す(転送)
    * Arguments    : 格納ポインタ,データ数
    * Return Value : 残りデータ数
    *******************************************************************************/
    uint8_t get_blk(uint8_t * const buff, uint8_t number)
    {

        uint8_t work;
        uint8_t cnt;                                /* 転送用カウンタ           */
        uint8_t * gp_buff;                          /* 転送用ポインタ           */

        work = number;                              /* 要求データ数をセット     */

        if ( ( 0 != number ) && ( 0 != g_rx_dtno ) )
        {                               /* 転送データがある場合                 */

            gp_buff = buff;                         /* 転送先ポインタを設定     */

    /* ---------------------------------------------------------------------------

        転送データ数を算出

    ----------------------------------------------------------------------------*/

            if ( number < g_rx_dtno )
            {                           /* 要求数がデータ量以下の場合           */
                cnt = number;                       /* 転送データ数をセット     */
                work = 0;                           /* 残りデータ数は0にする    */
            }
            else
            {                           /* 要求数がデータ量以上の場合           */
                cnt = g_rx_dtno;                    /* 転送数はバッファ内の全て */
                work = number - g_rx_dtno;          /* 残りデータ数を算出       */
            }

    /* ---------------------------------------------------------------------------

        データを転送

    ----------------------------------------------------------------------------*/

            for (  ; cnt > 0 ; cnt-- )
            {
                *gp_buff = g_rx_buff[g_rx_rdpt];    /* リングバッファから転送   */
                SRMK0 = 1;                          /* INTSR0との排他制御       */
                taskENTER_CRITICAL();
                g_rx_rdpt++;                        /* 読み出しポインタを更新   */
                g_rx_rdpt &= 0x0F;
                g_rx_dtno--;                        /* データ数を-1            */
                SRMK0 = 0;                          /* INTSR0との排他制御終了   */
                taskEXIT_CRITICAL();
                gp_buff++;                          /* 転送ポインタを更新       */

            }
        

        }

        return( work );

    }

    また、FITのR_SCI_RXモジュールにも同様な場所があります。

    。。。以後省略。。。

  • こんにちは。NoMaYです。

    UART受信にチョコさんのリングバッファのソースコードを使用する版をデバッグしていて、不具合が存在していたことに気付きました。以下のg_rx_dtnoという変数は割り込み処理で値が更新される変数なのですが、そのことは以下の4箇所で読み出された値が同じ値とは限らないということになります。このg_rx_dtnoという変数はリングバッファ内の受信済みデータ量を示す変数ですが、青文字のところで、要求データ数 number >= リングバッファ内データ量 g_rx_dtno が成立していても、赤文字のところに来るまでに、もしデータを受信してしまっていると number < g_rx_dtno となってしまっている場合があります。その場合には、要求データ数よりも多くのデータを 読み出しバッファ buff へ書き込んでしまいますので、(読み出しバッファを余分に確保していなかったのであれば)、バッファオーバーランが起きてしまいます。実際、以下の画面コピーの通り、起きてしまっているようでした。(表現が微妙なのは、アクセスブレークポイント機能やトレース機能を使おうとすると再現しなくなってしまうのみならず、読み出しバッファの値をチェックするようなif文を追加することでも再現しなくなってしまうという、トホホな状況に陥ってしまっていたからです。(ですので、この部分を対処したことで現象は起きなくなったのですが、デバッグしているプログラムには本当の原因が別にある、という可能性は残っています。))

    なお、先日投稿したRL78のコード生成機能で生成されたUART受信処理のコードと同様に、この区間でタスク切り替えが発生してしまった場合には他タスクへミリ秒オーダーで行ってしまうかもしれず、そうなると、ずっと先までバッファオーバーランしてしまうこともあるかもしれないと思いました。

    異常時(時々発生)の画面コピー


    正常時(期待値)の画面コピー


    RL78コード生成へのリングバッファ追加
    japan.renesasrulz.com/cafe_rene/m/sample_program/306

    RL78のUARTへのリングバッファ追加/UART_3/UART0.c

    対処前:
    ・ g_rx_dtnoは割り込み処理で値が更新される変数なので、以下の4箇所(太字箇所(黒/青/赤/黒))で読み出された値が同じ値とは限らない
    ・ 青文字で、要求データ数 number >= リングバッファ内データ量 g_rx_dtno でも、赤文字で、number < g_rx_dtno の場合が有り得る
    ・ その場合、要求データ数よりも多くのデータを 読み出しバッファ buff へ書き込んでしまうのでバッファオーバーランが起きてしまう

    uint8_t get_blk(uint8_t * const buff, uint8_t number)
    {

        uint8_t work;
        uint8_t cnt;                                /* 転送用カウンタ           */
        uint8_t * gp_buff;                          /* 転送用ポインタ           */

        work = number;                              /* 要求データ数をセット     */

        if ( ( 0 != number ) && ( 0 != g_rx_dtno ) )
        {                               /* 転送データがある場合                 */

            gp_buff = buff;                         /* 転送先ポインタを設定     */

    /* ---------------------------------------------------------------------------

        転送データ数を算出

    ----------------------------------------------------------------------------*/

            if ( number < g_rx_dtno )
            {                           /* 要求数がデータ量以下の場合           */
                cnt = number;                       /* 転送データ数をセット     */
                work = 0;                           /* 残りデータ数は0にする    */
            }
            else
            {                           /* 要求数がデータ量以上の場合           */
                cnt = g_rx_dtno;                    /* 転送数はバッファ内の全て */
                work = number - g_rx_dtno;          /* 残りデータ数を算出       */
            }

    /* ---------------------------------------------------------------------------

        データを転送

    ----------------------------------------------------------------------------*/

            for (  ; cnt > 0 ; cnt-- )
            {
                *gp_buff = g_rx_buff[g_rx_rdpt];    /* リングバッファから転送   */
                SRMK0 = 1;                          /* INTSR0との排他制御       */
                g_rx_rdpt++;                        /* 読み出しポインタを更新   */
                g_rx_rdpt &= 0x0F;
                g_rx_dtno--; ←ここは問題は無いです /* データ数を-1            */
                SRMK0 = 0;                          /* INTSR0との排他制御終了   */
                gp_buff++;                          /* 転送ポインタを更新       */

            }
        

        }

        return( work );

    }

    対処案: (追加箇所/変更箇所を赤字にしています)

    uint8_t get_blk(uint8_t * const buff, uint8_t number)
    {

        uint8_t work;
        uint8_t dtno;                               /* リングバッファ内データ量 */
        uint8_t cnt;                                /* 転送用カウンタ           */
        uint8_t * gp_buff;                          /* 転送用ポインタ           */

        work = number;                              /* 要求データ数をセット     */
        dtno = g_rx_dtno;                           /* 変数がvolatileなので保持 */

        if ( ( 0 != number ) && ( 0 != dtno ) )
        {                               /* 転送データがある場合                 */

            gp_buff = buff;                         /* 転送先ポインタを設定     */

    /* ---------------------------------------------------------------------------

        転送データ数を算出

    ----------------------------------------------------------------------------*/

            if ( number < dtno )
            {                           /* 要求数がデータ量以下の場合           */
                cnt = number;                       /* 転送データ数をセット     */
                work = 0;                           /* 残りデータ数は0にする    */
            }
            else
            {                           /* 要求数がデータ量以上の場合           */
                cnt = dtno;                         /* 転送数はバッファ内の全て */
                work = number - dtno;               /* 残りデータ数を算出       */
            }

    /* ---------------------------------------------------------------------------

        データを転送

    ----------------------------------------------------------------------------*/

            for (  ; cnt > 0 ; cnt-- )
            {
                *gp_buff = g_rx_buff[g_rx_rdpt];    /* リングバッファから転送   */
                SRMK0 = 1;                          /* INTSR0との排他制御       */
                g_rx_rdpt++;                        /* 読み出しポインタを更新   */
                g_rx_rdpt &= RX_BUFF_SIZE_BITS;
                g_rx_dtno--; ←ここはそのままです   /* データ数を-1            */
                SRMK0 = 0;                          /* INTSR0との排他制御終了   */
                gp_buff++;                          /* 転送ポインタを更新       */

            }
        

        }

        return( work );

    }

    以下、デバッグ時の画面コピーです。(いつものようにRL78シミュレータでデバッグしていました。) なお、初めの方で書いた「読み出しバッファの値をチェックするようなif文」とは、以下のことです。(ちなみに、ワーニングが出ますが、今回は放置しました。(CC-RL V1.02ですが。))

    if ((*(volatile __near uint8_t *)0xf500e) != 0)
    {
        nop();
    }

    CS+


    シミュレータGUI


    アクセスブレークポイント機能を使おうとしたら再現しなくなってしまいました(以下は受信オーバーランエラーになった画面)


    トレース機能を使おうとしたら再現しなくなってしまいました(以下は正常動作している画面)

     

  • こんにちは。NoMaYです。#3連投の1つ目です。

    このスレッドで以前に投稿した、RX-TBの自作のFreeRTOS Sample ProgramをRL78/G14 Fast Prototyping Boardへ移植したSample Programですが、今回、UART受信にチョコさんのリングバッファのソースコードを使用する版を作成してみました。(なお、UART送信はDTCで行う方ではなく割り込みのみで行う方です。)

    思い立ってから随分と時間が経ってしまったのですが、自分の感覚的には、少し凝って(?)以下の動作を目指したのが拙かったかなぁ、と思っているところです。(といっても、たかが数十行のことですので、自分のコーディング能力の無さによるところが大きいですけど、、、)

    ● 受信エラーやリングバッファオーバーフローが発生した時点で即それ以降のリングバッファからの(正常に)受信済みのデータの読み出しが出来なくなるようなエラー処理では無くて、それらが発生していても読み出すデータの数が(正常に)受信済みのデータの数より多くなければ読み出しが出来るようにしてみた。

    以下、プロジェクトのファイル一式です。(CC-RL版はCS+ V8.03/e2 studio v7.6.0+CC-RL V1.02でビルド(e2 studio用.project/.cproject等を同梱)(当方特有の事情でCC-RL V1.02を使用))(GNURL78版はe2 studio v7.6.0+GNURL78 2019q4(4.9.2.201904)でビルド)(共にzipファイルをe2 studioに直接インポート可能) プロジェクト構造は極力RX-TBの自作のFreeRTOSプロジェクトもどきSample Programに似せてあります。

    rl78g14fpb_freertos_sampleprog3_ccrl_c_csplus_20200220.zip (*1)    459KB
    rl78g14fpb_freertos_sampleprog3_gnurl78_c_e2v760_20200220.zip (*1,*2)   425KB

    含まれるプロジェクト
    sampleprog3: UART受信をリングバッファで行いUART送信を割り込みのみで行う版

    *1:実機用のデバッガプロパティ設定(CS+)や.launchファイル設定(e2 studio)は当方特有の事情でオンボードエミュレータが使えない為に未確認です。(ひと通りは設定してあります。)

    *2:リンカスクリプトには別スレッド「GNURL78でconst領域/Mirror領域をちょっと安全に使えるようにlinker scriptのASSERT()で小技(TIPS)を考えてみた」に投稿した内容を反映してあります。

    この後、2投目、3投目は以下の通りです。

    2投目

    ・受信リングバッファ版のCONIOタスクのソース
    (コード生成されたシリアル処理のユーザ記述部のソースは省略します。)
    (なお、LED0タスクとLED1タスクにのソースに関しては今までと同じです。)

    ・チョコさんのリングバッファのソースの変更点(ソース比較ツールの画面コピー)

    もともとのソースがUART番号依存していないことを利用してUART[番号].cに以下の#defineを記述してUART3対応にしてみました。(なお、もともとはr_cg_serial_user.cのr_uart[番号]_callback_softwareoverrun()に記述されていた処理を、UART[番号].cにuartx_callback_receivedata()を作成して、そちらへ移しています。)

    #define init_bf u_uart3_init_bf
    #define chk_status u_uart3_chk_status
    #define get_data u_uart3_get_data
    #define get_blk u_uart3_get_blk
    #define g_rx_buff g_uart3_rx_buff
    #define g_rx_rdpt g_uart3_rx_rdpt
    #define g_rx_dtno g_uart3_rx_dtno
    #define g_rx_status g_uart3_rx_status
    #define uartx_callback_receivedata u_uart3_callback_receivedata
    #define RX_BUFF_SIZE UART3_RX_BUFF_SIZE
    #define RX_DTPT_MASK (UART3_RX_BUFF_SIZE - 1)

    3投目

    ・Renesas RL78 Simulatorでの実行例(画面コピー: CC-RL+CS+ 及び GNURL78+e2 studio)

    正常系 (シリアル送信データファイルはsim_rl78_serial_data_ok_3.serを使用)
    異常系 (シリアル送信データファイルはsim_rl78_serial_data_err_overrun.serを使用)

    ・Renesas RL78 Simulatorのトレース機能で調べた割り込み禁止時間の例(画面コピー: CC-RL+CS+ のみ)

    タスク切り替え処理用割り込み(割り込み禁止)で4μs強の時間が掛かっている。(1バイトの転送時間が5.5μsなのですが、残念ながら、連続転送させるとオーバーランエラーが発生します。)

    ・Renesas RL78 Simulatorのカバレッジ機能でソースウィンドウに未実行箇所を表示させた例(画面コピー: CC-RL+CS+ のみ)

    以下、実機で実行させた時のTeraTermの画面コピーと送信したデータのファイルです。(割り込み禁止時間との兼ね合いで1msの送信遅延を挿入しています。さすがに1msも必要無いのですが、TeraTermで設定出来る最小時間が1msですので、そのように設定しています。)



    4061.teraterm_serial_data_send_with_1ms_interval_20200224.txt
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    END_