MCU通过Bootloader实现了接收、解析、刷写并校验完上位机发送的固件(.bin)后,需要跳转到固件程序

首先,我们先复习一下main()函数的跳转步骤

一、MCU 上电后 main 函数的启动

MCU启动核心是中断向量表 _Vectors ,其中存了 MSP、Reset_Handler、中断函数入口地址

其中Reset_Handler里包含了跳转main动作

__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

固件APP的.bin文件开头是 startup.s 中定义的中断向量表。Bootloader 跳转 APP 时,需要先找到 APP 固件在 Flash 中的起始地址,也就是 APP 向量表地址

二、Bootloader 跳转固件流程

2.1 关闭Bootloader外设以及中断

led_off(led1);
TIM_DeInit(TIM6);
USART_DeInit(USART1);
USART_DeInit(USART3);
NVIC_DisableIRQ(TIM6_DAC_IRQn);
NVIC_DisableIRQ(USART1_IRQn);
NVIC_DisableIRQ(USART3_IRQn);

2.2 跳转程序

step1:跳转固件时,我们首先得更新中断向量表的位置(固件中断向量表存放在刷写.bin文件的开头)

SCB->VTOR 是 Cortex-M 内核中的 向量表偏移寄存器,用于指定当前程序使用哪一张中断向量表。

如果不修改 SCB->VTOR,即使程序已经跳转到 APP,发生中断时 CPU 仍然会去 Bootloader 的中断向量表中查找中断服务函数,这会导致中断执行错误,甚至程序跑飞。

举个例子:在进入固件主程序的中断时,可能跑偏到Bootloader的中断配置

void USART3_IRQHandler(void)
{
    if (USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)
    {
        if (rx_callback)
        {
            uint8_t data = (uint8_t)USART_ReceiveData(USART3);
            rx_callback(&data, 1);
        }
        USART_ClearITPendingBit(USART3, USART_IT_RXNE);
    }
}

上述为Bootloader串口3中断函数实现数据的接收并写入环形缓冲区,而固件APP串口3中断函数可能实现其他功能,因此需要更新SCB->VTOR。

step2:配置MSP、PC指针指向固件的中断向量表

//step1. 更新中断向量表位置
SCB->VTOR = APP_VTOR_ADDR;  //APP_VTOR_ADDR = 0x08010000;

//step2. 程序跳转
extern void JumpApp(uint32_t base);
JumpApp(APP_VTOR_ADDR);


//跳转程序
JumpApp         PROC
                LDR     SP, [R0, #0]
                LDR     PC, [R0, #4]
                ENDP
END

这里跳转程序采用汇编语言,更新MSP、PC栈顶;下一步进入固件的中断向量表→Reset_Handle →SystemInit→main(),继而进入了固件的主程序

Logo

免费领 150 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐