*注意*

近年、AIによるサイト内情報の要約や解説など便利な機能が増えています。 ですが、技術系のブログについてのAI要約はまだまだ不正確な記述をする場合が多いです。 AI要約や解説を鵜呑みにせず、元記事を参照して必ず自らの目で確かめて判断するようにしてください。 *Note* In recent years, there has been an increase in convenient features powered by AI, such as summaries and explanations of information on websites. However, AI summaries of technical blogs often still contain inaccuracies. Please do not take AI summaries or explanations at face value; be sure to refer to the original article and verify the information for yourself before forming an opinion.

2024年10月3日木曜日

RAマイコンの扱いのメモ 使い方等 [FSPなど]

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

なお、言及するFSPのバージョンは5.4.0のものなので、別のバージョンのFSPだと参考にならないかもしれません。



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


・FDT&非同期シリアル通信で書き込むときの設定

非同期シリアル通信による書き込み用にsrecファイル(SH時代はmotファイルでしたね)を生成するには以下の2つの設定が必要です。

「プロジェクト(P)」→「C/C++ Project Settings」→左の「C/C++  ビルド」→「設定」→「ツール設定」タブ→「GNU Arm Cross Create Flash Image」→「General」を選択して右側の「Output file format」を「Motorola S-record」を選択

「プロジェクト(P)」→「C/C++ Project Settings」→左の「C/C++  ビルド」→「設定」→「Toolchain」タブ内→「Create Flash Image」のチェックボックスにチェックを入れる

以上の設定を行い、ビルドしたときに.srecファイルが生成されていたら成功です。


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

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として実行されます。



ボーレートを変更して開始する
FSPのプロパティ設定値のボーレートを変更してから通信開始したい場合の例
    uart_cfg_t g_uart9_cfg_u=g_uart9_cfg;
    sci_uart_extended_cfg_t g_uart9_cfg_extend_u=g_uart9_cfg_extend;
    baud_setting_t g_uart9_baud_setting;

    
    R_SCI_UART_BaudCalculate(9600, false, 5000, &g_uart9_baud_setting);
    g_uart9_cfg_u=g_uart9_cfg;
    g_uart9_cfg_extend_u=g_uart9_cfg_extend;
    g_uart9_cfg_extend_u.p_baud_setting=&g_uart9_baud_setting;
    g_uart9_cfg_u.p_extend= &g_uart9_cfg_extend_u;
    R_SCI_UART_Open(&g_uart9_ctrl, &g_uart9_cfg_u);

R_SCI_UART_BaudSet関数が別途用意されていますが、なぜかボーレートが変更されない場合は上記例を試してみてください。
クロックの状態によっては上記の方法が使えない可能性があります。


ソフトウェアウェイト

Arduinoのdelay関数の代わりの1つとして利用できます。

R_BSP_SoftwareDelay(「待ち時間」, 「接頭語」);
「接頭語」に以下のいずれかを設定します。
BSP_DELAY_UNITS_SECONDS : 秒
BSP_DELAY_UNITS_MILLISECONDS : m秒
BSP_DELAY_UNITS_MICROSECONDS : μ秒
「待ち時間」に時間を指定します。

*ソフトウェアウェイトの例*
//0.1秒待つ
 R_BSP_SoftwareDelay(100, BSP_DELAY_UNITS_MILLISECONDS);


タイマー割り込み関係



タイマーのコールバックの引数
    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));




・データフラッシュ

近年のマイコンはEEPROMが入っておらず、代わりにデータフラッシュ領域が用意されています。
このデータフラッシュ領域の書き換え可能回数(耐性)は、プログラム領域の書き換え可能回数より多く、比較的頻度の高いデータ書き換えを想定しています。
つまり、設定データなどを残したいときは、EEPROMの代わりにデータフラッシュ領域を使用すればよいということです。

EEPROMとデータフラッシュ領域の決定的に異なるところは、データの削除方法です。
EEPROMの場合、1ワード単位(1バイトまたは2バイト)でデータの削除や書き換えが可能ですが、データフラッシュの場合、削除(イレース)はブロック単位(RA2E1の場合1024バイト)しかできません。書き込みは1バイト単位で可能です。
よって、EEPROMと同じ使い方は不可能です。

例えば1024バイトのデータのうち1バイトだけ書き換えたい場合、書き換えない1023バイトを一旦RAMに退避させて1024バイト削除し1024バイトを書き込む必要があります。
データフラッシュといえど、書き換え可能回数はそれほど多くなく、この例の方法ではあっという間に書き込み寿命を迎えてしまいます。
なので、実用するにはできるだけページ削除を実行しないように工夫する必要があります。

簡単な方法としては、アドレスとデータをデータフラッシュに書き込み、データが更新されるごとに空き領域にアドレスとデータを追加で書き込む方法です。データフラッシュ領域が満タンになったら古い領域をブロック削除します。
この方法ではデータだけでなく、アドレスやほかの情報も書き込むため、データの保存として使える容量が減ります。また、ページ削除のアルゴリズムが少し面倒です。

もう一つは、装置の電源を切るタイミングでデータ書き込みを行う方法です。装置が起動したタイミングであらかじめデータフラッシュのデータをRAMに退避させてデータフラッシュをブロック削除します。
データの更新はRAM上のデータの書き換えのみ行います。
装置の電源電圧を監視して電圧が低下し始めたタイミングでデータフラッシュにデータを一気に書き込みます。
この方法では、ADCでの電源電圧の監視が必要になります。さらに電源断後にマイコンが動作する電圧である内にデータを書き終えなければいけません。

これ以外にも方法はありますが、基本的にこのような感じでしょう。


データフラッシュ読み書きの準備
FSPコンフィグの「Stacks」でFlash(r_flash_lp)を追加しないと使えません。追加したFlash(r_flash_lp)のプロパティでフラッシュ書き込み許可などの設定ができます。
バックグラウンドでデータフラッシュの書き込みを行うには、プロパティ「Data Flash Background Operation」をEnabled、「Flash Ready Interrupt Priority」をEnabledにする必要があり、「Callback」にコールバック関数を設定する必要があります。
初期設定はそれぞれEnabled、Disabled、NULLになっているので、バックグラウンド書き込みをしない場合は「Data Flash Background Operation」をDisabledにしてください。

インスタンスを開く
    R_FLASH_LP_Open(&g_flash0_ctrl, &g_flash0_cfg);


ブロック削除
    R_FLASH_LP_Erase(&g_flash0_ctrl, 「開始アドレス」, 「ブロック数」);
「開始アドレス」にデータフラッシュ領域のリードアドレス値を指定します。(ハードウェアマニュアル記載のP/Eアドレスではありません。)
RA2E1の場合、
ブロック0 : 0x40100000 ~ 0x401003FF
ブロック1 : 0x40100400 ~ 0x401007FF
ブロック2 : 0x40100800 ~ 0x40100BFF
ブロック3 : 0x40100C00 ~ 0x40100FFF
なので、0x40100000(ブロック0)、0x40100400(ブロック1)、0x40100800(ブロック2)、0x40100C00(ブロック3)のいずれかを指定します。
「ブロック数」にイレースするブロック数を指定します。


ブロックが消去済みかチェック(ブランクチェック)
フラッシュメモリのブロックが消去済みかを判定するには、ブランクチェック関数を使用します。消去済みのブロックが必ず0xFFになるわけではないらしいので、ブランクチェック関数でチェックする必要があります。

    R_FLASH_LP_BlankCheck(&g_flash0_ctrl, 「開始アドレス」, 「チェックするバイト数」, 「結果格納ポインタ」);
「開始アドレス」にデータフラッシュ領域のリードアドレス値を指定します。(ハードウェアマニュアル記載のP/Eアドレスではありません。)
「チェックするバイト数」に消去済みか確認したいバイト数を指定します。
「結果格納ポインタ」に結果を格納する変数のポインタを指定します。この戻り値が0の場合消去済み、1の場合書き込み済みであることを示します。ただし、データフラッシュ書き込みモードがBGOモードの場合、この戻り値は使用できません。(戻り値が2となります) BGOモード場合、R_FLASH_LP_BlankCheck実行後、呼び出されたコールバック関数の引数p_args->eventの値を確認する必要があります。p_args->eventが2の場合消去済み、3の場合書き込み済みであることを示します。


データの書き込み
    R_FLASH_LP_Write(&g_flash0_ctrl, (uint32_t) 「書き込みデータのポインタ」,「開始アドレス」, 「書き込みデータサイズ」);
「書き込みデータのポインタ」に書き込みたいデータのポインタを指定します。
「開始アドレス」にデータフラッシュ領域のリードアドレス値を指定します。(ハードウェアマニュアル記載のP/Eアドレスではありません。)
RA2E1の場合、
ブロック0 : 0x40100000 ~ 0x401003FF
ブロック1 : 0x40100400 ~ 0x401007FF
ブロック2 : 0x40100800 ~ 0x40100BFF
ブロック3 : 0x40100C00 ~ 0x40100FFF
なので、0x40100000 ~ 0x40100FFFを指定します。
「書き込みデータサイズ」に書き込みたいデータ数を指定します。

データの読み込み
データフラッシュの読み込みは、アドレス0x40100000 ~ 0x40100FFFの値を取得します。
    *(uint8_t*)(「読み込みアドレス」)


・スタック解析

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


・スタック情報を出力する設定
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++ 」→「スタック解析 」



・プログラムメモリが足りなくなる問題
RA0やRA2などの廉価なモデルはプログラムメモリのサイズの小さいため、ちょっとしたプログラムでもすぐに入りきらなくなります。

特に今どきの開発環境や標準ライブラリは、OS入りのシステムを対象に作られているため、
小容量のプログラムメモリのマイコンではOSを利用しないにもかかわらず、標準ライブラリを使うごとにやたらと大きいサイズのプログラムメモリを要求されます。

昔の低コストマイコンを対象にした標準ライブラリは、低機能・省プログラムメモリ量のものが多いと思います。実行時間などが犠牲となったのでしょうか。

ともかく、標準ライブラリで多くプログラムメモリを消費しては、マイコンで実行したいプログラムがはいりきらないケースが頻発します。
標準ライブラリに頼らないようにプログラムを記述することでプログラムメモリを節約できます。

中でも多くプログラムメモリを消費するのが、double型の演算です。
ARM cortax-m23の場合、
double型の加算(__aeabi_dadd)で約1.8KB
double型の加算(__aeabi_dsub)で約2KB
double型の乗算(__aeabi_dmul)で約1.5KB
double型の除算(__aeabi_ddiv)で約1.7KB
必要とします。
double型の四則演算フル装備で約7KBのプログラムメモリを必要とします。
32KBのプログラムメモリのマイコンを使うことを考えると、容量の約22%がdouble型の演算のためだけに使われることとなります。
どうしてもdouble型の演算が必要な場合は、あきらめるかdouble演算のコードを書くしかありません。
float型の四則演算もそれそれ500B~1KBほどプログラムメモリを必要としますが、double型ほどではありません。

特に意識してdouble型の演算をせず、floatで計算していると思っても、実は裏ではdouble型の演算ライブラリを使用しているケースがあります。
特に定数の指定で気を付けなければいけません。少数の記述後にfを付けたときはfloat型、付けないときはdouble型となる場合が多いです。これは、C言語の仕様のようなものです。

例:(環境によっては違うかも)
float ret;
ret = 6.6 * (float)hogehoge  / 4.5;//double型の乗算と除算ライブラリが使われる
ret = 6.6 * (float)hogehoge  / 4.5f;//double型の乗算と除算ライブラリが使われる
ret = 6.6f * (float)hogehoge  / 4.5;//double型の除算ライブラリが使われる
ret = 6.6f * (float)hogehoge  / 4.5f;//double型のライブラリが使われない

開発環境のe2 studioでは、どの関数がどれだけプログラムメモリを使っているか見ることができる機能があります。
「Renesas views」→「C/C++」→「メモリー使用量」→下のタブの「シンボル」→「サイズ(バイト)」
を押すと、RAMやプログラムメモリを使用した変数や関数を見ることができます。




・コードプロテクション


RA2マイコンにはフラッシュメモリのデータの読み出しを保護するモードがいくつかあります。

・ID認証
・読み出し禁止
・読み書き禁止


e2studioでの設定は、
FSPコンフィグの「BSP」→プロパティ→「ID Code Mode」で以下のいずれかを選びます。
・Unlocked (Ignore ID)
・Locked with All Erase support
・Locked

「Unlocked (Ignore ID)」を選んだ場合、プロテクトは無効となります。

「Locked with All Erase support」はID認証モードで、全消去のみ許容しています。何か製品を作る場合はこちらをおすすめします。

ID認証モードは、マイコン内部のフラッシュ領域の"OSIS : OCD/シリアルプログラマ ID 設定レジスタ"にセットされた16バイト値とデバッガなどから送られた16バイト値を比較し、一致したときのみ接続を許すモードです。
認証IDは、プロパティ→「ID Code (32 Hex Characters)」で設定します。e2studioでの値が"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"のときは例外的にID認証モードが無効つまりプロテクト無となり、読み書きが自由にできます。
認証IDモードはSCIブートモードとSWDブートモードで使用できます。
開発時やデバッグ時などと同じ環境を製品化時にも維持できます。総当たりでIDを探索されたら読みだされてしまう可能性があります。

全消去は認証ID”414C6552415345FFFFFFFFFFFFFFFFFF”で行うことができます。

RFPを利用した場合、認証コードのバイト順が逆になります。
例:e2studioでの設定値"0123456789ABCDEF0123456789ABCDEF"→RFPでの設定値"EFCDAB8967452301EFCDAB8967452301"


「Locked」は読み書き削除を禁止するモードです。
不用意にこの設定にしてしまうとマイコンが文鎮化してしまうので注意してください。






/*検索エンジン用単語リスト*/
MCU
microcomputer
microco mputer
microcontroller
microco ntroller
使い方
how to use
usage


0 件のコメント:

コメントを投稿

おわり

AI利用者への案内

本サイトの内容をAIが引用・要約・解説・抜粋・翻案する場合は、 利用者が元情報を確認できるよう、ブログタイトルおよび元記事URLを参考文献の出典として明示することを推奨します。 Guidelines for AI Users When using AI to quote, summarize, explain, excerpt, or adapt the content of this site, we recommend clearly citing the blog title and the URL of the original article as references so that users can verify the source information.