啟動流程
一個開發(fā)板上的RT-Thread的啟動流程可能是首先從bsp?當中鏈接腳本指定的startup_xxx.S?中的入口函數(shù)(ENTRY)或者復位異常處理函數(shù)(ResetHandler)開始運行,這部分我們在講?bsp?支持時會詳細講解。
之后跳入entry?函數(shù)(GCC,使用不同編譯器會進入不同的函數(shù))執(zhí)行,這里也可以跳入用戶自己的main函數(shù),但是需要用戶自己完成rtthread_startup?的工作。
這個函數(shù)十分簡單,首先先關中斷(關中斷操作由cpu支持部分提供),然后進入RT-Thread的全局初始化中。
#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;
}
#elif defined( ICCARM )
extern int main(void);
/ __low_level_init will auto called by IAR cstartup /
extern void __iar_data_init3(void);
int __low_level_init(void)
{
// call IAR table copy function.
__iar_data_init3();
rt_hw_interrupt_disable();
rtthread_startup();
return 0;
}
#elif defined( GNUC )
extern int main(void);
/ Add -eentry to arm-none-eabi-gcc argument */
int entry(void)
{
rt_hw_interrupt_disable();
rtthread_startup();
return 0;
}
#endif
?rtthread_startup?是啟動流程當中的關鍵,首先rtthread_startup?會先調用rt_hw_board_init?,這個函數(shù)也由bsp支持部分提供,一般來說主要完成例如初始化中斷向量表、系統(tǒng)時鐘的初始化,設置輸出控制臺,同時初始化系統(tǒng)堆內存。
緊接著會調用rt_show_version?打印RT-Thread內核的系統(tǒng)版本信息,其中主要是利用控制臺(rt_printf?)進行輸出,通常來說是用戶在bsp支持中提供串口的注冊來實現(xiàn)的。以RT-Thread Simulator 例程來說,會通過設備驅動一路調用到bsp支持部分提供的串口輸出。
void rt_kprintf(const char *fmt, ...) ->
rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) ->
static rt_size_t rt_serial_write(struct rt_device *dev, rt_off_t pos, const void *buffer, rt_size_t size) ->
rt_inline int _serial_poll_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length) ->
static int stm32_putc(struct rt_serial_device *serial, char c)
然后會調用rt_system_timer_init?初始化系統(tǒng)定時器鏈表,這個函數(shù)比較簡單,主要就是數(shù)據(jù)結構進行初始化。
緊接著會調用rt_system_scheduler_init?初始化RT-Thread系統(tǒng)調度器相關的數(shù)據(jù)結構:
線程優(yōu)先級鏈表:每一個優(yōu)先級對應一個鏈表,通過rt_thread?結構中的tlist?成員來進行相同優(yōu)先級線程的連接
當前線程優(yōu)先級
當前線程控制塊
?rt_thread_ready_priority_group?中的每一位代表1個優(yōu)先級,該位為1表示該優(yōu)先級下有就緒線程,該位為0表示該優(yōu)先級下沒有就緒線程
僵尸線程鏈表
void rt_system_scheduler_init(void)
{
register rt_base_t offset;
rt_scheduler_lock_nest = 0;
RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("start scheduler: max priority 0x%02xn",
RT_THREAD_PRIORITY_MAX));
for (offset = 0; offset < RT_THREAD_PRIORITY_MAX; offset ++)
{
rt_list_init(&rt_thread_priority_table[offset]);
}
rt_current_priority = RT_THREAD_PRIORITY_MAX - 1;
rt_current_thread = RT_NULL;
/* initialize ready priority group /
rt_thread_ready_priority_group = 0;
#if RT_THREAD_PRIORITY_MAX > 32
/ initialize ready table /
rt_memset(rt_thread_ready_table, 0, sizeof(rt_thread_ready_table));
#endif
/ initialize thread defunct */
rt_list_init(&rt_thread_defunct);
}
接下來會調用rt_application_init?初始化一個main主線程(并不會馬上運行),主要完成組件的初始化以及多核的處理,最后跳入用戶的main?函數(shù)
完成組件初始化的實現(xiàn)與rt_components_board_init?類似,在BSP支持部分講解。
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_MAIN_THREAD_PRIORITY, 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_MAIN_THREAD_PRIORITY, 20);
RT_ASSERT(result == RT_EOK);
/* if not define RT_USING_HEAP, using to eliminate the warning */
(void)result;
#endif
rt_thread_startup(tid);
}
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
}
void rt_components_init(void)
{
#if RT_DEBUG_INIT
int result;
const struct rt_init_desc *desc;
rt_kprintf("do components intialization.n");
for (desc = &__rt_init_desc_rti_board_end; desc < &__rt_init_desc_rti_end; desc ++)
{
rt_kprintf("initialize %s", desc->fn_name);
result = desc->fn();
rt_kprintf(":%d donen", result);
}
#else
const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++)
{
(*fn_ptr)();
}
#endif
}
然后,rt_system_timer_thread_init?主要是初始化軟件定時器的列表,并且創(chuàng)建軟件定時器線程。而rt_thread_idle_init?創(chuàng)建空閑線程,在系統(tǒng)沒有任何用戶線程調度的時候,就會被調度起來,這個空閑線程主要是檢查系統(tǒng)有沒有已經消亡的線程,如果有,則把消亡線程的資源進行回收,如果系統(tǒng)使能了電源管理,則會讓系統(tǒng)進行低功耗模式。
最后通過rt_system_scheduler_start?開啟調度器。
BSP支持
首先需要提供startup_xxx.S?類似的啟動文件,一般來說可能包含中斷向量表以及默認的中斷服務函數(shù),以及選擇入口函數(shù),一般可能為_start?或者ResetHandler?。
AREA RESET, DATA, READONLY
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
; External Interrupts
DCD WWDG_IRQHandler ; Window Watchdog
DCD PVD_IRQHandler ; PVD through EXTI Line detect
DCD TAMPER_IRQHandler ; Tamper
DCD RTC_IRQHandler ; RTC
DCD FLASH_IRQHandler ; Flash
DCD RCC_IRQHandler ; RCC
DCD EXTI0_IRQHandler ; EXTI Line 0
DCD EXTI1_IRQHandler ; EXTI Line 1
DCD EXTI2_IRQHandler ; EXTI Line 2
DCD EXTI3_IRQHandler ; EXTI Line 3
DCD EXTI4_IRQHandler ; EXTI Line 4
DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1
DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2
DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3
DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4
DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5
DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6
DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7
DCD ADC1_2_IRQHandler ; ADC1_2
DCD USB_HP_CAN1_TX_IRQHandler ; USB High Priority or CAN1 TX
DCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0
DCD CAN1_RX1_IRQHandler ; CAN1 RX1
DCD CAN1_SCE_IRQHandler ; CAN1 SCE
DCD EXTI9_5_IRQHandler ; EXTI Line 9..5
DCD TIM1_BRK_IRQHandler ; TIM1 Break
DCD TIM1_UP_IRQHandler ; TIM1 Update
DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Commutation
DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare
DCD TIM2_IRQHandler ; TIM2
DCD TIM3_IRQHandler ; TIM3
DCD 0 ; Reserved
DCD I2C1_EV_IRQHandler ; I2C1 Event
DCD I2C1_ER_IRQHandler ; I2C1 Error
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SPI1_IRQHandler ; SPI1
DCD 0 ; Reserved
DCD USART1_IRQHandler ; USART1
DCD USART2_IRQHandler ; USART2
DCD 0 ; Reserved
DCD EXTI15_10_IRQHandler ; EXTI Line 15..10
DCD RTC_Alarm_IRQHandler ; RTC Alarm through EXTI Line
DCD USBWakeUp_IRQHandler ; USB Wakeup from suspend
__Vectors_End
__Vectors_Size EQU __Vectors_End - __Vectors
AREA |.text|, CODE, READONLY
; Reset handler routine
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
bsp支持主要提供對開發(fā)板的硬件初始化(通過rt_hw_board_init?向上提供接口)以及各類外設的驅動。
以RT-Thread Simulator 例程為例:rt_hw_board_init?中主要完成HAL庫的初始化、時鐘配置和RT-Thread系統(tǒng)堆的初始化,以及rt_components_board_init?會完成硬件的初始化。
#define RT_DEBUG_INIT 0
/**
- RT-Thread Components Initialization for board
*/
void rt_components_board_init(void)
{
#if RT_DEBUG_INIT
int result;
const struct rt_init_desc *desc;
for (desc = &__rt_init_desc_rti_board_start; desc < &__rt_init_desc_rti_board_end; desc ++)
{
rt_kprintf("initialize %s", desc->fn_name);
result = desc->fn();
rt_kprintf(":%d donen", result);
}
#else
const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < & **rt_init_rti_board_end; fn_ptr++)
{
(fn_ptr)();
}
#endif
}
?RT_USED?: attribute ((used))?,標識不允許編譯器進行優(yōu)化
?init_fn_t?:typedef int (init_fn_t)(void)?,函數(shù)指針
?##?:連接符
?SECTION?: attribute ((section(x)))?,執(zhí)行輸入節(jié)名稱
所以INIT_EXPORT(rti_board_start, "0.end")?等價于__attribute ((used)) const init_fn_t __rt_init_rti_board_start attribute ((section(".rti_fn.""0.end"))) = rti_board_start?
static int rti_board_start(void)
{
return 0;
}
INIT_EXPORT(rti_board_start, "0.end");
#define INIT_EXPORT(fn, level) RT_USED const init_fn_t _ rt_init ##fn SECTION(".rti_fn."level) = fn
?rt_board_end?同理,所以rt_components_board_init?的含義則為執(zhí)行__rt_init_rti_board_start?到__rt_init_rti_board_end?之間函數(shù)
指定節(jié).rti_fn.1?,根據(jù)鏈接器節(jié)放置規(guī)則,將放置在.rti_fn.0.end?節(jié)和.rti_fn.1.end?節(jié)之間。
#define INIT_BOARD_EXPORT(fn) INIT_EXPORT(fn, "1")
所以RT-Thread提供了另一個宏,它的主要作用就是用來在初始化硬件時調用相應的函數(shù)。所以一些外設驅動初始化都展開了這個宏。以RT-Thread Simulator 例程為例:
INIT_BOARD_EXPORT(rt_hw_usart_init);
INIT_BOARD_EXPORT(rt_hw_pin_init);
總的來說,一個基本的BSP主要任務是建立讓操作系統(tǒng)運行的基本環(huán)境,所以大致需要完成的主要工作是:
初始化CPU內部寄存器,設定RAM工作時序。
實現(xiàn)時鐘驅動及中斷控制器驅動,完善中斷管理。
實現(xiàn)串口和 GPIO 驅動。
初始化動態(tài)內存堆,實現(xiàn)動態(tài)堆內存管理。
CPU支持
這部分官方文檔很詳細,可參考內核移植 (rt-thread.org)
在嵌入式領域有多種不同 CPU 架構,例如 Cortex-M、ARM920T、MIPS32、RISC-V 等等。為了使 RT-Thread 能夠在不同 CPU 架構的芯片上運行,RT-Thread 提供了一個 libcpu 抽象層來適配不同的 CPU 架構。libcpu 層向上對內核提供統(tǒng)一的接口,包括全局中斷的開關,線程棧的初始化,上下文切換等。
RT-Thread 的 libcpu 抽象層向下提供了一套統(tǒng)一的 CPU 架構移植接口,這部分接口包含了全局中斷開關函數(shù)、線程上下文切換函數(shù)、時鐘節(jié)拍的配置和中斷函數(shù)、Cache 等等內容。下表是 CPU 架構移植需要實現(xiàn)的接口和變量。
libcpu 移植相關 API
-
電源管理
+關注
關注
115文章
6177瀏覽量
144443 -
定時器
+關注
關注
23文章
3246瀏覽量
114719 -
RT-Thread
+關注
關注
31文章
1285瀏覽量
40081 -
gcc編譯器
+關注
關注
0文章
78瀏覽量
3378 -
調度器
+關注
關注
0文章
98瀏覽量
5245
發(fā)布評論請先 登錄
相關推薦
評論