bootloader 主函数
1)我们以 stm32f103xb_bl 为例,从 bootloader 项目的 main() 函数开始:
- (1)sdk_init():MCU 初始化
- (2)gpio_init():USB 相关引脚配置
- (3)config_init():RAM 中配置初始化
- (4)board\_bootloader\_init():nothing
- (5)reset_button_pressed() 和 modify_stack_pointer_and_start_app():运行 BL or IF
(6)OS 和 main task 配置:
- osKernelInitialize():初始化
- osThreadNew(main\_task):主任务回调函数 main\_task()
- osKernelStart():启动主任务
1 sdk_init()
1)sdk_init() 是 DAPLINK 提供的 厂商 sdk 初始化 接口函数。移植时可实现该函数,以进行时钟等外设上电初始化:
// 在 /source/hic_hal/sdk.h 文件中声明
void sdk_init(void);
// 在 /source/daplink/sdk_stub.c 文件中提供默认实现
__WEAK void sdk_init()
{
// Do nothing
}
2)在 stm32f103xb_bl 项目中,该函数的调用逻辑如下:
2 gpio_init()
1)gpio_init() 是 DAPLINK 提供的 LED、按键初始化接口函数。移植时可实现该函数,用来来指示 USB 状态、等
2)在 stm32f103xb_bl 项目中,该函数的调用逻辑如下:
3 config_init()
config_init() 用来进行配置初始化。其创建 static cfg_ram_t 结构体的两个实例:config_ram 和 config_ram_copy。
可以用来对配置进行备份:比如我们在程序运行过程中(非上电)调用 config_init() 函数,则当前配置将保存到 config_ram_copy 中;此时我们可以对配置进行临时修改,修改完成后如果我们想恢复,只需要将 config_ram.key 修改一下后再次调用 config_init() 函数即可。
2)在 stm32f103xb_bl 项目中,该函数的调用逻辑如下:
4 board_bootloader_init()
board_bootloader_init() 是 DAPLINK 提供的板级 bootloader 初始化接口函数。移植时可实现该函数,用来对开发板进行初始化(DAPLINK 中,除了 nrf52820 芯片对 bootloader 所在的 flash 进行锁定外,没有其它厂商实现该函数)。
// 定义在 source/daplink/bootloader/main_bootloader.c 文件中
__WEAK void board_bootloader_init()
{
return;
}
5 运行 IF 还是 BL?
BL 表示 bootloader,用来升级 IF;IF 则表示 interface,是 DAPLINK 实现烧录的执行代码。
该部分代码逻辑如下:
if (!reset_button_pressed()
&& g_board_info.target_cfg
&& validate_bin_nvic((uint8_t *)g_board_info.target_cfg->flash_regions[0].start)
&& !config_ram_get_initial_hold_in_bl()) {
// change to the new vector table
SCB->VTOR = g_board_info.target_cfg->flash_regions[0].start; //bootloaders should only have one flash region for interface
// modify stack pointer and start app
modify_stack_pointer_and_start_app((*(uint32_t *)(g_board_info.target_cfg->flash_regions[0].start)),
(*(uint32_t *)(g_board_info.target_cfg->flash_regions[0].start + 4)));
}
- 1)首先是 reset_button_pressed() 函数,该函数主要用来判断当前是否是复位状态。以 stm32f103xb_bl 为例来看,当 MCU 低电平复位时,该函数返回 1,无法进入 if 逻辑内。
2)然后是 validate_bin_nvic() 函数,该函数有两个判断:
(1)判断 SP 指针。即 IF 在 FLASH 起始地址存储的内容是否在 RAM 的地址范围内。
当我们没有烧录 IF 时,该地址上存储值为 0xFFFFFFFF,不在 RAM 范围内。将无法进入到该 if 判断内部执行。
- (2)判断 Reset_Handler、NMI_Handler、HardFault_Handler 指针。即 IF 在 FLASH 起始地址偏移 4、8、12 位置存储的内容是否在超出 FLASH 区域
- 3)最后是 config_ram_get_initial_hold_in_bl() 函数,该函数用来判断是否 hold in bootloader。
cfg_ram_t config_ram 结构体持有的 uint8_t 类型的 hold_in_bl 变量,该变量只有在虚拟文件系统 vfs 改变时才会通过调用 config_ram_set_hold_in_bl() 函数来修改。 - 4)modify_stack_pointer_and_start_app() 函数,从 FLASH 中 IF 所在位置开始运行。
6 main_task()
- 1)OsKernelInitialize():初始化 OS 内核
- 2)osThreadNew():将 main_task() 函数赋值给到 OS 的主函数回调上。
- 3)osKernelStart():回调 main_task() 函数
7 总结
关于 main() 函数,我们来总结一下移植时需要修改的地方:
移植点 | 描述 |
---|---|
sdk_init() | 厂商 sdk 初始化。主要用来进行时钟 RCC 的初始化 |
gpio_init() | led、button 初始化。USB 状态指示灯、复位按钮初始化 |
board_bootloader_init() | 缺省。bootloader flash |
board_info_t g_board_info | 板子信息定义。包括 daplink 名称、mcu flash/ram 信息等 |
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。