备注:基于正点原子阿波罗STM32F767IGT6开发板,RT-Thread Studio开发环境
目录
1、入口函数的确认
编译工具链为arm-none-eabi,根据链接文件link.lds中的关键字ENTRY可以确认程序的入口地址是Reset_Handler。
/*
* linker script for STM32F767IG with GNU ld
*/
/* Program Entry, set to mark it as "used" and avoid gc */
MEMORY
{
ROM (rx) : ORIGIN = 0x08000000, LENGTH = 1024k /* 1024K flash */
RAM (rw) : ORIGIN = 0x20020000, LENGTH = 384k /* 384K sram */
}
ENTRY(Reset_Handler)
_system_stack_size = 0x400;
2、函数执行流程
一图流,如图2.2.1
图2.2.1
3、关键函数功能详解
函数功能讲解增加到了代码注释中。
3.1 Reset_Handler
主板上电复位后从Reset_Handler开始执行,在进入SystemInit函数前,主要做了如下事情:
3.1.1 设置SP栈指针;
3.1.2 data段数据从flash搬运到SRAM;
3.1.3 bss段清零,然后跳转至SystemInit; 注意:地址相关信息均由link.lds链接文件在编译时确定。
.section .text.Reset_Handler
.weak Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
ldr sp, =_estack /* set stack pointer initialize, the '_estack' definition in the link file*/
/**
* Data segment: Data stored at compile time (not at runtime) that can be read and written.
* This is also known as static storage,
* where initialized global variables and initialized static variables are stored,
* and where constants are stored
*/
/* Copy the data segment initializers from flash to SRAM */
movs r1, #0 /* save 0 to R1 */
b LoopCopyDataInit
CopyDataInit: /* .data data copy */
ldr r3, =_sidata /* R3 = The .data is at the starting address of Flash */
ldr r3, [r3, r1] /* load the value of the [R3 + R1] address into R3 */
str r3, [r0, r1] /* save the value of R3 to the address of [R0 + R1] */
adds r1, r1, #4 /* R1 = R1 + 4 */
LoopCopyDataInit:
ldr r0, =_sdata /* assign the .data start address into R0 */
ldr r3, =_edata /* assign the .data end address into R3 */
adds r2, r0, r1 /* r2 = r0 + r1, */
cmp r2, r3 /* compare R2 and R3 value */
bcc CopyDataInit /* if R2 and R3 value not equality, jump */
ldr r2, =_sbss /* load the .bss satrt address into R2 */
b LoopFillZerobss /* jump to LoopFillZerobss */
/**
* BSS section:
* Global and static variables defined without initial values are placed in this section
*/
/* Zero fill the bss segment. */
FillZerobss:
movs r3, #0 /* save 0 to R3 */
str r3, [r2], #4 /* save the value of R3 to the address of [R2], and R2 = R2 + 4 */
LoopFillZerobss:
ldr r3, = _ebss /* load the .bss end address into R2 */
cmp r2, r3 /* compare R2 and R3 value */
bcc FillZerobss /* if R2 and R3 value not equality, jump */
/* Call the clock system initialization function.*/
bl SystemInit /* jump to SystemInit */
/* Call static constructors */
/* bl __libc_init_array */
/* Call the application's entry point.*/
bl entry /* jump to entry */
bx lr /* */
.size Reset_Handler, .-Reset_Handler
3.2 SystemInit()
该函数由ST官方提供,进行了FPU设置,时钟初始化,中断向量表偏移地址设置。
void SystemInit(void)
{
/* FPU settings ------------------------------------------------------------*/
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
#endif
/* Reset the RCC clock configuration to the default reset state ------------*/
/* Set HSION bit */
RCC->CR |= (uint32_t)0x00000001;
/* Reset CFGR register */
RCC->CFGR = 0x00000000;
/* Reset HSEON, CSSON and PLLON bits */
RCC->CR &= (uint32_t)0xFEF6FFFF;
/* Reset PLLCFGR register */
RCC->PLLCFGR = 0x24003010;
/* Reset HSEBYP bit */
RCC->CR &= (uint32_t)0xFFFBFFFF;
/* Disable all interrupts */
RCC->CIR = 0x00000000;
/* Configure the Vector Table location add offset address ------------------*/
#ifdef VECT_TAB_SRAM
SCB->VTOR = RAMDTCM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif
}
3.3 entry()
调用rtthread_startup(),进入RT-Thread启动阶段。
int entry(void)
{
rtthread_startup();
return 0;
}
3.4 rtthread_startup
内部进行函数调用,包括硬件初始化和OS kernel启动两大部分。
int rtthread_startup(void)
{
rt_hw_interrupt_disable();
/* board level initialization
* NOTE: please initialize heap inside board 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();
#ifdef RT_USING_SIGNALS
/* signal system initialization */
rt_system_signal_init();
#endif
/* create init_thread */
rt_application_init();
/* timer thread initialization */
rt_system_timer_thread_init();
/* idle thread initialization */
rt_thread_idle_init();
#ifdef RT_USING_SMP
rt_hw_spin_lock(&_cpus_lock);
#endif /*RT_USING_SMP*/
/* start scheduler */
rt_system_scheduler_start();
/* never reach here */
return 0;
}
3.4 rt_hw_interrupt_disable
硬件初始化前关闭中断,防止硬件初始化失败,由汇编语言实现,该函数操作了只有一位的中断屏蔽寄存器PRIMASK。
/*
* rt_base_t rt_hw_interrupt_disable();
* interrupt mask register
* This register has only one bit, and when set to 1, it will turn off all interrupt-masking exceptions,
* leaving only NMI and hard fault. The default value is 0
*
* operating instructions:
* MRS R0, PRIMASK ; R0=PRIMASK
* MSR PRIMASK, R0 ; PRIMASK=R0
* CPSID I ; PRIMASK=1
* CPSIE I ; PRIMASK=0
*/
.global rt_hw_interrupt_disable
.type rt_hw_interrupt_disable, %function
rt_hw_interrupt_disable:
MRS r0, PRIMASK
CPSID I
BX LR
/*
* void rt_hw_interrupt_enable(rt_base_t level);
*/
.global rt_hw_interrupt_enable
.type rt_hw_interrupt_enable, %function
rt_hw_interrupt_enable:
MSR PRIMASK, r0
BX LR
3.4 rt_hw_board_init
该函数中的堆初始化函数涉及到内存管理算法,具体实现较为复杂,这里不展开讲解,感兴趣的请自行学习,
RT_WEAK void rt_hw_board_init()
{
extern void hw_board_init(char *clock_src, int32_t clock_src_freq, int32_t clock_target_freq);
/* Heap initialization */
#if defined(RT_USING_HEAP)
rt_system_heap_init((void *) HEAP_BEGIN, (void *) HEAP_END);
#endif
hw_board_init(BSP_CLOCK_SOURCE, BSP_CLOCK_SOURCE_FREQ_MHZ, BSP_CLOCK_SYSTEM_FREQ_MHZ);
/* Set the shell console output device */
#if defined(RT_USING_DEVICE) && defined(RT_USING_CONSOLE)
rt_console_set_device(RT_CONSOLE_DEVICE_NAME);
#endif
/* Board underlying hardware initialization */
#ifdef RT_USING_COMPONENTS_INIT
rt_components_board_init();
#endif
}
3.5 hw_board_init
板级驱动初始化,包括时钟初始化,系统定时器Systick初始化,GPIO_Pin设备初始化,USART串口初始化。其中设备初始化用到了设备驱动框架,会在后续章节展开讲解。
void hw_board_init(char *clock_src, int32_t clock_src_freq, int32_t clock_target_freq)
{
extern void rt_hw_systick_init(void);
extern void clk_init(char *clk_source, int source_freq, int target_freq);
#ifdef SCB_EnableICache
/* Enable I-Cache---------------------------------------------------------*/
SCB_EnableICache();
#endif
#ifdef SCB_EnableDCache
/* Enable D-Cache---------------------------------------------------------*/
SCB_EnableDCache();
#endif
/* HAL_Init() function is called at the beginning of the program */
HAL_Init();
/* enable interrupt */
__set_PRIMASK(0);
/* System clock initialization */
clk_init(clock_src, clock_src_freq, clock_target_freq);
/* disbale interrupt */
__set_PRIMASK(1);
rt_hw_systick_init();
/* Pin driver initialization is open by default */
#ifdef RT_USING_PIN
extern int rt_hw_pin_init(void);
rt_hw_pin_init();
#endif
/* USART driver initialization is open by default */
#ifdef RT_USING_SERIAL
extern int rt_hw_usart_init(void);
rt_hw_usart_init();
#endif
}
3.6 rt_console_set_device
控制台设备绑定,这里在创建工程的时候指定为UART1(PA9、PA10),也涉及到了设备驱动框架,通过 rt_device_t rt_device_find(const char *name) 函数找到名为name的设备,然后将该设备的设备描述结构体赋值给全局变量 _console_device,然后在调用void rt_kprintf(const char *fmt, ...)等输出函数时,通过设备初始化时绑定的write函数进行具体的输出行为。
rt_device_t rt_console_set_device(const char *name)
{
rt_device_t new_device, old_device;
/* save old device */
old_device = _console_device;
/* find new console device */
new_device = rt_device_find(name);
if (new_device != RT_NULL)
{
if (_console_device != RT_NULL)
{
/* close old console device */
rt_device_close(_console_device);
}
/* set new console device */
rt_device_open(new_device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_STREAM);
_console_device = new_device;
}
return old_device;
}
rt_kprintf部分源码,着重理解rt_device_write()函数调用,指定了输出设备的具体行为。
void rt_kprintf(const char *fmt, ...)
{
va_list args;
rt_size_t length;
static char rt_log_buf[RT_CONSOLEBUF_SIZE];
va_start(args, fmt);
/* the return value of vsnprintf is the number of bytes that would be
* written to buffer had if the size of the buffer been sufficiently
* large excluding the terminating null byte. If the output string
* would be larger than the rt_log_buf, we have to adjust the output
* length. */
length = rt_vsnprintf(rt_log_buf, sizeof(rt_log_buf) - 1, fmt, args);
if (length > RT_CONSOLEBUF_SIZE - 1)
length = RT_CONSOLEBUF_SIZE - 1;
#ifdef RT_USING_DEVICE
if (_console_device == RT_NULL)
{
rt_hw_console_output(rt_log_buf);
}
else
{
rt_uint16_t old_flag = _console_device->open_flag;
_console_device->open_flag |= RT_DEVICE_FLAG_STREAM;
/* by rt_console_set_device() function bound output device */
rt_device_write(_console_device, 0, rt_log_buf, length);
_console_device->open_flag = old_flag;
}
#else
rt_hw_console_output(rt_log_buf);
#endif
va_end(args);
}
3.7 rt_components_board_init
该函数用于执行通过 INIT_BOARD_EXPORT(fn)宏把fn函数的地址放到.rti_fn.1段,该宏展开步骤如下:
第一步:INIT_EXPORT(fn, "1")
第二步:RT_USED const init_fn_t _rt_init##fn SECTION(".rti_fn." level) = fn
第三步:__attribute__((used)) const init_fn_t__rt_init_##fn SECTION(".rti_fn." level) = fn
第四步:__attribute__((used)) const init_fn_t__rt_init_##fn __attribute__((section(x))) = fn
以drv_clk.c中的INIT_BOARD_EXPORT(clock_information)宏为例,展开后形式如下:
__attribute__((used)) const init_fn_t __rt_init_clock_information __attribute__((section(".rti_fn." "1"))) = clock_information
翻译过来就是把 clock_information的名字拼接为__rt_init_clock_information并把函数地址放在.rti_fn.1 section段中
void rt_components_board_init(void)
{
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
}
该函数的执行需要结合.map文件来看,可知该函数共执行了两个函数,int rti_board_start(void) 和 int clock_information(void)。
*(SORT(.rti_fn*))
.rti_fn.0 0x0800d0d8 0x4 ./rt-thread/src/components.o
0x0800d0d8 __rt_init_rti_start
.rti_fn.0.end 0x0800d0dc 0x4 ./rt-thread/src/components.o
0x0800d0dc __rt_init_rti_board_start
.rti_fn.1 0x0800d0e0 0x4 ./drivers/drv_clk.o
0x0800d0e0 __rt_init_clock_information
.rti_fn.1.end 0x0800d0e4 0x4 ./rt-thread/src/components.o
0x0800d0e4 __rt_init_rti_board_end
.rti_fn.6 0x0800d0e8 0x4 ./rt-thread/components/finsh/shell.o
0x0800d0e8 __rt_init_finsh_system_init
.rti_fn.6.end 0x0800d0ec 0x4 ./rt-thread/src/components.o
0x0800d0ec __rt_init_rti_end
0x0800d0f0 __rt_init_end = .
0x0800d0f0 . = ALIGN (0x4)
3.8 rt_show_version
该函数打印输出OS系统版本,编译日期等信息。
void rt_show_version(void)
{
rt_kprintf("\n \\ | /\n");
rt_kprintf("- RT - Thread Operating System\n");
rt_kprintf(" / | \\ %d.%d.%d build %s\n",
RT_VERSION, RT_SUBVERSION, RT_REVISION, __DATE__);
rt_kprintf(" 2006 - 2019 Copyright by rt-thread team\n");
}
至此,板级硬件初始化结束,下一步进行OS内核启动。