2024年10月3日木曜日

RAマイコンの扱いのメモ [FSP]

 
ここでは、RAマイコン(RA2E1グループ)を扱うときのちょっとしたメモを残しています。





ユーザープログラムはhal_entry()内に記述します。
hal_entry()はsrcのhal_entry.c内にあります。
main関数内に記述すると、FSP Configurationでの設定変更時にユーザー記述したプログラムが消えます。



・ポート入出力方向の設定例

FSPを通した場合のポート操作方法をメモします。
基本的にhttps://renesas.github.io/fsp/group___i_o_p_o_r_t.htmlに記述されている内容を簡単に例を挙げています。


・ピンごと
ポート P407を入力に設定する(デフォルト、高Z)
R_IOPORT_PinCfg(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_07,  IOPORT_CFG_PORT_DIRECTION_INPUT);

ポート P407を入力に設定する(プルアップ)
R_IOPORT_PinCfg(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_07,  IOPORT_CFG_PULLUP_ENABLE);

ポート P010をアナログ入力に設定する
R_IOPORT_PinCfg(&g_ioport_ctrl, BSP_IO_PORT_01_PIN_10,  IOPORT_CFG_ANALOG_ENABLE);

ポート P407を出力に設定する
R_IOPORT_PinCfg(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_07,  IOPORT_CFG_PORT_DIRECTION_OUTPUT);

・ポートまとめて
ポート P015, P013を出力、P014, P012入力に設定する(ポート P011~P000は変更なし)
R_IOPORT_PortDirectionSet(&g_ioport_ctrl, BSP_IO_PORT_00, 0xA000, 0xF000);
第四引数はマスクビットです。


・ポート操作例

・ピンごと
ポート P407の出力をLにする
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_07, 0);

ポート P407の出力をHにする
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_07, 1);

・ポートまとめて
ポート P103~P100にL,H,L,Hをセットする(ポート P104~P115は変更なし)
R_IOPORT_PortWrite(&g_ioport_ctrl, BSP_IO_PORT_01, 0x0005, 0x000F);
第四引数はマスクビットです。


・ポートの読み取り

・ピンごと
ポート P200の状態を取得する
bsp_io_level_t pin_read;//ピンの状態(1bit)が入る
R_IOPORT_PinRead(&g_ioport_ctrl, BSP_IO_PORT_02_PIN_00, &pin_read);

・ポートまとめて
ポート P103~P100の状態を取得する
uint16_t port_read;//ピンの状態(16bit)が入る
R_IOPORT_PortRead(&g_ioport_ctrl, BSP_IO_PORT_01, &port_read);




・UART関係

シリアルデータの送受信の準備
FSPコンフィグの「Stacks」でUART(r_sci_uart)を追加しないと使えません。追加したUART(r_sci_uart)のプロパティでUARTの設定ができます。
今回はチャンネル9を例とします。

通信開始
R_SCI_UART_Open(&g_uart9_ctrl, &g_uart9_cfg);


コールバック関数を設定する

fsp_err_t R_SCI_UART_CallbackSet (&g_uart9_ctrl, 「実行するコールバック関数のポインタ」, NULL, NULL);

「実行するコールバック関数のポインタ」に「実行するコールバック関数名」をセットします。
受信完了や送信準備完了、UART関係のエラーなど時の割り込み発生時、最終的に「実行するコールバック関数のポインタ」にセットした関数へ飛びます。

*コールバック関数の例*
void 「実行するコールバック関数名」(uart_callback_args_t * p_args){
    switch(p_args->event){
        case UART_EVENT_RX_COMPLETE:   ///< Receive complete event
            //受信完了
            break;

        case UART_EVENT_TX_COMPLETE:   ///< Transmit complete event
            //送信完了
            break;

        case UART_EVENT_RX_CHAR:       ///< Character received
            //文字の受信
            break;

        case UART_EVENT_ERR_PARITY:    ///< Parity error event
            //パリティエラー
            break;

        case UART_EVENT_ERR_FRAMING:   ///< Mode fault error event
            //フレームエラー
            break;

        case UART_EVENT_ERR_OVERFLOW:  ///< FIFO Overflow error event
            //FIFOオーバーフロー
            break;

        case UART_EVENT_BREAK_DETECT:  ///< Break detect error event
            //破壊検出
            break;

        case UART_EVENT_TX_DATA_EMPTY: ///< Last byte is transmitting, ready for more data
            //送信データをセット可能
            break;

        default:
            break;
    }
}


シリアルデータの受信
R_SCI_UART_Read(&g_uart9_ctrl, uint8_t * const p_dest, uint32_t const bytes);

「p_dest」に受信データの格納先のポインタを引数として渡す。

R_SCI_UART_Read実行後の累計受信データ数が引数「bytes」と同じ値になると、
R_SCI_UART_CallbackSetで設定したコールバック関数が実行されます。
このコールバック関数のp_args->eventの値はUART_EVENT_RX_COMPLETEとなり実行されます。
つまり、R_SCI_UART_Readの引数「bytes」分受信されると、コールバック関数が引数UART_EVENT_RX_COMPLETEで実行されます。

コールバック関数の引数がUART_EVENT_RX_CHARとなるとき、R_SCI_UART_Read関数で指定した「bytes」外となったことを示します。

R_SCI_UART_Read関数を実行しない場合、UART_EVENT_RX_CHAR利用して1バイトずつ受信することができます。
この場合、1バイト受信するたびにコールバック関数の引数がUART_EVENT_RX_CHARで実行されます。
コールバック関数の引数はUART_EVENT_RX_COMPLETEとならないので注意してください。
受信したデータは(uint8_t) p_args->dataに格納されています。


シリアルデータの送信
R_SCI_UART_Write(&g_uart9_ctrl, uint8_t const * const p_src, uint32_t const bytes);

「p_src」に受信データの格納先のポインタを引数として渡します。
「bytes」に送信するデータのサイズを指定します。
R_SCI_UART_Write関数による送信が完了すると、コールバック関数が引数UART_EVENT_TX_COMPLETEとして実行されます。




タイマー関係



タイマーのコールバックの引数
    TIMER_EVENT_CYCLE_END,                     ///< Requested timer delay has expired or timer has wrapped around
    TIMER_EVENT_CREST = TIMER_EVENT_CYCLE_END, ///< Timer crest event (counter is at a maximum, triangle-wave PWM only)
    TIMER_EVENT_CAPTURE_A,                     ///< A capture has occurred on signal A
    TIMER_EVENT_CAPTURE_B,                     ///< A capture has occurred on signal B
    TIMER_EVENT_TROUGH,                        ///< Timer trough event (counter is 0, triangle-wave PWM only
    TIMER_EVENT_COMPARE_A,                     ///< A compare has occurred on signal A
    TIMER_EVENT_COMPARE_B,                     ///< A compare has occurred on signal B
    TIMER_EVENT_COMPARE_C,                     ///< A compare has occurred on signal C
    TIMER_EVENT_COMPARE_D,                     ///< A compare has occurred on signal D
    TIMER_EVENT_COMPARE_E,                     ///< A compare has occurred on signal E
    TIMER_EVENT_COMPARE_F,                     ///< A compare has occurred on signal F
    TIMER_EVENT_DEAD_TIME                      ///< Dead time event



・RAマイコンが正常に起動しない時の確認

サブクロック生成用端子XCINに水晶振動子が接続されていない状態でマイコンを起動すると、正常動作しないことがあります。

これは、 サブクロック生成が有効になった状態で起動することにより、マイコンのサブクロック生成回路が正常発振せず、リセット開放へ移行できなくなると思われます。

サブクロックを無効にすることで、正常動作するようになるかもしれません。


・サブクロック無効化手順
「FSP Configuration」→「BSP」タブ→下の「プロパティ」タブにある
「Subclock Populated」の設定を確認します。

もし、「Populated」に設定されている場合、サブクロック生成が有効になっているため、「Not Populated」に設定してください。



・RAマイコンの電源電圧の設定

「FSP Configuration」→「BSP」タブ→「プロパティ」タブにある
「MCU Vcc (mV)」で設定を変更できます。




・内臓温度センサの温度取得

温度センサの温度を取得するための準備
FSPコンフィグの「Stacks」でADC(r_adc)を追加しないと使えません。追加したADC(r_adc)のプロパティでADCの設定ができます。

ADC(r_adc)のプロパティの設定項目
General→Mode : Single Scan
Input→Channel Scan Mask (channel availability varies by MCU)→Temperature Sensor : ☑
Input→Reference Voltage control : AVCC0/AVSS0

プログラム例////////////////////////////////////////////////////////////////////////////////////////////////////////
#define PWR_V 5.0 //AVCC0の電圧

uint8_t str_buf[256];
uint16_t adc_data;
uint32_t tmp_ref_cal_data;
int16_t tmp_ref_cal_slope;
volatile float v1; 
volatile float vs;
volatile float tmp_val;

//UART準備
R_SCI_UART_Open(&g_uart9_ctrl, &g_uart9_cfg);

//温度センサキャリブレーション値の取得
R_ADC_Open(&g_adc0_ctrl, &g_adc0_cfg);
R_ADC_InfoGet(&g_adc0_ctrl, &adc_info);
tmp_ref_cal_data=adc_info.calibration_data;//125℃の時のADC値(VCC=3.3V)
tmp_ref_cal_slope=adc_info.slope_microvolts;//温度対する電圧変化(μV)
R_ADC_Close(&g_adc0_ctrl);

//ADC
R_ADC_Open(&g_adc0_ctrl, &g_adc0_cfg);
R_ADC_ScanCfg(&g_adc0_ctrl, &g_adc0_channel_cfg);
R_ADC_ScanStart(&g_adc0_ctrl);
R_BSP_SoftwareDelay(10, BSP_DELAY_UNITS_MILLISECONDS);
R_ADC_Read(&g_adc0_ctrl, ADC_CHANNEL_TEMPERATURE, &adc_data);
R_ADC_Close(&g_adc0_ctrl);

//計算
v1 = 3.3 * (float)tmp_ref_cal_data / 4096.0;
vs = PWR_V * (float)adc_data / 4096.0;
tmp_val = ((vs - v1) / ((float)tmp_ref_cal_slope/1000000.0)) + 125.0;

//UART表示
sprintf((char*)str_buf,"MCU temp[m℃] : %d \r\n", (int)(tmp_val*1000.0));
R_SCI_UART_Write(&g_uart9_ctrl,str_buf,sizeof(str_buf));
////////////////////////////////////////////////////////////////////////////////////////////////////////


・ADCの基準電圧でInternal Reference Voltageにすると内臓温度センサの温度取得ができない・異常な値が出る

RA2E1グループ ユーザーズマニュアル ハードウェア編の
"29.7 高電位基準電圧に内部基準電圧を選択する A/D 変換手順"
に記述があります。
これによるとMCU内臓温度センサの出力値の測定において、
ADCの基準電圧としてMCU内部で生成した内部基準電圧を利用することは禁止されています。

残念ですがあきらめましょう。

ちなみにRA2E1グループの内部基準電圧値は1.48V±4%です。

外部基準電圧を用意するのが楽で良さそうです。
内部基準電圧をどうしても利用したい場合、
まずADC電源電圧を分圧したもの(1.4V未満になるようにする)を内部基準電圧で測定してADC電源電圧を割り出したのち、
ADC電源電圧を基準電圧としてMCU内臓温度センサの出力電圧を測定する方法があります。
このとき、ADC電源電圧でなく外部基準電圧端子を利用したほうが良さそうな気がしますね。



・内臓温度センサの温度計算が合わない・異常な値が出る

MCU内臓温度センサの温度による電圧変化slopeは標準で-3.3mV/℃です。この温度センサは25度で標準1.05Vの電圧を出力します。

(RA2E1グループ ユーザーズマニュアル ハードウェア編の"30.3.1 使用前の準備"の表記だとslopeはV/1000[V/℃]表記となっています。)

温度を算出するとき、ちょっと注意点があります。
FSPによる標準電圧変化値取得関数R_ADC_InfoGetの戻り値slope_microvoltsの単位がμV/℃となっている点です。
"30.3.1 使用前の準備"の式に当てはめる際のslope値の単位はV/℃なので、
slope = 何か名前.slope_microvolts/1000000.0
としなければいけません。

"30.3.1 使用前の準備"の単位の表記、R_ADC_InfoGetの取得値の単位、
RA2E1グループのデータシートに記述されているslopeの単位がそれぞれ違う(V/1000[V/℃], μV/℃, mV/℃)というハマりポイントですね。
(私もこれで時間を浪費しました。)



・文字列をフラッシュメモリ(コードフラッシュ)上に配置する

Arduinoなどで文字列データをプログラムメモリ上に配置するとき、よくFマクロを使います。例 : "TEST" → F("TEST")

RAの場合は、Fマクロが用意されていません。
基本的には、変数を定義するときに"const"をつけると、文字列はコードフラッシュメモリ上のみに配置されます。

例:
uint8_t str[] = "TEST";//起動後、RAM上に配置
const uint8_t str[] = "TEST";//起動後、RAM上に配置せず、コードフラッシュから参照される

コンパイラの設定にもよりますが、"const"を付け足すだけでコードフラッシュ上に配置できます。
一応、メモリ領域使用量の変化を確認したところ、"const"を付けずに文字列変数として追加したときは「プログラム」、「初期化済みデータ」および「データ」領域が増加しました。
「初期化済みデータ」は文字列変数として確保したRAMを示しています。
"const"を付けて文字列定数として追加したときは「プログラム」領域のみ増加しました。

もちろん文字列データだけでなく、変数に対して"const"をつけて定数にした場合もRAMの節約になります。


文字列データ限定ですが、「ポインタ初期化+文字列」で文字列データをフラッシュ上に配置する方法もできます。

例:
uint8_t * str = "TEST";
const uint8_t * str = "TEST";
基本的に"const"の有無にかかわらず、文字列データはフラッシュメモリ上にのみ入ります。
この場合、ポインタ変数としてのRAM確保が起こるため、const uint8_t str[] = "TEST";より数バイト(文字列の長さに依存しない)多くRAM領域を使います。



・ユニークIDの取得
だいたいのマイコンには、そのマイコン固有のユニークIDが割り振られています。(UID)
RAマイコンの場合、128ビット幅のUIDが割り振られています。

R_BSP_UniqueIdGet()関数によって、UIDが格納されているアドレスが取得できます。

・ユニークIDの表示
        sprintf((char*)str_buf,"UID_31_0 : %x \r\n", (uint32_t)(R_BSP_UniqueIdGet()->unique_id_words[0]));
        R_SCI_UART_Write(&g_uart9_ctrl,str_buf,sizeof(str_buf));
        R_BSP_SoftwareDelay(50, BSP_DELAY_UNITS_MILLISECONDS);
        sprintf((char*)str_buf,"UID_63_32 : %x \r\n", (uint32_t)(R_BSP_UniqueIdGet()->unique_id_words[1]));
        R_SCI_UART_Write(&g_uart9_ctrl,str_buf,sizeof(str_buf));
        R_BSP_SoftwareDelay(50, BSP_DELAY_UNITS_MILLISECONDS);
        sprintf((char*)str_buf,"UID_95_64 : %x \r\n", (uint32_t)(R_BSP_UniqueIdGet()->unique_id_words[2]));
        R_SCI_UART_Write(&g_uart9_ctrl,str_buf,sizeof(str_buf));
        R_BSP_SoftwareDelay(50, BSP_DELAY_UNITS_MILLISECONDS);
        sprintf((char*)str_buf,"UID_127_96 : %x \r\n", (uint32_t)(R_BSP_UniqueIdGet()->unique_id_words[3]));
        R_SCI_UART_Write(&g_uart9_ctrl,str_buf,sizeof(str_buf));




・スタック解析

スタック解析を実行すると、呼び出し先の関数のアドレスとスタックサイズを見ることができます。
スタック解析を実行するためには、スタック情報を出力する設定を行う必要があります。


・スタック情報を出力する設定
e2studioの一番上のタブ「プロジェクト(P)」→「C/C++ Project Settings」→左のリスト「⌵C/C++ビルド」→「設定」→「ツール設定」タブ→リスト中「⌵GNU Arm Cross C Compiler」→「Miscellaneous」→「Other compiler flags」のボックス中テキストの後ろに"-fstack-usage -fdump-rtl-pro_and_epilogue"を追加する。

・スタック解析の表示
e2studioの一番上のタブ「Renesas Views」」→「C/C++ 」→「スタック解析 」







0 件のコメント:

コメントを投稿