File Name : startup_stm32l071xx.s
1. 裸机
启动文件由汇编编写,是系统上电复位后第一个执行的程序。主要做了以下工作:
- 初始化堆栈指针 SP(__initial_sp)
- 初始化 PC 指针(Reset_Handler)
- 初始化中断向量表(__Vectors)
- 配置系统时钟(SystemInit)
- 调用 C 库函数 _main 初始化用户堆栈,从而最终调用 main 函数去到 C 的世界
1.栈设置
Stack_Size EQU 0x200
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
开辟栈的大小为 0X00000400(1KB),名字为 STACK,NOINIT 即不初始化,可读可写,8(2^3)字节对齐
栈的作用是用于局部变量,函数调用,函数形参等的开销,栈的大小不能超过内部 SRAM 的大小
如果编写的程序比较大,定义的局部变量很多,那么就需要修改栈的大小。如果某一天,你写的程序出现了莫名奇怪的错误,并进入了硬 fault 的时候,这时你就要考虑下是不是栈不够大,溢出了
标号 __initial_sp 紧挨着 SPACE 语句放置,表示栈的结束地址,即栈顶地址,栈是由高向低生长的
2. 堆设置
Heap_Size EQU 0x200
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
开辟堆的大小为 0X00000200(512 字节),名字为 HEAP,NOINIT 即不初始化,可读可写
__heap_base 表示对的起始地址,__heap_limit 表示堆的结束地址。堆是由低向高生长的,跟栈的生长方向相反
堆主要用来动态内存的分配,像 malloc() 函数申请的内存就在堆上面
3. 向量表
; Vector Table Mapped to Address 0 at Reset
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 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD 0 ; Reserved
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 RTC_IRQHandler ; RTC through EXTI Line
DCD FLASH_IRQHandler ; FLASH
DCD RCC_IRQHandler ; RCC
DCD EXTI0_1_IRQHandler ; EXTI Line 0 and 1
DCD EXTI2_3_IRQHandler ; EXTI Line 2 and 3
DCD EXTI4_15_IRQHandler ; EXTI Line 4 to 15
DCD 0 ; Reserved
DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1
DCD DMA1_Channel2_3_IRQHandler ; DMA1 Channel 2 and Channel 3
DCD DMA1_Channel4_5_6_7_IRQHandler ; DMA1 Channel 4, Channel 5, Channel 6 and Channel 7
DCD ADC1_COMP_IRQHandler ; ADC1, COMP1 and COMP2
DCD LPTIM1_IRQHandler ; LPTIM1
DCD USART4_5_IRQHandler ; USART4 and USART5
DCD TIM2_IRQHandler ; TIM2
DCD TIM3_IRQHandler ; TIM3
DCD TIM6_IRQHandler ; TIM6
DCD TIM7_IRQHandler ; TIM7
DCD 0 ; Reserved
DCD TIM21_IRQHandler ; TIM21
DCD I2C3_IRQHandler ; I2C3
DCD TIM22_IRQHandler ; TIM22
DCD I2C1_IRQHandler ; I2C1
DCD I2C2_IRQHandler ; I2C2
DCD SPI1_IRQHandler ; SPI1
DCD SPI2_IRQHandler ; SPI2
DCD USART1_IRQHandler ; USART1
DCD USART2_IRQHandler ; USART2
DCD LPUART1_IRQHandler ; LPUART1
DCD 0 ; Reserved
DCD 0 ; Reserved
__Vectors_End
4. 复位程序
_main 是一个标准的 C 库函数,主要作用是初始化用户堆栈,并在函数的最后调用 main 函数去到 C 的世界
; 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
; Dummy Exception Handlers (infinite loops which can be modified)
//2. 主函数
int main()
{
}
2.操作系统
//1. 启动文件
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
// 2. 主函数之前代码(理解为抢占了进入主函数的先机)
#if defined(__CC_ARM) || defined(__CLANG_ARM)
extern int $Super$$main(void);
/* re-define main function */
int $Sub$$main(void)
{
rtthread_startup();
return 0;
}
//3. 真正初始化启动
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();
/* 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;
}
//4. 里面创建main线程
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);
}
//5. 恢复跳到主函数
/* the system main thread */
void main_thread_entry(void *parameter)
{
extern int main(void);
extern int $Super$$main(void);
#ifdef RT_USING_COMPONENTS_INIT
/* RT-Thread components initialization */
rt_components_init();
#endif
//恢复跳主函数
/* invoke system main function */
#if defined(__CC_ARM) || defined(__CLANG_ARM)
$Super$$main(); /* for ARMCC. */
#elif defined(__ICCARM__) || defined(__GNUC__)
main();
#endif
}
- 上面操作系统相当于在使用库函数_main之前作弊插入一个函数
- 这个函数做了初始化操作系统资源的作用
- 然后在某一个线程里恢复到主函数main的运行
- 相当于主函数mian只是操作系统的一个线程
示例:
在主程序执行前插入一段新程序
int $Sub$$main(void)
{
rt_hw_interrupt_disable();
rtthread_startup();
return 0;
}
恢复主函数main
#if defined (__CC_ARM)
$Super$$main(); /* for ARMCC. */
#elif defined(__ICCARM__) || defined(__GNUC__)
main();
#endif
rtthread_startup() 是进入主程序前的系统初始化,因此这两段程序作用就是为了完成主程序运行前的系统初始化工作,其中int Sub main(void) 是在主程序main前插入一段新代码,这段代码可以用来初始化系统,当然也可以做其他事情;
而Super main() 是在主程序main前插入一段已有的代码,比如以前写好的一段系统初始化的程序段 。
使用示例
void $Sub$$main(void)
{
extern int $Super$$main(void);
//初始化HAL
HAL_Init();
//初始化系统时钟
SystemClock_Config();
delay_init(80); //初始化延时函数 80M系统时钟
uart_init(115200); //初始化串口,波特率为115200
printf("初始化已完成\n");
//回到真正的main函数里
$Super$$main();
}
int main(void)
{
u8 len;
u16 times = 0;
// HAL_Init();
// SystemClock_Config(); //初始化系统时钟为80M
// delay_init(80); //初始化延时函数 80M系统时钟
// uart_init(115200); //初始化串口,波特率为115200
printf("main()\r\n");
while(1)
{
}
通俗一点讲,实际上就相当于在main函数之前先运行了$ Sub $ $ main这个函数,
并且在$ Sub $ $ main这个函数里最后一句$ Super $ $ main();又用真正的main函数整体替换这个$ Super $ $main(),
- sub
- super
以上使用注意点:
void $ Sub$$main(void)和int main(void)可以不放在同一个C文件中,就算放在同一文件中也没有先后顺序之分;
extern int $ Super $ $ main(void);必须要有,且放在 $ Super $ $ main();调用之前,可以放在$ Sub $ $main函数里也可以放在外面。
$ Super $ 和 和和 Sub $ $基本是要成对使用
需要特定编译器才支持该符号,比如MDK
$ Super $ 和 和和 Sub $ $不只用于main函数,也可以用于其他任意函数