何為RT-Thread Nano?大家知道,Keil5以后采用pack形式管理芯片及各種相關(guān)組件的。RT-Thread Nano就是通過Keil pack方式發(fā)布,在保持原有RT-Thread基本功能的情況下,實(shí)現(xiàn)了極小的Flash和Ram占用。默認(rèn)配置下,F(xiàn)lash可小至2.5K, Ram可以小至1K。
目前pack包含有kernel、shell(msh)、device drivers三部分功能,這3個(gè)功能可按實(shí)際使用情況按需加載。本次使用的主芯片為GD32F150C8T6,資源為Flash:64K,RAM:8K。
一、RT-Thread Nano Pack下載安裝
1.在Keil5主界面上點(diǎn)擊“Pack Install”按鈕,即可進(jìn)入Pack Install界面
圖1:Keil5主界面
2.在Pack Install界面下,RT-Thread Pack在右邊欄中。如未下載,可點(diǎn)擊“Install”下載;如已安裝,版本有更新,將提示“Update”可更新。
圖2:RT-ThreadPack下載
3.如在圖2界面“Packs”欄中未發(fā)現(xiàn)“RT-Thread”,可先在菜單“Packs”下點(diǎn)擊“Check for Updates”。Update完成后,將可看到RT-Thread Pack。
圖3:Pack Update
4.Pack下載完成后,Keil將自動(dòng)彈出Pack安裝界面,按步驟依次完成安裝。
二、裸機(jī)最小系統(tǒng)工程建立
1.本次工程使用的是芯片是GD32F150C8T6,64KFlash、8KRam。Keil5下開發(fā)須先在官網(wǎng)下載Keil Pack (GigaDevice.GD32F1x0_DFP.pack),并正確安裝。
2.先按照裸機(jī)Keil工程流程搭建工程,為測試Flash及Ram大小,最小工程只包含必須的Libraries文件,main函數(shù)也未作任何多余處理。
圖4:GD32F150C8T6最小工程
3.編譯完成后,默認(rèn)配置Flash:1112字節(jié)、Ram:2144字節(jié)
4.修改默認(rèn)啟動(dòng)文件startup_gd32f1x0.s定義堆和棧大小:默認(rèn)堆為0x400,棧為0x400。后續(xù)我們將采用RT-Thread管理內(nèi)存堆,所有堆設(shè)置為0;??砂凑誱ain函數(shù)應(yīng)用需求調(diào)整為0x100或以上。
圖5:啟動(dòng)文件棧和堆修改
啟動(dòng)文件修改后,Ram大小為352字節(jié)
圖6:修改堆和棧后Flash和棧占用大小
三、kernel加載與應(yīng)用
1.加載RT-Thread Kernel:在主界面點(diǎn)擊“ManageRun-Time Environment”按鈕即可進(jìn)入加載頁。
圖7:ManageRun-Time Environment
在“RTOS”一欄中選中“RT-Thread”,并在列表中選中“kernel”,當(dāng)前版本為2.1.2。
圖8:RT-Thread kernel選擇
2.確定后,keil界面上會(huì)加載RT-Thread的kernel文件,更根據(jù)當(dāng)前選擇芯片類型加入已移植完成的M3芯片內(nèi)核代碼、配置文件等。
圖9:RT-Thread kernel文件
其中:
Kernel文件包括:
-
clock.c
-
components.c
-
device.c
-
idle.c
-
ipc.c
-
irq.c
-
kservice.c
-
mem.c
-
object.c
-
scheduler.c
-
thread.c
-
timer.c
Cortex-m3芯片內(nèi)核移植代碼:
應(yīng)用代碼及配置文件:
-
board.c
-
rtconfig.h
3.此時(shí)再次編譯工程,編譯器會(huì)提示有函數(shù)被重復(fù)定義了。需按照如下方式做一些修改:
a) 修改gd32f10x_it.c文件,刪除如下函數(shù):
-
void HardFault_Handler(void)
-
void PendSV_Handler(void)
-
void SysTick_Handler(void)
b) 按照board.c上的說明,依次完成如下操作:
圖10:board.c修改流程說明
-
修改24行:#include “gd32f1x0.h”
-
修改48行:在rt_hw_board_init()函數(shù)內(nèi)開啟SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
-
修改66行:voidSysTick_Handler(void)
4.修改main.c:
-
加入#include
-
在while循環(huán)中加入rt_thread_dealy(100);
5.再次編譯順利通過,下載至芯片運(yùn)行可看到main函數(shù)中每1s可中斷一次。RT-Thread任務(wù)調(diào)度器已經(jīng)正常運(yùn)行。
圖11:RT-Thread正常運(yùn)行
通過查看.map文件可獲取當(dāng)前各文件資源占用情況。在未開啟任何優(yōu)化的情況下,可以看到RT-Thread內(nèi)核各文件資源占用情況。
圖12:資源占用表
6.可在main函數(shù)內(nèi)添加RT-Thread支持的任務(wù)、定時(shí)器、信號(hào)量等功能。Nano默認(rèn)rtconfig.h配置只支持靜態(tài)任務(wù)、信號(hào)量創(chuàng)建。在靜態(tài)模式下,不能使用rt_thread_create/rt_thread_delete/rt_sem_create/rt_sem_delete/rt_malloc/rt_free與動(dòng)態(tài)創(chuàng)建、刪除有關(guān)的接口。如需動(dòng)態(tài)創(chuàng)建,需開啟RT_USING_HEAP項(xiàng),詳見本篇第五部分:《RT-Thread配置》
四、RT-Thrad啟動(dòng)流程分析
這次創(chuàng)建的keil工程雖然應(yīng)用了RT-Thread嵌入式操作系統(tǒng),但開發(fā)流程無不帶os開發(fā)幾乎沒有差別。都是將main作為入口,完成硬件初始化、應(yīng)用代碼添加,而且可以直接應(yīng)用RT-Thread的各種功能完成產(chǎn)品開發(fā)。但是我們沒有添加RT-Thread相關(guān)初始化、啟動(dòng)等代碼到我們的工程里面,但實(shí)際情況是調(diào)度器已經(jīng)正常運(yùn)行了,這是怎么實(shí)現(xiàn)的呢?
01
RT-Thread入口
我們可以在components.c文件的140行看到#ifdef RT_USING_USER_MAIN宏定義判斷,這個(gè)宏是定義在rtconfig.h文件內(nèi)的,而且處于開啟狀態(tài)。同時(shí)我們可以在146行看到#if defined (__CC_ARM)的宏定義判斷,__CC_ARM就是指keil的交叉編譯器名稱。
我們可以在這里看到定義了2個(gè)函數(shù):$$Sub$$main()和$$Super$$main()函數(shù);這里通過$$Sub$$main()函數(shù)在程序就如主程序之前插入一個(gè)例程,實(shí)現(xiàn)在不改變?cè)创a的情況下擴(kuò)展函數(shù)功能。鏈接器通過調(diào)用$$Sub$$Main()函數(shù)取代main(),然后通過$$Super$$main再次回到main()
#if defined (__CC_ARM)
extern int $Super$$main(void);
/* re-define main function */
int $Sub$$main(void)
{
rt_hw_interrupt_disable();
rtthread_startup();
return 0;
}
在$$Sub$$main函數(shù)內(nèi)調(diào)用了rt_hw_interrutp_disable()和rtthread_startup()兩個(gè)函數(shù)。熟悉RT-Thread開發(fā)流程的,一看就知道這是標(biāo)準(zhǔn)的RT-Thread的啟動(dòng)入口。
其中:
-
rt_hw_interrupt_disable():關(guān)中斷操作,
-
rtthread_startup():完成systick配置、timer初始化/啟動(dòng)、idle任務(wù)創(chuàng)建、應(yīng)用線程初始化、調(diào)度器啟動(dòng)等工作。
int rtthread_startup(void)
{
rt_hw_interrupt_disable();
/* board level initalization
* NOTE: please initialize heap insideboard initialization.
*/
rt_hw_board_init();
/* show RT-Thread version */
rt_show_version();
/* timer system initialization */
rt_system_timer_init();
/* scheduler system initialization */
rt_system_scheduler_init();
/* create init_thread */
rt_application_init();
/* timer thread initialization */
rt_system_timer_thread_init();
/* idle thread initialization */
rt_thread_idle_init();
/* start scheduler */
rt_system_scheduler_start();
/* never reach here */
return 0;
}
-
rt_hw_board_init():該函數(shù)定義在board.c文件內(nèi),需要修改systick配置
-
rt_system_timer_init()/rt_system_timer_thread_init():timer初始化/啟動(dòng)
-
rt_thread_idle_init():idle任務(wù)創(chuàng)建
-
rt_application_init():應(yīng)用線程初始化
-
rt_system_scheduler_start():調(diào)度器啟動(dòng)
02
應(yīng)用線程入口
rt_application_init()
void rt_application_init(void)
{
rt_thread_t tid;
#ifdef RT_USING_HEAP
tid = rt_thread_create("main",main_thread_entry, RT_NULL,
RT_MAIN_THREAD_STACK_SIZE,RT_THREAD_PRIORITY_MAX / 3, 20);
RT_ASSERT(tid != RT_NULL);
#else
rt_err_t result;
tid = &main_thread;
result = rt_thread_init(tid,"main", main_thread_entry, RT_NULL,main_stack, sizeof(main_stack),RT_THREAD_PRIORITY_MAX / 3, 20);
RT_ASSERT(result == RT_EOK);
#endif
rt_thread_startup(tid);
}
在這里,我們可以看到應(yīng)用線程創(chuàng)建了一個(gè)名為main_thread_entry的任務(wù),并且已經(jīng)啟動(dòng)了該任務(wù)。我們?cè)俅蝸砜匆幌耺an_thread_entry任務(wù)。
/* the system main thread */
void main_thread_entry(void*parameter)
{
extern int main(void);
extern int $Super$$main(void);
/* RT-Thread components initialization */
rt_components_init();
/* invoke system main function */
#if defined (__CC_ARM)
$Super$$main(); /* for ARMCC. */
#elif defined(__ICCARM__) ||defined(__GNUC__)
main();
#endif
}
man_thread_entry任務(wù)完成了2個(gè)工作:調(diào)用rt_components_init()、進(jìn)入應(yīng)用代碼真正的main函數(shù)。
在這里我們看到了$$Super$$main()的調(diào)用,在前面我們講了調(diào)用該函數(shù)可用來回到main()的。
圖13:RT-Thread初始化及啟動(dòng)流程
從以上分析可以,正是由于在rtconfig.h內(nèi)開啟了RT_USING_USER_MAIN選項(xiàng),編譯器在main之前插入了$$Sub$$main(),完成了RT-Thread初始化及調(diào)度器啟動(dòng)工作。并且通過創(chuàng)建main_thread_entry任務(wù),并通過$$Super$$main()回到main()函數(shù)。這樣看來main()函數(shù)其實(shí)只是RT-Thread的一個(gè)任務(wù),該任務(wù)的優(yōu)先級(jí)為 RT_THREAD_PRIORITY_MAX / 3,任務(wù)棧為RT_MAIN_THREAD_STACK_SIZE。
圖14:RT_USING_USER_MAIN選項(xiàng)
五、RT-Thread配置(rtconfig.h)
RT-Thread是一個(gè)高度可配置的嵌入式實(shí)時(shí)操作系統(tǒng),配置通過rtconfig.h文件實(shí)現(xiàn)。Nano就是在rtconfig.h配置下實(shí)現(xiàn)了2.5KFlash,1KRam的內(nèi)核應(yīng)用,但是由于Nano未開啟RT_USING_HEAP選項(xiàng),故只支持靜態(tài)方式創(chuàng)建任務(wù)及信號(hào)量。下面分步開啟rtconfig.h配置常用選項(xiàng)。
01
RT_USING_HEAP:開啟heap
根據(jù)芯片型號(hào)在board.c第37行,修改SARM_SIZE大小,默認(rèn)為8,GD32F150C8T6正好也為8K。
圖15:SRAM_SIZE配置
開啟RT_USING_HEAP選項(xiàng)后,在board.c的rt_hw_board_init()內(nèi)將調(diào)用rt_system_heap_init()
#if defined(RT_USING_USER_MAIN)&& defined(RT_USING_HEAP)
rt_system_heap_init((void*)HEAP_BEGIN,(void*)SRAM_END);
#endif
其中:
SRAM_END:根據(jù)宏定義為0x20000000 +SRAM_SIZE * 1024
HEAP_BEGIN:
圖16:HEAP_BEGIN定義
其中Image$$RW_IRAM1$$ZI$$Limit是鏈接器導(dǎo)出符號(hào),表示ZI段的結(jié)束地址。
配置完成后,就可通過動(dòng)態(tài)創(chuàng)建任務(wù)、信號(hào)量等方式開發(fā)軟件了。
02
RT_USING_TIMER_SOFT:開啟軟件定時(shí)器
Nano默認(rèn)配置未開啟軟件定時(shí)器功能。開啟軟件定時(shí)器功能后,可創(chuàng)建多個(gè)軟件定時(shí)器,定時(shí)器精度為Systick觸發(fā)精度。
圖17:軟件定時(shí)器開啟
- End -
-
rt_thread
+關(guān)注
關(guān)注
2文章
13瀏覽量
14658
原文標(biāo)題:手把手教你實(shí)戰(zhàn)RT-Thread Nano,不信都這樣了你還不會(huì)
文章出處:【微信號(hào):RTThread,微信公眾號(hào):RTThread物聯(lián)網(wǎng)操作系統(tǒng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論