一般嵌入式操作系統(tǒng)因為它的特殊性,往往和硬件平臺密切相關,具體的嵌入式操作系統(tǒng)往往只能在特定的硬件上運行。對于剛接觸 RT-Thread 操作系統(tǒng)的讀者來說并不容易馬上就獲得一個和 RT-Thread 操作系統(tǒng)相配套的硬件模塊,但隨著計算機技術的發(fā)展,我們可以采用軟件方式來模擬一個能夠運行 RT-Thread 操作系統(tǒng)的硬件模塊,這就是 ARM 公司的 MDK-ARM 仿真模擬環(huán)境。
MDK-ARM(MDK-ARM Micro
controller Development Kit)軟件是一套完整的集成開發(fā)環(huán)境(IDE),它出自 ARM 公司,包括了針對 ARM 芯片(ARM7、ARM9、Cortex-M 系列、Cortex-R 系列等)的高效 C/C++ 編譯器;針對各類 ARM 設備、評估板的工程向導、工程管理;用于軟件模擬運行硬件平臺的模擬器;以及與市面上常見的如 ST-Link、JLink 等在線仿真器相連接以配合調試目標板的調試器。MDK-ARM 軟件中的軟件仿真模擬器,采用完全軟件模擬方式解釋執(zhí)行 ARM 的機器指令,并實現(xiàn)外圍的一些外設邏輯,從而構成一套完整的虛擬硬件環(huán)境,使得用戶能夠不借助真實的硬件平臺就能夠在電腦上執(zhí)行相應的目標程序。
MDK-ARM 集成開發(fā)環(huán)境因為其完全的 STM32F103 軟件仿真環(huán)境,也讓我們有機會在不使用真實硬件環(huán)境的情況下直接在電腦上運行目標代碼。這套軟件仿真模擬器能夠完整地虛擬出 ARM Cortex-M3 的各種運行模式、外設,如中斷異常、時鐘定時器、串口等,這幾乎和真實的硬件環(huán)境完全一致。實踐也證明,本文使用到的這份 RT-Thread 入門例程,在編譯成二進制代碼后,不僅能夠在模擬器上實現(xiàn)軟件模擬運行,也能夠不需要修改而在真實硬件平臺上正常運行。
下面我們將選擇 MDK-ARM 集成開發(fā)環(huán)境作為目標硬件平臺來觀察 RT-Thread 操作系統(tǒng)是如何運行的。
一、準備工作
MDK 開發(fā)環(huán)境:需要安裝 MDK-ARM 5.24 (正式版或評估版,5.14 版本及以上版本均可),這個版本也是當前比較新的版本,它能夠提供相對比較完善的調試功能。安裝方法可以參考Keil MDK安裝。
Keil MDK安裝鏈接:
https://www.rt-thread.org/docum
ent/site/tutorial/quick-start/keil/keil/
二、初識 RT-Thread
作為一個操作系統(tǒng),RT-Thread 的代碼規(guī)模怎么樣呢?我們找到RT-Thread壓縮包文件,將它解壓,我們這里解壓到 D盤,解壓完成后的目錄結構如圖1所示:
各個目錄所包含的文件類型的描述如圖2所示:
在目錄下,有一個 project.uvprojx 文件,它是本文內容所引述的例程中的一個 MDK5 工程文件,雙擊 “project.uvprojx” 圖標,打開此工程文件(圖3):
在工程主窗口的左側 “Project” 欄里可以看到該工程的文件列表,這些文件被分別存放到如下幾個組內,分別是(圖4):
現(xiàn)在我們點擊一下窗口上方工具欄中的按鈕,對該工程進行編譯,如圖5所示:
編譯的結果顯示在窗口下方的 “Build” 欄中,沒什么意外的話,最后一行會顯示“0 Error(s), * Warning(s).”,即無任何錯誤和警告。
在編譯完 RT-Thread/STM32 后,我們可以通過 MDK-ARM 的模擬器來仿真運行 RT-Thread。點擊窗口右上方的按鈕或直接按 “Ctrl+F5” 進入仿真界面,再按 F5 開始運行,然后點擊該圖工具欄中的按鈕或者選擇菜單欄中的 “View→Serial Windows→UART#1”,打開串口 1 窗口,可以看到串口的輸出只顯示了 RT-Thread 的 LOGO,這是因為用戶代碼是空的,其模擬運行的結果如圖6、7所示:
三、系統(tǒng)啟動代碼
一般了解一份代碼大多從啟動部分開始,同樣這里也采用這種方式,先尋找啟動的源頭。以 MDK-ARM 為例,MDK-ARM 的用戶程序入口為 main() 函數(shù),位于 main.c 文件中。系統(tǒng)啟動后先從匯編代碼 startup_stm32f103xe.s 開始運行,然后跳轉到 C 代碼,進行 RT-Thread 系統(tǒng)功能初始化,最后進入用戶程序入口 main()。
下面我們來看看在 components.c 中定義的這段代碼:
1//components.c中定義
2/*re-define main function */
3int $Sub$$main(void)
4{
5rt_hw_interrupt_disable();
6rtthread_startup();
7return 0;
8}
在這里$Sub$$main函數(shù)僅僅調用了rtthread_startup()函數(shù)。RT-Thread 支持多種平臺和多種編譯器,而rtthread_startup()函數(shù)是 RT-Thread 規(guī)定的統(tǒng)一入口,所以$Sub$$main函數(shù)只需調用rtthread_startup()函數(shù)即可。例如采用 GNU GCC 編譯器編譯的 RT-Thread,就是直接從匯編啟動代碼部分跳轉到rtthread_startup()函數(shù)中,并開始第一個 C 代碼的執(zhí)行的。在 components.c 的代碼中找到rtthread_startup()函數(shù),我們將可以看到 RT-Thread 的啟動流程:
1int rtthread_startup(void)
2{
3rt_hw_interrupt_disable();
4
5/*board level initalization
6*NOTE:please initialize heap inside board initialization.
7*/
8rt_hw_board_init();
9
10/*show RT-Thread version?*/
11 rt_show_version();
12
13/*timer system initialization*/
14rt_system_timer_init();
15
16/*scheduler system initializati on*/
17rt_system_scheduler_init();
18
19#ifdef RT_USING_SIGNALS
20/*signal system initialization */
21rt_system_signal_init();
22#endif
23
24/* create init_thread */
25rt_application_init();
26
27/* timer thread initialization*/
28rt_system_timer_thread_init();
29
30/* idle thread initialization*/
31rt_thread_idle_init();
32
33/* start scheduler */
34rt_system_scheduler_start();
35
36/* never reach here */
37return 0;
38}
這部分啟動代碼,大致可以分為四個部分:
初始化與系統(tǒng)相關的硬件;
初始化系統(tǒng)內核對象,例如定時器、調度器;
初始化系統(tǒng)設備,這個主要是為 RT-Thread 的設備框架做的初始化;
初始化各個應用線程,并啟動調度器。
四、用戶入口代碼
上面的啟動代碼基本上可以說都是和 RT-Thread 系統(tǒng)相關的,那么用戶如何加入自己的應用程序的初始化代碼呢?RT-Thread 將 main 函數(shù)作為了用戶代碼入口,只需要在 main 函數(shù)里添加自己的代碼即可。
1int main(void)
2{
3/* user app entry*/
4return 0;
5}
ARM教程鏈接:
http://infocenter.arm.com/help/index.jsp topic=/com.arm.doc.dui0377g/pge1362065967698.html
五、跑馬燈的例子
對于從事電子方面開發(fā)的技術工程師來說,跑馬燈大概是最簡單的例子,就類似于每種編程語言中程序員接觸的第一個程序 Hello World 一樣,所以這個例子就從跑馬燈開始。讓它定時地對 LED 進行更新(關或滅)。
我們 在UART#1 中輸入 msh 命令:led 然后回車就可以運行起來了,如圖8所示:
六、跑馬燈例子
1/*
2*程序清單:跑馬燈例程
3*
4*跑馬燈大概是最簡單的例子,就類似于每種編程語言中程序員接觸的第一個程序
5*Hello World 一樣,所以這個例子就從跑馬燈開始。創(chuàng)建一個線程,讓它定時地對
6*LED進行更新(關或滅)
7*/
8
9int led(void)
10{
11 rt_uint8_t count;
12
13rt_pin_mode(LED_PIN,PIN_MODE_OUTPUT);
14
15for(count=0;count<10;count++)
16{
17rt_pin_write(LED_PIN,PIN_HIG
H);
18rt_kprintf(“l(fā)ed on,count:%d”,count);
19rt_thread_mdelay(500);
20
21rt_pin_write(LED_PIN,PIN_LO
W);
22rt_kprintf(“l(fā)ed off
”);
23rt_thread_mdelay(500);
24}
25return 0;
26}
27MSH_CMD_EXPORT(led,RT-Thread first led sample);
七、常見問題
出現(xiàn)如下編譯錯誤
1rt-threadsrckservice.c(823):error:#929:incorrect use of vaarg fieldwidth=aarg(args,int);
2rt-threadsrckservice.c(842):error:#929:incorrect use of vaarg precision=aarg(args,int);
3………
原因:這類問題基本上都是因為安裝了 ADS 導致,ADS 與 keil共存,va_start 所在的頭文件指向了 ADS 的文件夾。
八、解決辦法
1.刪除 ADS 環(huán)境變量;
2.卸載 ADS 和Keil,重啟電腦,重裝Keil。