4.1 查看程序入口
查看./obj/main/betaflight_AT32F437DEV.map文件,发现在Flash的起始地址即0x0800 0000处链接的是startup_at32f435_437.o文件,startup_at32f435_437.o文件由startup_at32f435_437.S汇编文件编译而来。则程序启动后会先执行startup_at32f435_437.S汇编文件。 (具体芯片启动规则还不太清楚,知道的大佬不吝赐教)。
.isr_vector 0x0000000008000000 0x20c obj/main/AT32F437DEV/startup_at32f435_437.o
0x0000000008000000 g_pfnVectors
0x000000000800020c . = ALIGN (0x4)
[!provide] PROVIDE (isr_vector_table_end = .
4.2 汇编 .S启动文件
打开 obj/main/AT32F437DEV/目录下的startup_at32f435_437.S文件。
4.2.1 汇编程序入口
汇编程序的默认入口标号是_start , 不过startup_at32f435_437.S中没有该标号,而是使用了自定义的入口标号Reset_Handler,该入口标号的定义方法是在链接脚本中使用ENTRY进行指明。
打开./src/link/at32_flash_f43xM.ld链接脚本文件,发现在该脚本50行引用了另一个链接脚本文件:
INCLUDE "at32_flash_f4_split.ld"
打开与at32_flash_f43xM.ld同目录的at32_flash_f4_split.ld文件,在12行找到如下代码:
/* Entry Point */
ENTRY(Reset_Handler)
这里用ENTRY指明了汇编文件的入口标号Reset_Handler
4.2.2 调用C语言main函数
在startup_at32f435_437.S文件第60行找到Reset_Handler,其下依次使用bl指令(执行后会返回到原处)调用了一些C言初始化函数、用b指令(执行后不会返回)调用许多汇编文件下的子标号(类似于C语言的函数调用)。
Reset_Handler:
/* custom init */
ldr sp, =_estack /* set stack pointer 设置栈指针*/
bl persistentObjectInit /*保护数据初始化*/
bl checkForBootLoaderRequest /*进行一些关于BootLoader支持的操作*/
/* Copy the data segment initializers from flash to SRAM */
movs r1, #0
b LoopCopyDataInit
CopyDataInit:
ldr r3, =_sidata
ldr r3, [r3, r1]
str r3, [r0, r1]
adds r1, r1, #4
LoopCopyDataInit:
ldr r0, =_sdata
ldr r3, =_edata
adds r2, r0, r1
cmp r2, r3
bcc CopyDataInit
ldr r2, =_sbss
b LoopFillZerobss
/* Zero fill the bss segment. */
FillZerobss:
movs r3, #0
str r3, [r2], #4
LoopFillZerobss:
ldr r3, = _ebss
cmp r2, r3
bcc FillZerobss
ldr r2, =_sfastram_bss
b LoopFillZerofastram_bss
/* Zero fill the fastram_bss segment. */
FillZerofastram_bss:
movs r3, #0
str r3, [r2], #4
LoopFillZerofastram_bss:
ldr r3, = _efastram_bss
cmp r2, r3
bcc FillZerofastram_bss
/* Call the clock system intitialization function.*/
bl SystemInit
/* Call static constructors */
bl __libc_init_array
/* Call the application's entry point.*/
bl main /*调用所有main函数?*/
bx lr
对汇编子标号的调用流程如下:
Reset_Handler (60)
-》LoopCopyDataInit (77)
-》LoopFillZerobss (84)
-》LoopFillZerofastram_bss (103)
最终在LoopFillZerofastram_bss下调用C语言的main函数
bl main /*调用所有main函数?*/
不过这里还有些问题,全文搜索int main(void)函数,发现会出现许多结果,大部分都为各种驱动与设备.c文件的,看内容类似初始化之类的操作。只有src/main/目录下main.c文件中的main函数最像c语言程序入口。
所以这里的bl main指令是只调用了main.c里的main函数(并未找到extern之类的声明),还是所有的main函数都调用了一遍,就不得而知了。先闻道的前辈不吝赐教O(∩_∩)O谢谢!
同时为了解决上术问题,可以在vscode文件夹下建立一个.vscode/settings.json的设置文件,在文件中输入:
{
//搜索注释
"search.exclude": {
"**.o": true,
"src/main/target/[B-Z]*":true,
"src/main/target/A[A-S]*":true,
"src/main/target/AT32F403ADEV*":true,
},
//文件目录注释
"files.exclude": {
"**.o": true,
"src/main/target/[B-Z]*":true,
"src/main/target/A[A-S]*":true,
"src/main/target/AT32F403ADEV*":true,
}
}
语法规则为:
"search.exclude": {} 表示搜索结果中排除
"files.exclude": {} 表示左侧工程目录中排除
"arch/arm/boot/dts/imx6ul-*":true, “true”表示排除, “false”表示不排除
**表示前缀匹配, *表示后缀匹配
[A-S] 表示对A~S所有字母进行匹配
4.3 C语言函数初始化
main() -》init() -》systemInit() 函数
4.3.1 systemInit() 函数
搜索发现systemInit() 函数在src/main/drivers/system.h 中初声明,确在src/main/drivers/system_at32f43x.c 、 src/main/target/SITL/target.c 与 obj/main/AT32F437DEV/drivers/system_at32f43x.i 三个文件中都有定义。
C语言中同名函数重复定义编译器会报错,这里编译成功说明一定有两会被舍弃。
首先obj/main/AT32F437DEV/drivers/system_at32f43x.i 位于obj输出文件夹下,是编译过程中产生的,可以先排除。
其次,打开make/targets.mk 文件发现 35~65行:
ifeq ($(TARGET),$(filter $(TARGET),$(F3_TARGETS)))
……
else ifeq ($(TARGET),$(filter $(TARGET), $(SITL_TARGETS)))
TARGET_MCU := SITL
SIMULATOR_BUILD = yes
……
else ifeq ($(TARGET),$(filter $(TARGET), $(AT32F43x_TARGETS)))
TARGET_MCU := AT32F43x
else
……
endif
在这个Makefile文件中,由于TARGET_MCU变量最终被赋值AT32F43x而不是SITL(一种芯片类型),所以在编译时src/main/target/SITL/下的文件并未被包含进来,故systemInit() 函数使用src/main/drivers/system_at32f43x.c中的定义。
验证的话,可以分别将两个.c文件中systemInit() 函数的定义注释掉,重新编译看哪个会出现报错。
4.3.2 其他模块命令等初始化
BF1在init()函数中对LED、陀螺仪等各种模块与命令进行初始化,去掉不用的宏为:
void init(void)
{
#ifdef SERIAL_PORT_COUNT
printfSerialInit();
#endif
……
swdPinsInit();
unusedPinsInit();
tasksInit();
systemState |= SYSTEM_STATE_READY;
}
这里注意在init()函数的最后,对各任务进行了初始化,即tasksInit();函数。
4.3.3 tasksInit() 函数
其中主要进行了三部分操作,使用schedulerInit() 函数进行调度器初始化,使用setTaskEnabled() 函数对各任务进行使能,使用rescheduleTask() 函数对各任务的运行周期重新设置等。
C语言部分的初始化流程大致如下: