本文轉自RA生態工作室:
瑞薩“RA MCU眾測寶典”環境搭建專題再添硬核實操!這次將解鎖“基本模板搭建與LED”技能,加入簡易調度器實現多任務管理,一步步搞定“能直接落地”的開發模板,不管是入門練手還是項目開發都能復用。
開啟寶典
前言
本人因參加嵌賽而接觸到瑞薩的新賽道,由此與瑞薩結緣。當時投入到學習瑞薩對我來說很新穎的開發環境。相較于傳統開發方式,瑞薩自家的FSP庫(類似于HAL庫)極大簡化了作品的開發流程,顯著提高了開發效率,并提供了更高的靈活性。在完賽后本人也是深深愛上這種簡便開發。
接下來,我們將開始對瑞薩基于Arm Cortex-M處理器內核的RA4M2-100pin進行系統學習與實驗測試。
NO.1
開發環境準備
首先,引用以下文檔:

點擊查看大圖
RAM2系列是比較主流化的類型之一,主打一個低功耗,基于48 MHz Arm Cortex-M33內核,適用于電池供電、物聯網終端及其他對功耗敏感的應用場景。我們自己做一些一般的嵌入式產品也完全夠用,我們測試的這一款是100pin腳的,有五六個串口,與獨立I2C等等。

點擊查看大圖
那么這里推薦用官方開放軟件e2s,你也可以上網搜下用keil教程;下載器的話,省時省力當時直接到立創商城買的官方下載器,瑞薩所有系列芯片都支持用,如果板子上有板載下載器還可以直接用串口下載,就是稍微麻煩點。
01
下載e2studio
e2studio軟件的官方頁面和github下載頁面如下,可掃描二維碼或復制鏈接到瀏覽器查看。
e2studio軟件的官方頁面
https://www.renesas.cn/cn/zh/software-tool/e-studio
github上有各種版本下載,下載頁面如下
https://github.com/renesas/fsp/releases

點擊查看大圖
注意在最開始讓選擇是快速安裝還是自定義安裝,選快速安裝,自己安裝容易少下載一些固件,在后續編碼時候容易出問題。
02
下載Flash Programmer
我使用Renesas Flash Programmer軟件進行燒錄hex程序,下載鏈接在官網,可掃描二維碼或復制鏈接到瀏覽器查看。
Flash Programme下載鏈接
https://www.renesas.cn/zh/software-tool/renesas-flash-programmer-programming-gui
NO.2
模板準備
01
入門
首先打開e2s,新建文件:

點擊查看大圖
根據板子上的芯片,在Device中選擇對應型號,注意下面的Toolchains中選擇GNU,后面編譯起來更方便。

點擊查看大圖
后面頁面默認不修改,之后得到一個初步程序。

點擊查看大圖
首先根據板子主晶振,修改時鐘:

點擊查看大圖
我這塊板子上芯片所接的外部晶振是24Mhz的,可能有些是12Mhz的,在下面這里可以修改晶振的大小,還可以修改不同時鐘線的頻率,我一般用外部晶振比較多,注意在這個小齒輪頁面做出的任何改動都要最終按右上角的生成鍵。

點擊查看大圖

點擊查看大圖
在這個頁面里,首先修改成生成hex文件;其次這幾個勾選上,不然后續串口輸出編譯會報錯。

點擊查看大圖

點擊查看大圖
02
配置基本函數
模板里面,需要一些LED和基本的串口輸出;因為HAL庫雖然簡便,但是也有弊端,程序出了問題很難找到原因,所以我習慣用LED和串口打印幫助排除問題;程序卡住LED可以直觀讓我知道具體卡在哪,串口則可以排除程序邏輯錯誤等等。
在src新建倆文件,在倆文件內新建LED和串口的.c.h文件。

點擊查看大圖
現在就可以去瑞薩特有的FSP庫生成初始代碼,先把燒錄方式改成SWD;看原理圖,這三個led燈接的pin腳,并且另一端是地,也就是給高電平是點亮,找到這幾個pin腳,修改為外部輸入模式OUTPUT。

點擊查看大圖

點擊查看大圖

點擊查看大圖
SCI里面找到和typeC一起的串口9,這樣既可以供電也可以用來調試。

點擊查看大圖

點擊查看大圖
串口這里要注意在pin腳頁面配置好后,還有配置Stack,會生成一個塊,點擊它,左下角屬性里面,可以修改配置串口詳細的所有基礎信息,函數名,波特率,回調函數等等。

點擊查看大圖
所使用的UART屬性描述

點擊生成代碼后旁邊的資源里面,在HAL里面就會有相應你配置模塊的函數,最常見一些OPEN,ENABLE等等,你可以直接拖動出來,使用這些函數,相當于幫你封裝好了功能函數,你只需要根據需求修改封裝函數的入口參數即可。

點擊查看大圖
03
key板塊
首先是編寫key常用封裝函數,包括LED的開閉,反轉以及定時閃爍。

點擊查看大圖
左右滑動查看完整內容
//LED單獨開閉 #define LED1_OFF R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_05, BSP_IO_LEVEL_LOW); #define LED1_ON R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_05, BSP_IO_LEVEL_HIGH); #define LED2_OFF R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_04, BSP_IO_LEVEL_LOW); #define LED2_ON R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_04, BSP_IO_LEVEL_HIGH); #define LED3_OFF R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_00_PIN_02, BSP_IO_LEVEL_LOW); #define LED3_ON R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_00_PIN_02, BSP_IO_LEVEL_HIGH); //LED翻轉 #defineLED1_TURN R_PORT4->PODR ^=1 <<(BSP_IO_PORT_04_PIN_05 & 0xFF); #define?LED2_TURN ? ? ? ? R_PORT4->PODR ^=1 <<(BSP_IO_PORT_04_PIN_04 & 0xFF); #define?LED3_TURN ? ? ? ? R_PORT0->PODR ^=1 <<(BSP_IO_PORT_00_PIN_02 & 0xFF); //LED閃爍函數 #define?LED1_SHINE() ? ? ? ? ? ?R_PORT4->PODR ^=1 <<(BSP_IO_PORT_04_PIN_05 & 0xFF); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?R_BSP_SoftwareDelay(500,BSP_DELAY_UNITS_MILLISECONDS); #define?LED2_SHINE() ? ? ? ? ? ?R_PORT4->PODR ^=1 <<(BSP_IO_PORT_04_PIN_04 & 0xFF); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?R_BSP_SoftwareDelay(500,BSP_DELAY_UNITS_MILLISECONDS); #define?LED3_SHINE() ? ? ? ? ? ?R_PORT0->PODR ^=1 <<(BSP_IO_PORT_00_PIN_02 & 0xFF); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?R_BSP_SoftwareDelay(500,BSP_DELAY_UNITS_MILLISECONDS);
開關函數(ON/OFF)
實現方式:調用瑞薩現成的庫函數R_IOPORT_PinWrite;
工作原理:直接向指定的引腳寫入明確的高電平或低電平信號,就像直接給燈下命令“開”或“關”;
特點:代碼最清晰,可移植性好,但執行效率相對較低。
翻轉函數(TURN)
實現方式:直接操作硬件寄存器。
工作原理:使用按位異或操作符^=。1 << (PIN號)會生成一個只有目標位為1的掩碼,與該寄存器的當前值進行異或運算。
異或規則:相同為0,不同為1
效果:如果目標位當前是0(燈滅),異或后變為1(燈亮);如果當前是1(燈亮),異或后變為0
特點:執行速度極快(直接操作寄存器),但代碼可讀性稍差,且高度依賴硬件,但我個人最喜歡用這個排除錯誤
閃爍函數(SHINE)
實現方式:翻轉函數+阻塞延時。
工作原理:它其實是先做了一次翻轉操作,然后立即調用一個延時函數R_BSP_SoftwareDelay。這個延時函數會讓整個CPU停下來等待指定的時間(500毫秒)。
特點:
這是一個組合動作(翻轉并延時)。
阻塞式:在延時的半秒內,CPU不能做任何其他事情。因此它通常需要放在循環里才能實現連續閃爍,并且不適合在需要同時處理多任務的系統中使用。
個人喜歡用這個解決程序堵塞。

將其放在.h文件內,在主函數中聲明后,全局就都可以使用。
04
uart模塊
回顯輸出
左右滑動查看完整內容
#include"debug.h"
voidUart9_Init()
{
fsp_err_terr = FSP_SUCCESS;
//串口初始化
err =R_SCI_UART_Open(&g_uart9_ctrl, &g_uart9_cfg);
assert(FSP_SUCCESS == err);
}
//R_SCI_UART_Write(&g_uart9_ctrl, p_src, bytes);
//使用該函數后,標志位會被置位,使用時要及時在被置位后清零標志位,否則在連續調用時,會導致數據丟失
volatilebool uart_complete_flag =false;
voidcallback_uart9_debug(uart_callback_args_t*p_args)
{
switch(p_args->event)
{
caseUART_EVENT_RX_CHAR:
{
R_SCI_UART_Write(&g_uart9_ctrl, (uint8_t*)&(p_args->data),1);
//當接受字符,出發中斷,發送原字符
break;
}
caseUART_EVENT_TX_COMPLETE:
{
uart_complete_flag=true;
break;
}
default:
break;
}
}
雖然Uart9_Init里面只有一行指令,但還是最好封裝為一個函數,這樣方便后續程序多起來后我們可以直觀的理解與調用,也方便他人閱讀。
這樣編寫好了串口的回顯,為了后續可以直接用printf串口打印調試信息,我們還需要在debug.c中加入printf向串口的重定義。
左右滑動查看完整內容
//串口重定向
//首先是條件編譯,檢查是否是GCC編譯;根據條件選擇使用哪種函數進行重定向
#ifdefined __GNUC__ && !defined __clang__
int_write(intfd,char*pBuffer,intsize);//防止編譯警告
int_write(intfd,char*pBuffer,intsize)
{
(void)fd;
R_SCI_UART_Write(&g_uart9_ctrl,(uint8_t*)pBuffer,(uint32_t)size);
while(uart_complete_flag ==false);
uart_complete_flag =false;
returnsize;
}
#else
intfputc(intch, FILE *f)
{
(void)f;
R_SCI_UART_Write(&g_uart9_ctrl,(uint8_t*)&ch,1);
while(uart_complete_flag==false);
uart_complete_flag =false;
returnch;
}
#endif
05
主函數部分
調度器
瑞薩主函數為hal_entry,你也可以理解為main。對于一般集成了較多的程序,我習慣使用調度器,可以自由直觀的開閉需要的部分,后期對于大項目也可以試試用rtos。
左右滑動查看完整內容
#include"hal_data.h" #include"gpt/gpt.h" #include"key/key.h" #include"led/led.h" #include"debug/debug.h" FSP_CPP_HEADER voidR_BSP_WarmStart(bsp_warm_start_event_tevent); FSP_CPP_FOOTER /*變量聲明區*/ /*------------------------------------------------------*/ uint32_tuwTick; //系統計時變量 /*------------------------------------------------------*/ uint32_tKey_Val, Key_Down, Key_Up, Key_Old; /*------------------------------------------------------*/ voidKey_Proc() { Key_Val =Key_Read(); Key_Down = Key_Val & (Key_Old ^ Key_Val);// 按鍵按下檢測 Key_Up = ~Key_Val & (Key_Old ^ Key_Val); // 按鍵抬起檢測 Key_Old = Key_Val; switch(Key_Down) { case3: LED1_TURN; break; case4: LED2_TURN; break; } } voidLED_Proc() { LED3_TURN; } /* 調度器任務結構體定義 */ typedefstruct { void(*task_func)(void); // 任務函數 unsignedlongint rate_ms; // 任務執行周期(毫秒) unsignedlongint last_run;// 任務上次運行時間 }task_t; /* 調度器任務列表 */ task_tScheduler_Task[] = { {Key_Proc,10,0}, // 鍵盤任務,每10毫秒執行一次 {LED_Proc,1000,0}, // 數碼管任務,每100毫秒執行一次 }; uint8_ttask_num=0;//任務數量 /* 調度器初始化 */ voidScheduler_Init(void) { task_num =sizeof(Scheduler_Task) /sizeof(task_t);// 計算任務數量 } /* 調度器運行 */ voidScheduler_Run(void) { uint8_ti=0; for(i =0; i < task_num; i++) ? { ? ??uint32_t?now_time = uwTick;?// 獲取當前時間 ? ??if?(now_time >= (Scheduler_Task[i].last_run + Scheduler_Task[i].rate_ms)) { Scheduler_Task[i].last_run = now_time;// 更新任務上次運行時間 Scheduler_Task[i].task_func(); // 執行任務 } } } /*******************************************************************************************************************//** * main() is generated by the RA Configuration editor and is used to generate threads if an RTOS is used. This function * is called by main() when no RTOS is used. **********************************************************************************************************************/ voidhal_entry(void) { /*TODO:add your own code here */ Scheduler_Init(); Uart9_Init(); System_Init(); while(1) { Scheduler_Run(); } #ifBSP_TZ_SECURE_BUILD /* Enter non-secure code */ R_BSP_NonSecureEnter(); #endif }
功能
代碼里我寫了,LED3的調度器設定的時間,間隔閃爍。

1000毫秒的閃爍間隔:

而按鍵則是,按鍵sw1,sw2分別對著LED1和LED2,按下則反轉一次。
NO.3
下載
然后準備的差不多了,就可以下載了,打開Flash Programmer,點擊左上角file,新建立一個。

按照這樣選:

點擊查看大圖
這里推薦給SWD下載這里做個段子,一來方便下載,二來這里要注意SWDIO,SWCLK兩條線要接緊一點,不然老下不下去。

點擊查看大圖
這樣就下載成功了。
如果在FSP配置、調度器編寫或下載調試中遇到問題,或是有模板優化、功能擴展的巧思,歡迎在評論區分享交流~
環境搭建專題會持續覆蓋更多RA系列開發板的實操指南,關注瑞薩嵌入式小百科,讓嵌入式開發“從0 到1”更高效,后續還能解鎖更多項目模板和避坑技巧!
-
處理器
+關注
關注
68文章
20255瀏覽量
252253 -
mcu
+關注
關注
147文章
18925瀏覽量
398124 -
瑞薩
+關注
關注
37文章
22481瀏覽量
90869 -
開發板
+關注
關注
26文章
6291瀏覽量
118082 -
開發環境
+關注
關注
1文章
270瀏覽量
17637
原文標題:瑞薩RA MCU眾測寶典 | 環境搭建之【RA-Eco-RA4M2】基本模板搭建與LED
文章出處:【微信號:瑞薩嵌入式小百科,微信公眾號:瑞薩嵌入式小百科】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
瑞薩RA-Eco-RA6E2-64PIN-V1.0開發板介紹及環境搭建指南
瑞薩RA-Eco-RA4M2開發板基本模板搭建與LED指南
評論