こんにちは。NoMaYです。#5連投の1つ目です。今は(今後も?)e2 studioでのみ作れるFreeRTOSプロジェクトですが、以下のプログラムを作ってみました。TB-RX65N用(1) TBボードのLED0を500ms毎にOn/Offする(FreeRTOS APIのvTaskDelay()を使用)(2) TBボードのSW1押下毎にLED1をOn/Offする(タスクでセマフォ待ちして割り込みでセマフォ待ち解除)(3) TBボードのSCI1で3文字受信毎に3文字エコーバックする(タスクでセマフォ待ちして割り込みで3文字毎にセマフォ待ち解除)Renesas RX Simulator用(1') Visual ExpressionのLED部品を50ms(但しシミュレーション上の時間)毎にOn/Offする(上の(1)の括弧内と同様)(2') Visual ExpressionのBUTTON部品押下毎にLED部品をOn/Offする(上の(2)の括弧内と同様)(3') Renesas Debug Virtual Console⇔SCI1で3文字受信毎に3文字エコーバックする(上の(3)の括弧内と同様)プロジェクトのファイル一式 (CC-RX V2.03でビルド、zipファイルをe2 studioに直接インポート可能)japan.renesasrulz.com/cafe_rene/m/sample_program/434sim_rx65n_freertos_proj_fit_scfg_ccrx_c_e2v750_20190924.zip 1.23MBsim_rx65n_freertos_proj_fit_scfg_ccrx_c_e2v760_20191019.zip 1.23MB (主にGNURX版との合わせ込みです)含まれるプロジェクトsim_rx65n_freertos_proj_fit_scfg ← こちら側にソースがあります(TB-RX65Nでも動作可)(*1,*2)tb_rx65n_freertos_proj_fit_scfg ← こちら側にソースは無いです(TB側はSIM側のソースフォルダをリンク機能で参照します)(*1,*2)x_DO_NOT_BUILD_rx65n_freertos_proj_fit_scfg_0 ← ソース編集前のRXスマートコンフィグレータ設定直後のソース(*3)*1:以前に別スレッド「RenesasさんからRXマイコンの低価格Target Boardが出たのでサンプルプログラムをCSplus projectへ変換してみようと思います」に投稿したプロジェクトと同じIDコードを設定しています。*2:実機用の.launchファイルは当方特有の事情でオンボードエミュレータが使えない為に未確認です。*3:RXスマートコンフィグレータの設定を行った直後にRXスマートコンフィグレータに生成させたソースがx_DO_NOT_BUILD_rx65n_freertos_proj_fit_scfg_0プロジェクトのsrcフォルダのソースです。ファイル比較ツールで比較することで、RXスマートコンフィグレータに生成させた後、どのように編集したか分かります。出来るだけ同じプログラムにしたかった/する為に、Renesas RX Simulator用に以下の#if~#else~#endifがあります。(私のパソコンの遅さゆえにそうしたものもあります。)(A) Visual ExpressionのBUTTON部品押下からのPORTB.PIDR.BIT.B1書き換えをポーリングしてIRQ4割り込みをエミュレート(B) Renesas Debug Virtual Consoleからの受信到着待ちをポーリングしてSCI1のRXI1割り込みをエミュレート(C) Renesas Debug Virtual Consoleへの送信可能待ちをポーリングしてSCI1のTXI1/TEI1(*3)割り込みをエミュレート(D) FreeRTOSのRX600v2ポートレイヤは別スレッドで試したRenesas RX Simulator用の無理矢理な代替実装を使用(実機動作可)(E) その別スレッドで試したr_bspモジュールのクロック切り替え時の無限ループ対処をフラッシュキャッシュ有効化時にも適用(F) TB-RX65N用では500msの点滅間隔をRenesas RX Simulator用では50ms(但しシミュレーション上の時間)の点滅間隔に変更(G) TB-RX65N用では10msのチャタリング除去待ち時間をRenesas RX Simulator用では待ち時間は無しに変更*3:もう少し正確に書くとTEI1の動作ではなくICUのGROUPBL0割り込みとGROUPBL0のビット6の動作に関してです。コーディング上の小技として以下のこともやってみました。(H) CGコンポーネント初期化用のr_cg_hardware_setup.cでFITモジュール端子設定関数を呼ぶStart user code~End user code内の記述(I) r_bspモジュールのソースを直接変更せずにRenesas RX Simulator用の無限ループ対処を組み込む#defineマクロFreeRTOS関連では以下のような設定変更や記述追加も行いました。(a) r_bsp_config.h(a-1) Uスタックサイズを0へ(FreeRTOS管理下のメモリがタスクのスタックに使われるので)(Iスタックはそのまま使われます)(a-2) Heapサイズを0へ(FreeRTOS管理下のメモリをFreeRTOSのスレッドセーフなAPIで操作する方が安全と思われますので)(b) FreeRTOSConfig.h(b-1) #define __TYPEDEF__ 1 を削除(b-2) #define configINCLUDE_PLATFORM_H_INSTEAD_OF_IODEFINE_H 1 を追加(b-3) #define configENABLE_BACKWARD_COMPATIBILITY 0 を追加(c) freertos_start.c(c-1) 別スレッド同様にRenesas RX Simulator用のRX600v2ポートレイヤの無理矢理な代替実装で使うCMT1の重複エラーを追加(c-2) 別スレッド同様にAmazon AWSのFreeRTOS Kernel Developer Guideのサンプルコードを試せるようにvPrintString()を追加(c-3) vAssertCalled(), vApplicationMallocFailedHook(), vApplicationStackOverflowHook()でvPrintString()でメッセージを出力(c-4) vApplicationTickHook()でIRQ4やSIC1の割り込み動作エミュレート用の関数を呼び出す(ここが一番手っ取り早そうだった)(c-5) FreeRTOSプロジェクトでmain_task()がFreeRTOS Objectコンポーネント管理外なので対処(今回はmain_task()は空です)(c-1) Heapサイズを0にするとsbrk()が無いというリンクエラーになってしまうのでエラー処理のみのsbrk()を追加後日手を入れたいと思っていることに以下のことがあります。(イ) R_SCI_RXモジュールのAPI+FreeRTOS APIで受信エラーをタスク側で拾うにはどうすると良いか?(RL78ではこうしたけれど)(ロ) 最終的に使わなかったR_CMT_RXモジュールとRX600v2ポートレイヤの無理矢理な代替実装の両方でCMT1の使用が重複してしまう問題への対処(R_CMT_RXモジュールに手を入れるか?)この後、2投目~5投目は以下の通りです。2投目・IRQ4割り込みエミュレーションのソース (大げさなものでは無いです)・SCI1のRXI1/TXI1/TEI1(前述の*3を参照)割り込みエミュレーションのソース (大げさなものでは無いです)・CGコンポーネント初期化用のr_cg_hardware_setup.cでFITモジュール端子設定関数を呼ぶStart user code~End user code内の記述・r_bspモジュールのソースを直接変更せずにRenesas RX Simulator用の無限ループ対処を組み込む#defineマクロ3投目・FreeRTOSプロジェクトでmain_task()がFreeRTOS Objectコンポーネント管理外なので対処(今回はmain_task()は空です)・tb_rx65n_main.c, main_task.c, task_LED0.c, task_LED1.c, task_CONIO.cのソース4投目 (ひたすら画面コピーです)・Visual Expressionの部品の設定・Visual Expressionの設定・Renesas Debug Virtual Consoleの設定・コンソールのCOMポートの設定5投目 (ひたすら画面コピーです)・RXスマートコンフィグレータの設定以下、画面コピーと写真です。Renesas RX Simulatorで実行TB-RX65Nで実行RXスマートコンフィグレータに生成させた後に編集/追加したソース(右側のペイン)[履歴]sim_rx65n_freertos_proj_fit_scfg_ccrx_c_e2v750_20190924.zip・初版sim_rx65n_freertos_proj_fit_scfg_ccrx_c_e2v760_20191019.zip・MOTファイルは変わりません・VOLATILEマクロ追加(GNURX版との合わせ込み) 変更ファイル: src/smc_gen/r_config/r_bsp_config.h, src/sim_rx65n_int_emulation.c・machine.hファイル追加(CC-RX版では未使用)(GNURX版との合わせ込み) 追加ファイル: src/r_bsp_modified/GNURX_support/machine.h 変更ファイル: src/smc_gen/r_config/r_bsp_config.h・GNURX版でのワーニング対処の反映(GNURX版との合わせ込み) 変更ファイル: src/smc_gen/r_pincfg/Pin.c・スタック情報ファイルを生成するプロジェクト設定を追加 変更ファイル: .cproject・実機用の.lauchファイルを追加(当方特有の事情でオンボードエミュレータが使えない為に未確認です) 追加ファイル: "sim_rx65n_freertos_proj_fit HardwareDebug.launch", "tb_rx65n_freertos_proj_fit HardwareDebug.launch"等
こんにちは。NoMaYです。#5連投の2つ目です。今回のFreeRTOSプロジェクトの構造は以下の画面コピーの通りです。今回作成した割り込み動作エミュレート用の関数はRenesasRXSimDebugIntEmulation()ですが、以下のようにFreeRTOSのTickフックから呼び出すようにしました。これは、ここが一番手っ取り早そうだったのでそうしただけで、Tick割り込みでFreeRTOSのTickハンドラから戻って来たところで呼んでも良かったですし、あるいは別のタイマ割り込み(意図的にTick割り込みの1ms周期とは異なるようにして)から呼んでも良かったものです。src/frtos_startup/freertos_start.c
#if( configUSE_TICK_HOOK != 0 )void vApplicationTickHook(void){ /* Implement user-code for user own purpose. */#if defined(RENESAS_SIMULATOR_DEBUGGING) if (IsRenesasSimDebugMode()) { /* Renesas RX Simulator */ RenesasRXSimDebugIntEmulation(); }#endif} /* End of function vApplicationTickHook() */#endif /* configUSE_TICK_HOOK != 0 */
IRQ4割り込みは以下のようにエミュレートしました。完全なエミュレーションを行うつもりは無く、R_IRQ_RXモジュールが動けば良し(もしCGコンポーネントが動作しなかったら直す)、というレベルです。処理内容は、Visual ExpressionのBUTTON部品は押下時にメモリ/内蔵周辺レジスタの値を変更することしかしませんので、その値をチェックしてIRQ4のモードに応じて適宜int_exception( VECT(ICU, IRQ4) )を呼び出していることと、BUTTON部品は押下時(RELEASE→PUSH)しか値を変更しない(PUSH→RELEASEでは変更しない)ので、押されてから暫く経ったら、こちら側で値を戻していること、ぐらいです。(ちなみに、Visual ExpressionのLED部品は設定されたリフレッシュ間隔でメモリ/内蔵周辺レジスタの値を読み出して表示を変化させることしかしません。)src/sim_rx65n_int_emulation.c
static uint8_t PORTB_PIDR_BIT_B1_prev = 0; static uint16_t PORTB_PIDR_BIT_B1_time = 0; static bool PORTB_PIDR_BIT_B1_trig = false; switch (ICU.IRQCR[4].BIT.IRQMD) { case ICU_IRQCR_IRQMD_LOW_LEVEL: if (0 == PORTB.PIDR.BIT.B1) { PORTB_PIDR_BIT_B1_trig = true; } break; case ICU_IRQCR_IRQMD_FALLING_EDGE: if (1 == PORTB_PIDR_BIT_B1_prev && 0 == PORTB.PIDR.BIT.B1) { PORTB_PIDR_BIT_B1_trig = true; } break; case ICU_IRQCR_IRQMD_RISING_EDGE: if (0 == PORTB_PIDR_BIT_B1_prev && 1 == PORTB.PIDR.BIT.B1) { PORTB_PIDR_BIT_B1_trig = true; } break; case ICU_IRQCR_IRQMD_BOTH_EDGE: if (PORTB_PIDR_BIT_B1_prev != PORTB.PIDR.BIT.B1) { PORTB_PIDR_BIT_B1_trig = true; } break; default: /* Never come here */ break; } if (true == PORTB_PIDR_BIT_B1_trig && 0 != IEN(ICU, IRQ4)) { int_exception( VECT(ICU, IRQ4) ); PORTB_PIDR_BIT_B1_trig = false; } switch (ICU.IRQCR[4].BIT.IRQMD) { case ICU_IRQCR_IRQMD_LOW_LEVEL: case ICU_IRQCR_IRQMD_BOTH_EDGE: /* Keep the value of PORTB.PIDR.BIT.B1 */ break; case ICU_IRQCR_IRQMD_FALLING_EDGE: /* Invert the value of PORTB.PIDR.BIT.B1 automatically */ if (0 == PORTB.PIDR.BIT.B1) { if (VEDIAGRAM_BUTTON_AUTORECOVERY_TIME > PORTB_PIDR_BIT_B1_time) { PORTB_PIDR_BIT_B1_time++; } else if (VEDIAGRAM_BUTTON_AUTORECOVERY_TIME <= PORTB_PIDR_BIT_B1_time) { PORTB.PIDR.BIT.B1 = 1; PORTB_PIDR_BIT_B1_time = 0; } } break; case ICU_IRQCR_IRQMD_RISING_EDGE: /* Invert the value of PORTB.PIDR.BIT.B1 automatically */ if (1 == PORTB.PIDR.BIT.B1) { if (VEDIAGRAM_BUTTON_AUTORECOVERY_TIME > PORTB_PIDR_BIT_B1_time) { PORTB_PIDR_BIT_B1_time++; } else if (VEDIAGRAM_BUTTON_AUTORECOVERY_TIME == PORTB_PIDR_BIT_B1_time) { PORTB.PIDR.BIT.B1 = 0; PORTB_PIDR_BIT_B1_time = 0; } } break; default: /* Never come here */ break; } PORTB_PIDR_BIT_B1_prev = PORTB.PIDR.BIT.B1;
SCIのRXI1/TXI1/TEI1(前述の*3を参照)割り込みは以下のようにエミュレートしました。こちらも、完全なエミュレーションを行うつもりは無く、R_SCI_RXモジュールが動けば良し(もしCGコンポーネントが動作しなかったら直す)、というレベルです。処理内容は、Renesas Debug Virtual Consoleへ送信可能な状態ならばint_exception( VECT(SCI1, TXI1) )を呼び出した後にTDRを読んで値を送信して、Renesas Virtual Debug Consoleから受信到着の状態ならば、受信した値をRDRに書いた後にint_exception( VECT(SCI1, RXI1) )を呼び出して、というものです。また、送信終了割り込み許可ならばint_exception( VECT(ICU, GROUPBL0) )をIS6ビットを操作しながら呼び出すことも行っています。src/sim_rx65n_int_emulation.c
while (0 == (BSP_PRV_E1_DBG_PORT.dbgstat & BSP_PRV_TXFL0EN)) { if (0 != SCI1.SCR.BIT.TE && 0 != SCI1.SCR.BIT.TIE && 0 != IEN(SCI1, TXI1)) { int_exception( VECT(SCI1, TXI1) ); if (0 != SCI1.SCR.BIT.TIE) { BSP_PRV_E1_DBG_PORT.tx_data = (int32_t)SCI1.TDR; } } else { break; } } if (0 != SCI1.SCR.BIT.TE && 0 != SCI1.SCR.BIT.TEIE && 0 != IEN(ICU, GROUPBL0) && 0 != ICU.GENBL0.BIT.EN6) { ICU.GRPBL0.BIT.IS6 = 1; int_exception( VECT(ICU, GROUPBL0) ); if(0 == SCI1.SCR.BIT.TE || 0 == SCI1.SCR.BIT.TEIE) { ICU.GRPBL0.BIT.IS6 = 0; } } while (0 != (BSP_PRV_E1_DBG_PORT.dbgstat & BSP_PRV_RXFL0EN)) { if(0 != SCI1.SCR.BIT.RE && 0 != SCI1.SCR.BIT.RIE && 0 != IEN(SCI1, RXI1)) { SCI1.RDR = (uint8_t)BSP_PRV_E1_DBG_PORT.rx_data; int_exception( VECT(SCI1, RXI1) ); } else { break; } }
話は変わって、今回どうしたものかと思ったのは、FITモジュール端子設定関数をどこで呼ぼうかという点です。FreeRTOSプロジェクトでなければmain()の先頭で呼べば良いと思うのですが、FreeRTOSプロジェクトではmain()がありません。Processing_Before_Start_Kernel()内で呼び出すというのも、この関数ではFreeRTOSのスケジューラ起動前の処理を行うものという感があり、ちょっとしっくり来ません。CGコンポーネント初期化用のr_cg_hardware_setup.c内で、CGコンポーネント初期化関数を呼び出している近辺で呼び出したいところですが、ちょうどよいStart user code~End user codeがありません。そこで以下のように記述してみました。(赤文字の行で#defineマクロによる小細工をしています。)src/general/r_cg_hardware_setup.c
#include "r_cg_macrodriver.h"#include "r_smc_cgc.h"#include "r_smc_interrupt.h"/* Start user code for include. Do not edit comment generated here */#include "r_gpio_rx_if.h"#include "r_pinset.h"/* End user code. Do not edit comment generated here */#include "r_cg_userdefine.h" /* Start user code for global. Do not edit comment generated here *//* Workaround for missing a place to initialize pin settings for FIT modules */void R_Systeminit1(void);void R_Systeminit2(void);void R_Systeminit(void){ R_Systeminit1(); R_Systeminit2();}#define R_Systeminit R_Systeminit1/* End user code. Do not edit comment generated here */ void R_Systeminit(void){ /* Enable writing to registers related to operating modes, LPC, CGC and software reset */ SYSTEM.PRCR.WORD = 0xA50BU; /* Enable writing to MPC pin function control registers */ MPC.PWPR.BIT.B0WI = 0U; MPC.PWPR.BIT.PFSWE = 1U; /* Initialize clocks settings */ R_CGC_Create(); /* Register undefined interrupt */ R_BSP_InterruptWrite(BSP_INT_SRC_UNDEFINED_INTERRUPT,(bsp_int_cb_t)r_undefined_exception); /* Disable writing to MPC pin function control registers */ MPC.PWPR.BIT.PFSWE = 0U; MPC.PWPR.BIT.B0WI = 1U; /* Enable protection */ SYSTEM.PRCR.WORD = 0xA500U;}/* Start user code for adding. Do not edit comment generated here */void R_Systeminit2(void){ /* Initialize pin settings for FIT modules */ /* GPIO for LED0 and LED1 */ R_GPIO_PinWrite(GPIO_PORT_D_PIN_6, GPIO_LEVEL_HIGH); // for the initial level ...略... R_GPIO_PinWrite(GPIO_PORT_D_PIN_7, GPIO_LEVEL_HIGH); // for the initial level ...略... R_GPIO_PinDirectionSet(GPIO_PORT_D_PIN_6, GPIO_DIRECTION_OUTPUT); R_GPIO_PinDirectionSet(GPIO_PORT_D_PIN_7, GPIO_DIRECTION_OUTPUT); /* GPIO for SW1 */ R_GPIO_PinDirectionSet(GPIO_PORT_B_PIN_1, GPIO_DIRECTION_INPUT ); /* Others */ R_ICU_PinSet(); R_SCI_PinSet_SCI1();}/* End user code. Do not edit comment generated here */
それから、#defineマクロによる小細工つながりで、r_bspモジュールのソースを直接変更せずにRenesas RX Simulator用の無限ループ対処を組み込む(小細工というよりパズル的な)#defineマクロが思い浮かんだので使ってみました。Renesas RX Simulatorでデバッグしようとすると、r_bspモジュール内のoperating_frequency_set()内とrom_cache_function_set()内で、無限ループしてしまうので、以下の#defineマクロを使って、それらの呼び出しがバイパスされるようにしました。ミソは、それらを呼び出している/定義しているmcu_clock.cもhwsetup.cも変更すること無しにバイパスされるようになる、という点です。(なお、この(小細工というよりパズル的な)#defineマクロは引数がvoidである関数に対してしか使えません。)src/r_bsp_modified/sim_debug_mode_hook.h
#define operating_frequency_set(...) operating_frequency_setXXX##__VA_ARGS__()#define operating_frequency_setXXX() do{ if (!IsRenesasSimDebugMode()) { operating_frequency_set(); } }while(0)#define operating_frequency_setXXXvoid() operating_frequency_set(void)#define rom_cache_function_set(...) rom_cache_function_setXXX##__VA_ARGS__()#define rom_cache_function_setXXX() do{ if (!IsRenesasSimDebugMode()) { rom_cache_function_set(); } }while(0)#define rom_cache_function_setXXXvoid() rom_cache_function_set(void)
このヘッダファイルは以下のようにr_bsp_config.hでインクルードされるようにしてあります。src/smc_gen/r_config/r_bsp_config.h
/* For Renesas RX Simulator Debugging. */#if defined(RENESAS_SIMULATOR_DEBUGGING)#include "r_bsp_modified/sim_debug_mode_hook.h"#include "sim_rx65n_int_emulation.h"#endif