RL78のDTCのchain転送を使って6chのanalog入力を(ほぼ)連続convertさせてみた(CC-RL/CSplus)

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

別スレッド『ADコンバータとDTC転送(ソフトウエアトリガ、スキャン、ワンショット変換)で他の端子のアナログデータを読み込む』で、RL78では5チャンネル以上のアナログ入力を連続変換する場合には4チャンネル毎に割り込み処理が必要になる、ということを最近理解したのですが、DTCのチェイン転送を使えば4チャンネル毎の割り込み処理が不要になり最後に1回だけの割り込み処理で済ますことが出来そうな気がしたので試してみました。以下、プロジェクトのファイル一式です。実機(RL78/G14 Fast Prototyping Board)とシミュレータ(Renesas RL78 Simulator)で試しました。(今回は私の毎度の投稿とは違い、CC-RL/CS+のみです。もっと簡単なやり方が思い浮かんだ時に、GNURL78/e2 studioについてもやってみようと思います。)

プロジェクトのファイル一式:
issue_20200317.zip

今回、6チャンネルのアナログ入力(RL78/G14 Fast Prototyping BoardのArduinoソケットのA5~A0)を以下の(3)~(5)のDTCのチェイン転送を繰り返すことで(ほぼ)連続変換させました。(途中で長時間の他のDTC転送が発生すると間が空いてしまいますが。)

DTC転送前の準備:

(1) ADSレジスタに1チャンネル目のANI入力番号を書いておく

1チャンネル目の変換のDTC転送(TAU0のチャンネル0で1ms毎に起動させています):

(2) ADM0レジスタのADCSビットに1を書く(DTC転送ではビット操作出来ないので8ビット転送します)

2チャンネル目~6チャンネル目の変換のDTC転送:

(3) ADCRレジスタからRAM上の変換結果の配列へ転送する(10ビットA/D変換にしましたので16ビット転送します)
(4) ADSレジスタへ変換するチャンネル番号の配列からANI番号を転送する
(5) ADM0レジスタのADCSビットに1を書く(DTC転送ではビット操作出来ないので8ビット転送します)

(4') 最後の転送ではADSレジスタへ内部基準電圧変換指定番号を転送する
(5') 最後の転送ではADM0レジスタのADCSビットを0のままにする(DTC転送ではビット操作出来ないので8ビット転送します)

なお、転送先と転送元のRAM上の配列ですが、以下の配列を宣言してDTC転送に使用しています。

volatile uint16_t g_adc_adcr[ADC_CH_LIST_NUM]; /* +1 is not necessary */
ad_channel_t g_adc_ads[ADC_CH_LIST_NUM + 1] = 
{
#if ADC_CH_LIST_NUM >= 1
    ADC_CH_LIST_00,
#endif
。。。途中省略。。。
#if ADC_CH_LIST_NUM >= 19
    ADC_CH_LIST_18,
#endif
    ADINTERREFVOLT /* dummy */
};
uint8_t g_adc_adm0[ADC_CH_LIST_NUM + 1] = 
{
#if ADC_CH_LIST_NUM >= 1
    ADC_ADM0_COMMON | ADC_ADM0_CONVERSION_ENABLE,
#endif
。。。途中省略。。。
#if ADC_CH_LIST_NUM >= 19
    ADC_ADM0_COMMON | ADC_ADM0_CONVERSION_ENABLE,
#endif
    ADC_ADM0_COMMON | ADC_ADM0_CONVERSION_DISABLE
};

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

・コード生成されたソースのユーザ記述部の内容(抜粋)

src/r_main.c
src/r_cg_dtc.c
src/r_cg_adc.h
src/r_cg_adc_user.c

・コード生成機能の設定画面(抜粋)

DTC転送
A/D変換
タイマ

以下、実行させた時のCS+とTeraTermの画面コピーです。

Renesas RL78 Simulatorの場合


RL78/G14 Fast Prototyping Boardの場合(Arduinoソケットは A5(ANI1)=VSS, A0(ANI6)=VDD, 他はOPEN としています)

 

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

    以下、コード生成されたソースのユーザ記述部の内容(抜粋)です。

    src/r_main.c

    void main(void)
    {
        R_MAIN_UserInit();
        /* Start user code. Do not edit comment generated here */
        {
            uint16_t adc_1ms_count;
            uint8_t adc_ch;

            R_UART3_Start();

            U_DTCD0_Start(); /* enable A/D converter DTC */
            U_ADC_Interrupt_Enable();

            U_DTCD3_Start(); /* enable TAU0 Channel0 DTC */
            R_TAU0_Channel0_Start();

            adc_1ms_count = 0;
            while (1U)
            {
    #if 1
                NOP();
    #elif 0
                DI();
                NOP30000(); /* 1 / 32MHz * 30000 `=. 940us */
                EI();
                NOP2000();
    #endif
                if (0 != g_adc_ready_flag)
                {
                    g_adc_ready_flag = 0;
                    if (ADC_PRINTF_PERIOD_MS <= ++adc_1ms_count)
                    {
                        adc_1ms_count = 0;
                        for (adc_ch = 0; adc_ch < ADC_CH_LIST_NUM; adc_ch++)
                        {
                            ani_volt[adc_ch] = ADC_VDD_VOLT / 1023 * (g_adc_adcr[adc_ch] >> 6);
                        }
                        if (!IsRenesasSimDebugMode())
                        {
                            printf( "A5-ANI1=%3.1fV "
                                    "A4-ANI2=%3.1fV "
                                    "A3-ANI3=%3.1fV "
                                    "A2-ANI4=%3.1fV "
                                    "A1-ANI5=%3.1fV "
                                    "A0-ANI6=%3.1fV\r\n",
                                    ani_volt[0],
                                    ani_volt[1],
                                    ani_volt[2],
                                    ani_volt[3],
                                    ani_volt[4],
                                    ani_volt[5]);
                        }
                    }
                }
            }
        }
        /* End user code. Do not edit comment generated here */
    }

    r_cg_dtc.c

    /* Start user code for adding. Do not edit comment generated here */

    void U_DTCD0_Start(void)
    {
        dtc_controldata_0.dtcct = ADC_CH_LIST_NUM;
        dtc_controldata_0.dtsar = (uint16_t)&ADCR;
        dtc_controldata_0.dtdar = (uint16_t)&g_adc_adcr[0];

        dtc_controldata_1.dtcct = ADC_CH_LIST_NUM;
        dtc_controldata_1.dtsar = (uint16_t)&g_adc_ads[1];
        dtc_controldata_1.dtdar = (uint16_t)&ADS;

        dtc_controldata_2.dtcct = ADC_CH_LIST_NUM;
        dtc_controldata_2.dtsar = (uint16_t)&g_adc_adm0[1];
        dtc_controldata_2.dtdar = (uint16_t)&ADM0;

        R_DTCD0_Start();
    }

    void U_DTCD3_Start(void)
    {
        ADS = g_adc_ads[0];

        dtc_controldata_3.dtcct = 1;
        dtc_controldata_3.dtsar = (uint16_t)&g_adc_adm0[0];
        dtc_controldata_3.dtdar = (uint16_t)&ADM0;

        R_DTCD3_Start();
    }

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

    r_cg_adc.h

    /* Start user code for function. Do not edit comment generated here */

    #define ADC_CH_LIST_NUM 6

    #define ADC_CH_LIST_00 ADCHANNEL1
    #define ADC_CH_LIST_01 ADCHANNEL2
    #define ADC_CH_LIST_02 ADCHANNEL3
    #define ADC_CH_LIST_03 ADCHANNEL4
    #define ADC_CH_LIST_04 ADCHANNEL5
    #define ADC_CH_LIST_05 ADCHANNEL6
    #define ADC_CH_LIST_06 /* not used */
    #define ADC_CH_LIST_07 /* not used */
    #define ADC_CH_LIST_08 /* not used */
    #define ADC_CH_LIST_09 /* not used */
    #define ADC_CH_LIST_10 /* not used */
    #define ADC_CH_LIST_11 /* not used */
    #define ADC_CH_LIST_12 /* not used */
    #define ADC_CH_LIST_13 /* not used */
    #define ADC_CH_LIST_14 /* not used */
    #define ADC_CH_LIST_15 /* not used */
    #define ADC_CH_LIST_16 /* not used */
    #define ADC_CH_LIST_17 /* not used */
    #define ADC_CH_LIST_18 /* not used */

    。。。途中省略。。。

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

    r_cg_adc_user.c

    /* Start user code for global. Do not edit comment generated here */

    #define ADC_ADM0_COMMON (_00_AD_CONVERSION_CLOCK_64 | _00_AD_TIME_MODE_NORMAL_1 \
    | _00_AD_OPERMODE_SELECT | _01_AD_COMPARATOR_ENABLE)
    #define ADC_ADM0_CONVERSION_ENABLE  (_80_AD_CONVERSION_ENABLE)
    #define ADC_ADM0_CONVERSION_DISABLE (_00_AD_CONVERSION_DISABLE)

    ad_channel_t g_adc_ads[ADC_CH_LIST_NUM + 1] =
    {
    #if ADC_CH_LIST_NUM >= 1
        ADC_CH_LIST_00,
    #endif
    。。。途中省略。。。
    #if ADC_CH_LIST_NUM >= 19
        ADC_CH_LIST_18,
    #endif
        ADINTERREFVOLT /* dummy */
    };
    uint8_t g_adc_adm0[ADC_CH_LIST_NUM + 1] =
    {
    #if ADC_CH_LIST_NUM >= 1
        ADC_ADM0_COMMON | ADC_ADM0_CONVERSION_ENABLE,
    #endif
    。。。途中省略。。。
    #if ADC_CH_LIST_NUM >= 19
        ADC_ADM0_COMMON | ADC_ADM0_CONVERSION_ENABLE,
    #endif
        ADC_ADM0_COMMON | ADC_ADM0_CONVERSION_DISABLE
    };
    volatile uint16_t g_adc_adcr[ADC_CH_LIST_NUM]; /* +1 is not necessary */

    volatile uint8_t g_adc_ready_flag = 0;

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

        U_DTCD0_Start(); /* enable A/D converter DTC */
        U_DTCD3_Start(); /* enable TAU0 Channel0 DTC */

        g_adc_ready_flag = 1;

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

    void U_ADC_Interrupt_Enable(void)
    {
        ADMK = 0U;  /* enable INTAD interrupt */
    }

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

    以下、コード生成機能の設定画面(抜粋)です。

    DTC転送 (転送元アドレスと転送先アドレスは仮の値を設定してます(あっ、転送回数もそうするつもりが忘れてます、、、))






    A/D変換 (今回は影響無いことですが割り込み優先順位を1段階上げています)


    タイマ



    [追記]

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

  • こんにちは。NoMaYです。

    先日の投稿のDTCのチェイン転送に、あと2つ8ビット転送を追加して、空き端子の出力をlow→high→lowしてやれば、オシロなどで転送タイミングを可視化することが出来るかも知れない、という考えが思い浮かびましたので試してみました。以下のRenesas RL78 Simulatorの画面コピーの通り、可視化することが出来ました。(今回はRL78/G14 Fast Prototyping Boardでは試してはいません。)

    プロジェクトのファイル一式:
    issue_20200317_2_0321.zip

    A/D変換時間=4.75μs @ CPU動作周波数=8MHz の場合


    A/D変換時間=19μs @ CPU動作周波数=8MHz の場合


    以下、追加したチェイン転送に関するコード生成機能の画面コピーです。(転送元アドレスと転送先アドレスと転送回数は仮の値を設定しています。)





    以下、追加したチェイン転送に関するコード生成されたソースのユーザ記述部の内容です。(青文字部分と紫文字部分を追加しています。これらの他に、追加したチェイン転送を挿入したことによる変更もあります。)

    src/r_cg_dtc.c

    void U_DTCD0_Start(void)
    {
        dtc_controldata_0.dtcct = ADC_CH_LIST_NUM;
        dtc_controldata_0.dtsar = (uint16_t)&ADCR;
        dtc_controldata_0.dtdar = (uint16_t)&g_adc_adcr[0];

        dtc_controldata_1.dtcct = ADC_CH_LIST_NUM;
        dtc_controldata_1.dtsar = (uint16_t)&g_adc_ads[1];
        dtc_controldata_1.dtdar = (uint16_t)&ADS;

        dtc_controldata_2.dtcct = ADC_CH_LIST_NUM;
        dtc_controldata_2.dtsar = (uint16_t)&g_adc_adm0[1];
        dtc_controldata_2.dtdar = (uint16_t)&ADM0;

        dtc_controldata_3.dtcct = ADC_CH_LIST_NUM;
        dtc_controldata_3.dtsar = (uint16_t)&g_adc_p1_high[1];
        dtc_controldata_3.dtdar = (uint16_t)&P1;

        dtc_controldata_4.dtcct = ADC_CH_LIST_NUM;
        dtc_controldata_4.dtsar = (uint16_t)&g_adc_p1_low[1];
        dtc_controldata_4.dtdar = (uint16_t)&P1;

        R_DTCD0_Start();
    }

    src/r_cg_adc_user.c

    //#define ADC_ADM0_COMMON (_00_AD_CONVERSION_CLOCK_64 | _00_AD_TIME_MODE_NORMAL_1 | _00_AD_OPERMODE_SELECT | _01_AD_COMPARATOR_ENABLE)
    //#define ADC_ADM0_COMMON (_18_AD_CONVERSION_CLOCK_8 | _00_AD_TIME_MODE_NORMAL_1 | _00_AD_OPERMODE_SELECT | _01_AD_COMPARATOR_ENABLE)
    #define ADC_ADM0_COMMON (_38_AD_CONVERSION_CLOCK_2 | _00_AD_TIME_MODE_NORMAL_1 | _00_AD_OPERMODE_SELECT | _01_AD_COMPARATOR_ENABLE)
    #define ADC_ADM0_CONVERSION_ENABLE  (_80_AD_CONVERSION_ENABLE)
    #define ADC_ADM0_CONVERSION_DISABLE (_00_AD_CONVERSION_DISABLE)
    uint8_t g_adc_p1_high[ADC_CH_LIST_NUM + 1];
    uint8_t g_adc_p1_low[ADC_CH_LIST_NUM + 1];
    void U_ADC_Interrupt_Enable(void)
    {
        ADMK = 0U;  /* enable INTAD interrupt */

        memset( (char *)g_adc_p1_high, 0x01, ADC_CH_LIST_NUM + 1 );
        memset( (char *)g_adc_p1_low , 0x00, ADC_CH_LIST_NUM + 1 );
    }

     

  • 小生も、速度が必要なところは、チェイン転送まで使って、意地でもDTCでやります。
    場合によりますが、DTCがバスを占有してしまう時間を分散したいので、例えば8chだと4chX2回にすべきか、1chX8回にすべきか考えたりします。
    あと、AD変換結果レジスタへのアクセスが遅く、RAMへのアクセスは速いマイコンの場合(ICLKが速いマイコン?)、そのアクセス時間も削りたいので、単にRAMにDTC転送するように書いたりしています。ch数が多いと結構効いてきます。同様にSCIの受信データなども。
    他にもテクがあれば教えて下さい!