RT-Thread學習筆記(二)—— RT-Thread啟動代碼及入口函數
從本篇開始正式進入RT-Thread的開發學習,參考RT-Thread快速入門實例教程對RT-Thread作以初步了解,探究RT-Thread啟動代碼及用戶入口函數。
1.工程結構
2.用戶入口代碼
上一節中,application文件夾中存放main.c文件,所以學習RTT的源碼從這個文件入手,由淺到深,打開這個文件後可以看到裏面內容很簡單:
#include <rtthread.h>
int main(void)
{
/* user app entry */
return 0;
}
在這裏不禁會有所疑惑,作為一個RTOS,main函數中竟然只有一行註釋和return 0,are you kiding me?其實,正如那一句精辟的註釋,user app entry,用戶應用程序入口,這個main函數並不是我們平常所接觸到的main函數,這個main函數其實是由RTT呈現給我們的main函數
3.RT-Thread啟動代碼
了解了rtt的用戶入口代碼之後,再來看看RT-Thread是如何啟動的?最好的辦法是使用MDK的軟件模擬debug功能,單步執行一遍程序,答案自然知曉。
首先編譯工程,然後更改Debug模式為軟件模擬模式,然後ctrl+F5進入debug模式,點擊左上角復位按鈕,將系統置於復位狀態,因為RTT使用USART1打印信息,所以需要調出USART1串口便於觀察(在View->Serial Windows->UART#1),接下來按F11就可以單步執行程序;
3.1.匯編啟動文件
這個啟動文件應該很熟悉了,無論裸機編程還是RTOS編程都是必不可少的,它的作用是構建好單片機上C語言的運行環境(主要是堆棧)
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0 /* 跳轉執行系統時鐘初始化 */
LDR R0, =__main
BX R0 /* 跳轉執行C語言中的main函數 */
ENDP
3.2.啟動文件跳轉到的main函數
在上一步的匯編啟動文件中跳轉到main函數中,我們可以發現,它跳轉到了RT-Thread源碼中的"components.c"文件的main函數中,具體代碼如下:
#if defined (__CC_ARM)
extern int $Super$$main(void);
/* re-define main function */
int $Sub$$main(void)
{
/* 關閉中斷系統 */
rt_hw_interrupt_disable();
/* 啟動RT-Thread系統 */
rtthread_startup();
return 0;
}
可以看到,這才是整個RTOS系統的核心main函數,依然很簡潔,精辟,第一行代碼關閉整個中斷系統,然後第二行代碼啟動RT-Thread系統;
這裏需要註意一點:在定義main函數的時候采用了<pre>不能識別此Latex公式: Sub</pre>
<pre>不能識別此Latex公式: main,這是ARM鏈接器的特殊語法,因為之前用戶入口函數是main,此處不能再次main,所以可以采用</pre>
Sub<pre>不能識別此Latex公式: </pre>
在不修改函數名的情況下對main函數的主體重新定義,在進入 main 程序之前調用另外一個例程完成系統功能初始化,具體說明可以參見ARM官方文檔。Linker User Guide
接下來我們繼續執行代碼:
3.2.1.關閉中斷系統
程序跳轉到了匯編編寫的“ context_rvds.S”文件中,執行以下幾行代碼,關閉整個中斷系統:
MRS r0, PRIMASK
CPSID I
BX LR
3.2.2.啟動RT-Thread系統
單步執行後系統再次跳轉到"components.c"文件中,rtthread_startup()函數的代碼如下,由註釋可以看到,中斷系統對於RTOS的影響很大,所以在啟動之前再次關閉中斷系統,然後便是整個RTT的啟動流程:
int rtthread_startup(void)
{
/* 再次關閉中斷系統 */
rt_hw_interrupt_disable();
/* 板級初始化 */
rt_hw_board_init();
/* 打印rt版本號 */
rt_show_version();
/* 定時器系統初始化 */
rt_system_timer_init();
/* 調度器系統初始化 */
rt_system_scheduler_init();
#ifdef RT_USING_SIGNALS
/* 信號量系統初始化 */
rt_system_signal_init();
#endif
/* 創建初始化線程 */
rt_application_init();
/* 定時器線程初始化 */
rt_system_timer_thread_init();
/* 空閑線程初始化 */
rt_thread_idle_init();
/* 啟動調度器 */
rt_system_scheduler_start();
/* 永遠不會執行到這兒 */
return 0;
}
至此,我們對RTT的啟動過程和我們需要的用戶入口函數有了大致的認識,接下來我們不再深入RT-Thread的內核底層,而是在RT-Thread的基礎上學習如何應用編程,畢竟原理上的理解是為了更好的應用,在學習完RT-Thread的基本應用之後,我們再回過頭來深入底層一探內核究竟,便於我們在一些特殊的應用裏對內核進行高度裁剪。
RT-Thread學習筆記(二)—— RT-Thread啟動代碼及入口函數