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追加

Parents
  • こんにちは。NoMaYです。

    FreeRTOSのタスク通知のAPIには、0以外の32ビット値を割り込み処理側から通常処理側(タスク処理側)へ通知することが出来る以下の関数があるのですが、それを使って先日投稿したSample Programをちょっと書き換えてみました。

    割り込み処理側(送り側)(今回から使用)

    BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, BaseType_t *pxHigherPriorityTaskWoken );

    通常処理側(タスク処理側)(受け側)(以前から使用)

    uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );

    以下、プロジェクトのファイル一式です。(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に似せてあります。(なお、タスク通知に絡んだ変更の他にも、FreeRTOS kernelのソースをv10.2.1→v10.3.1へ更新したり、UART送信の転送モードやUART受信割り込み/UART受信エラー割り込みの優先順位、あれこれ細かな部分、等の変更も行っています。)

    rl78g14fpb_freertos_sampleprog1_ccrl_c_csplus_20200407.zip (*1)    456KB
    rl78g14fpb_freertos_sampleprog2_ccrl_c_csplus_20200407.zip (*1)    447KB
    rl78g14fpb_freertos_sampleprog3_ccrl_c_csplus_20200407.zip (*1)    507KB
    rl78g14fpb_freertos_sampleprog1_gnurl78_c_e2v760_20200407.zip (*1,*2)   422KB
    rl78g14fpb_freertos_sampleprog2_gnurl78_c_e2v760_20200407.zip (*1,*2)   413KB
    rl78g14fpb_freertos_sampleprog3_gnurl78_c_e2v760_20200407.zip (*1,*2)   474KB

    sampleprog1: UART送受信をDTCで行う版
    sampleprog2: UART送受信を割り込みのみで行う版
    sampleprog3: UART受信をリングバッファで行いUART送信を割り込みのみで行う版

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

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

    今回使ったAPI関数の日本語ドキュメントは以下にあります。

    xTaskNotifyFromISR() (今回から使用)
    docs.aws.amazon.com/ja_jp/freertos-kernel/latest/dg/task-notification.html#xtasknotify-and-xtasknotifyfromisr-api-functions


    ulTaskNotifyTake() (以前から使用)
    docs.aws.amazon.com/ja_jp/freertos-kernel/latest/dg/task-notification.html#ultasknotifytake-api-function


    これを以下のように使ってみました。(直接は使わずにラッパー関数を作って使うようにしました。)

    freertos_sampleprog2_ccrl_c/src/frtos_startup/freertos_start.c (sampleprog1,3も同じ)

    BaseType_t xTaskNotifyFromISR_R_Helper(TaskHandle_t *pxTask, uint32_t ulValue)
    {
        if (NULL != *pxTask)
        {
            BaseType_t sHigherPriorityTaskWoken = pdFALSE;

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

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

            portYIELD_FROM_ISR( sHigherPriorityTaskWoken );
        }

        return pdTRUE;

    } /* End of function xTaskNotifyFromISR_R_Helper() */
    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() */

    freertos_sampleprog2_ccrl_c/src/r_cg_serial_user.c (sampleprog1,3も同様)

    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;

            xTaskNotifyFromISR_R_Helper( &g_uart3_rx_task, 0x10000 | MD_OK );
        }

        /* 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 */

        U_UART3_Receive_Stop();
        g_uart3_rx_error_type = err_type;

        xTaskNotifyFromISR_R_Helper( &g_uart3_rx_task, 0x10000 | (err_type << 8) | MD_RECV_ERROR );

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

    freertos_sampleprog2_ccrl_c/src/r_cg_serial.c (sampleprog1,3も同様)

    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;
        uint32_t value;

        if (rx_num < 1U)
        {
            status = MD_ARGERROR;
        }
        else
        {
            g_uart3_rx_task = xTaskGetCurrentTaskHandle_R_Helper();
            U_UART3_Receive( rx_buf, rx_num );

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

            if (0U == value)
            {
                /* Timeout */
                U_UART3_Receive_Stop();
                g_uart3_rx_task = NULL;

                /* Check an unhandled notification from timeout till stop */
                value = ulTaskNotifyTake_R_Helper( 0 );
            }

            if (0U != value)
            {
                /* Normal receive end or receive error */
                *p_err_type = (value >> 8) & 0xFFU;
                status = value & 0xFFU;
            }
            else
            {
                /* Timeout */
                *p_err_type = SCI_EVT_RXWAIT_TMOT;
                status = MD_RECV_TIMEOUT;
            }
        }

        return status;
    }

    なお、以下の点を鑑み、UART受信割り込み/UART受信エラー割り込みの優先順位変更(sampleprog1,2,3)とUART送信の転送モード変更(sampleprog2,3)を行っています。

    本スレッド
    japan.renesasrulz.com/cafe_rene/f/forum21/5845/rl78-freertos-api-cc-rl-gnurl78/34294#34294

    今になって拙かったかなと思ったのは、多重割り込みは使用していないものの割り込み禁止期間中に割り込みが複数発生することはありますので、今回は割り込み優先順位が以下の通りになっている為に受信割り込みの受付がもっとも後回しにされてしまっている筈であり、そのせいでオーバーランエラーが発生してしまうのかな、と思い始めています。

    割り込み優先順位: 役割表記(上側)と割り込み要求名表記(下側)

    ソフトウェア割り込み(もどき)でのタスク切り替え処理用割り込み > ティック割り込み > 送信割り込み > 受信割り込み

    INTWDTI > INTIT > INTST3 > INTSR3


    チョコさんが以前に指摘されていたRL78コード生成機能によるCSI連続送信の終了の割り込みコードの問題を調べています
    japan.renesasrulz.com/cafe_rene/f/forum18/6217/rl78-csi/34513#34513

    今回の割り込みコードの問題はCSI連続送信でしたが、他のタイプの連続転送に関しても、コードを生成させて、生成コードを確認してみました。不具合原因のコードから推測すると、問題点の(2)の該当/非該当は以下の表のようになると思います。

    ...略...

    SAU UART連続送信モード(繰り返し送信モード)        該当?

    ...略...


    以下、上記に関してコード生成機能の設定を変更した箇所の画面コピーです。(以下の画面コピーはCC-RL+CS+のものですが、CC-RL+e2 studioでもGNURL78+e2 studioでも、同様です。)

    sampleprog1(UART送受信をDTCで行う版)


    sampleprog2(UART送受信を割り込みのみで行う版)とsample3(UART受信をリングバッファで行いUART送信を割り込みのみで行う版)


     

Reply
  • こんにちは。NoMaYです。

    FreeRTOSのタスク通知のAPIには、0以外の32ビット値を割り込み処理側から通常処理側(タスク処理側)へ通知することが出来る以下の関数があるのですが、それを使って先日投稿したSample Programをちょっと書き換えてみました。

    割り込み処理側(送り側)(今回から使用)

    BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, BaseType_t *pxHigherPriorityTaskWoken );

    通常処理側(タスク処理側)(受け側)(以前から使用)

    uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );

    以下、プロジェクトのファイル一式です。(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に似せてあります。(なお、タスク通知に絡んだ変更の他にも、FreeRTOS kernelのソースをv10.2.1→v10.3.1へ更新したり、UART送信の転送モードやUART受信割り込み/UART受信エラー割り込みの優先順位、あれこれ細かな部分、等の変更も行っています。)

    rl78g14fpb_freertos_sampleprog1_ccrl_c_csplus_20200407.zip (*1)    456KB
    rl78g14fpb_freertos_sampleprog2_ccrl_c_csplus_20200407.zip (*1)    447KB
    rl78g14fpb_freertos_sampleprog3_ccrl_c_csplus_20200407.zip (*1)    507KB
    rl78g14fpb_freertos_sampleprog1_gnurl78_c_e2v760_20200407.zip (*1,*2)   422KB
    rl78g14fpb_freertos_sampleprog2_gnurl78_c_e2v760_20200407.zip (*1,*2)   413KB
    rl78g14fpb_freertos_sampleprog3_gnurl78_c_e2v760_20200407.zip (*1,*2)   474KB

    sampleprog1: UART送受信をDTCで行う版
    sampleprog2: UART送受信を割り込みのみで行う版
    sampleprog3: UART受信をリングバッファで行いUART送信を割り込みのみで行う版

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

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

    今回使ったAPI関数の日本語ドキュメントは以下にあります。

    xTaskNotifyFromISR() (今回から使用)
    docs.aws.amazon.com/ja_jp/freertos-kernel/latest/dg/task-notification.html#xtasknotify-and-xtasknotifyfromisr-api-functions


    ulTaskNotifyTake() (以前から使用)
    docs.aws.amazon.com/ja_jp/freertos-kernel/latest/dg/task-notification.html#ultasknotifytake-api-function


    これを以下のように使ってみました。(直接は使わずにラッパー関数を作って使うようにしました。)

    freertos_sampleprog2_ccrl_c/src/frtos_startup/freertos_start.c (sampleprog1,3も同じ)

    BaseType_t xTaskNotifyFromISR_R_Helper(TaskHandle_t *pxTask, uint32_t ulValue)
    {
        if (NULL != *pxTask)
        {
            BaseType_t sHigherPriorityTaskWoken = pdFALSE;

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

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

            portYIELD_FROM_ISR( sHigherPriorityTaskWoken );
        }

        return pdTRUE;

    } /* End of function xTaskNotifyFromISR_R_Helper() */
    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() */

    freertos_sampleprog2_ccrl_c/src/r_cg_serial_user.c (sampleprog1,3も同様)

    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;

            xTaskNotifyFromISR_R_Helper( &g_uart3_rx_task, 0x10000 | MD_OK );
        }

        /* 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 */

        U_UART3_Receive_Stop();
        g_uart3_rx_error_type = err_type;

        xTaskNotifyFromISR_R_Helper( &g_uart3_rx_task, 0x10000 | (err_type << 8) | MD_RECV_ERROR );

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

    freertos_sampleprog2_ccrl_c/src/r_cg_serial.c (sampleprog1,3も同様)

    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;
        uint32_t value;

        if (rx_num < 1U)
        {
            status = MD_ARGERROR;
        }
        else
        {
            g_uart3_rx_task = xTaskGetCurrentTaskHandle_R_Helper();
            U_UART3_Receive( rx_buf, rx_num );

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

            if (0U == value)
            {
                /* Timeout */
                U_UART3_Receive_Stop();
                g_uart3_rx_task = NULL;

                /* Check an unhandled notification from timeout till stop */
                value = ulTaskNotifyTake_R_Helper( 0 );
            }

            if (0U != value)
            {
                /* Normal receive end or receive error */
                *p_err_type = (value >> 8) & 0xFFU;
                status = value & 0xFFU;
            }
            else
            {
                /* Timeout */
                *p_err_type = SCI_EVT_RXWAIT_TMOT;
                status = MD_RECV_TIMEOUT;
            }
        }

        return status;
    }

    なお、以下の点を鑑み、UART受信割り込み/UART受信エラー割り込みの優先順位変更(sampleprog1,2,3)とUART送信の転送モード変更(sampleprog2,3)を行っています。

    本スレッド
    japan.renesasrulz.com/cafe_rene/f/forum21/5845/rl78-freertos-api-cc-rl-gnurl78/34294#34294

    今になって拙かったかなと思ったのは、多重割り込みは使用していないものの割り込み禁止期間中に割り込みが複数発生することはありますので、今回は割り込み優先順位が以下の通りになっている為に受信割り込みの受付がもっとも後回しにされてしまっている筈であり、そのせいでオーバーランエラーが発生してしまうのかな、と思い始めています。

    割り込み優先順位: 役割表記(上側)と割り込み要求名表記(下側)

    ソフトウェア割り込み(もどき)でのタスク切り替え処理用割り込み > ティック割り込み > 送信割り込み > 受信割り込み

    INTWDTI > INTIT > INTST3 > INTSR3


    チョコさんが以前に指摘されていたRL78コード生成機能によるCSI連続送信の終了の割り込みコードの問題を調べています
    japan.renesasrulz.com/cafe_rene/f/forum18/6217/rl78-csi/34513#34513

    今回の割り込みコードの問題はCSI連続送信でしたが、他のタイプの連続転送に関しても、コードを生成させて、生成コードを確認してみました。不具合原因のコードから推測すると、問題点の(2)の該当/非該当は以下の表のようになると思います。

    ...略...

    SAU UART連続送信モード(繰り返し送信モード)        該当?

    ...略...


    以下、上記に関してコード生成機能の設定を変更した箇所の画面コピーです。(以下の画面コピーはCC-RL+CS+のものですが、CC-RL+e2 studioでもGNURL78+e2 studioでも、同様です。)

    sampleprog1(UART送受信をDTCで行う版)


    sampleprog2(UART送受信を割り込みのみで行う版)とsample3(UART受信をリングバッファで行いUART送信を割り込みのみで行う版)


     

Children
No Data