STM32 BootLoader 刷新项目 (九) 跳转指定地址-下令0x55
前面我们讲述了几种BootLoader中的下令,包括获取软件版本号、获取帮助、获取芯片ID、读取Flash保护Level。
下面我们来先容一下BootLoader中最紧张的功能之一—跳转!就像BootLoader词汇中的Boot一词一样,就是启动跳转。
首先我们来先容一下Boot跳转的应用。
1. BootLoader跳转指定地址的应用
STM32的BootLoader中跳转指定地址的应用场景主要包括以下几个方面:
- 固件升级(Firmware Upgrade):
- BootLoader允许在不改变硬件毗连的环境下进行固件的在线升级。当新的固件版本需要部署到装备上时,BootLoader可以吸取新的固件并将其烧录到指定的Flash地址,然后跳转到新固件的执行地址,从而实现固件的无缝更新。
- 多应用管理(Multi-Application Management):
- 在一些复杂的应用中,可能需要在同一个装备上运行多个应用步伐。BootLoader可以通过跳转到不同的地址来选择性地加载和执行不同的应用步伐,实现多应用的管理。
- 体系恢复(System Recovery):
- 假如装备在运行过程中出现软件故障,BootLoader可以作为一个恢复点,通过跳转到备份的固件地址来恢复体系的正常运行。
- 安全启动(Secure Boot):
- 在安全敏感的应用中,BootLoader可以检查固件的正当性,确保只有颠末认证的固件才华被加载执行。这可以通过跳转到颠末签名验证的固件地址来实现。
- 调试和测试(Debugging and Testing):
- 在开发和测试阶段,BootLoader可以方便地进行步伐的调试和测试。开发者可以通过BootLoader跳转到不同的测试固件地址,快速验证新功能或修复bug。
- 节流资源(Resource Saving):
- 对于资源受限的嵌入式体系,BootLoader可以减少对外部存储器的需求,通过内部Flash存储固件,节流成本和空间。
- 产品差异化(Product Differentiation):
- 通过BootLoader,制造商可以为不同的市场或客户定制不同的固件版本,通过跳转到不同的固件地址来实现产品的差异化。
- 长途维护(Remote Maintenance):
- BootLoader支持长途固件更新,使得装备的维护和升级可以在不打仗硬件的环境下完成,这对于长途或难以打仗的装备尤为紧张。
这些应用场景展示了BootLoader在STM32微控制器中的机动性和紧张性,它们使得装备可以或许更加智能、安全和易于维护。
2. 函数跳转的方法
在STM32的BootLoader中跳转到指定地址通常涉及到以下几个步调:
- 验证目标地址:确保目标地址是有效的,而且位于应用步伐的正当执行区域内。
- 设置堆栈指针:将堆栈指针(MSP)设置为应用步伐的初始堆栈值。
- 跳转到应用步伐:使用函数指针或者直接修改步伐计数器(PC)来跳转到应用步伐的入口点。
下面是一个具体的代码示例,包括注释,解释每一步的作用:
- #include "stm32f10x.h" // 包含STM32F10x系列的头文件
- // 假设应用程序的入口地址存储在特定的Flash地址
- #define APP_ENTRY_ADDR (0x08005000) // 应用程序的入口地址
- // 跳转到应用程序的函数
- void Jump_To_Application(void) {
- volatile uint32_t *appEntryAddr; // 指向应用程序入口地址的指针
- void (*Reset_Handler)(void); // 应用程序的Reset_Handler函数指针
- // 将appEntryAddr指向存储应用程序入口地址的位置
- appEntryAddr = (uint32_t *) APP_ENTRY_ADDR;
- // 检查应用程序入口地址是否有效
- // 这里简单地检查地址是否在Flash范围内,实际应用中可能需要更复杂的检查
- if ((*appEntryAddr & 0x2FFE0000) == 0x08000000) {
- // 读取应用程序的入口地址
- uint32_t appStartAddr = *appEntryAddr;
- // 将Reset_Handler函数指针指向应用程序的Reset_Handler
- Reset_Handler = (void (*)(void)) appStartAddr;
- // 设置堆栈指针为应用程序的初始堆栈值
- // 这通常是应用程序入口地址的下一个地址
- __set_MSP(*((volatile uint32_t *) appStartAddr + 1));
- // 关闭所有中断
- NVIC->ICER[0] = 0xFFFFFFFF; // 禁用所有中断
- NVIC->ICPR[0] = 0xFFFFFFFF; // 清除所有中断挂起位
- // 跳转到应用程序的Reset_Handler
- Reset_Handler();
- } else {
- // 应用程序入口地址无效,可以在这里处理错误
- while(1) {
- // 错误处理代码
- }
- }
- }
复制代码 代码解释:
- 包罗头文件:包罗STM32F10x系列的头文件,以便使用STM32的寄存器界说和宏。
- 界说应用步伐入口地址:APP_ENTRY_ADDR是存储应用步伐入口地址的位置。
- 跳转到应用步伐的函数:Jump_To_Application函数执行跳转到应用步伐的操纵。
- 指向应用步伐入口地址的指针:appEntryAddr指向存储应用步伐入口地址的位置。
- 应用步伐的Reset_Handler函数指针:Reset_Handler是一个函数指针,指向应用步伐的Reset_Handler函数。
- 检查应用步伐入口地址是否有效:通过检查地址是否在Flash范围内来验证地址的有效性。
- 读取应用步伐的入口地址:从appEntryAddr读取应用步伐的实际入口地址。
- 设置堆栈指针:将MSP设置为应用步伐的初始堆栈值,通常是应用步伐入口地址的下一个地址。
- 关闭全部中断:禁用全部中断并清除全部中断挂起位,以确保跳转过程中不会被中断干扰。
- 跳转到应用步伐的Reset_Handler:通过Reset_Handler函数指针跳转到应用步伐的Reset_Handler函数,开始执行应用步伐代码。
这个代码示例展示了如安在BootLoader中跳转到应用步伐的入口地址,包括地址验证、堆栈指针设置和实际的跳转操纵。在实际应用中,你可能需要根据具体的硬件和应用步伐需求调整这些步调。
3. 0x55下令先容–地址跳转
在本篇文章,我们的主要是先容0x55的下令,这个下令主要是在BootLoader中让步伐跳转到指定地址。
通过上位机发送10 Byte的数据,其中第1 Byte为整个数据的长度,第2Byte为指令码,第3-6 Byte为跳转的地址,第7-10 Byte为前6个Byte的CRC校验值。上位机通过串口UART发送给下位机,下位机回复地址是否跳转成功的标志。
下面是整个步伐执行地址跳转的流程图:
4. 步伐计划
下面我们来进行步伐计划,下面是读取上位机指令,并解析指令的过程,通过switch case判断执行哪种下令。目前通过上位机执行BL_GO_TO_ADDR指令,然后执行bootloader_handle_go_cmd(bl_rx_buffer)函数。
- void bootloader_uart_read_data(void)
- {
- uint8_t rcv_len=0;
- printmsg_Host("BL_DEBUG_MSG: Receive CMD\n\r");
- while (1)
- {
- HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);
- memset(bl_rx_buffer, 0, 200);
- //here we will read and decode the commands coming from host
- //first read only one byte from the host , which is the "length" field of the command packet
- HAL_UART_Receive(C_UART,bl_rx_buffer,1,HAL_MAX_DELAY);
- rcv_len= bl_rx_buffer[0];
- HAL_UART_Receive(C_UART,&bl_rx_buffer[1],rcv_len,HAL_MAX_DELAY);
- switch(bl_rx_buffer[1])
- {
- case BL_GET_VER:
- bootloader_handle_getver_cmd(bl_rx_buffer);
- break;
- case BL_GET_HELP:
- bootloader_handle_gethelp_cmd(bl_rx_buffer);
- break;
- case BL_GET_CID:
- bootloader_handle_getcid_cmd(bl_rx_buffer);
- break;
- case BL_GET_RDP_STATUS:
- bootloader_handle_getrdp_cmd(bl_rx_buffer);
- break;
- case BL_GO_TO_ADDR:
- bootloader_handle_go_cmd(bl_rx_buffer);
- break;
- default:
- printmsg("BL_DEBUG_MSG:Invalid command code received from host \n");
- break;
- }
- }
- }
复制代码 下面是地址跳转函数,通过过去输入的Buffer进行解析出跳转地址,判断跳转地址是否在精确的跳转范围内,假如在精确的跳转范围内,则向上位机发送可以或许跳转的回复,之后执行地址跳转。假如不在跳转范围内,则向上位机回复,地址不精确,请重新输入。
- /*Helper function to handle BL_GO_TO_ADDR command */
- void bootloader_handle_go_cmd(uint8_t *pBuffer)
- {
- uint32_t go_address=0;
- uint8_t addr_valid = ADDR_VALID;
- uint8_t addr_invalid = ADDR_INVALID;
- printmsg("BL_DEBUG_MSG:bootloader_handle_go_cmd\n");
- //Total length of the command packet
- uint32_t command_packet_len = bl_rx_buffer[0]+1 ;
- //extract the CRC32 sent by the Host
- uint32_t host_crc = *((uint32_t * ) (bl_rx_buffer+command_packet_len - 4) ) ;
- if (! bootloader_verify_crc(&bl_rx_buffer[0],command_packet_len-4,host_crc))
- {
- printmsg("BL_DEBUG_MSG:checksum success !!\n");
- bootloader_send_ack(pBuffer[0],1);
- //extract the go address
- go_address = *((uint32_t *)&pBuffer[2] );
- printmsg("BL_DEBUG_MSG:GO addr: %#x\n",go_address);
- if( verify_address(go_address) == ADDR_VALID )
- {
- //tell host that address is fine
- bootloader_uart_write_data(&addr_valid,1);
- /*jump to "go" address.
- we dont care what is being done there.
- host must ensure that valid code is present over there
- Its not the duty of bootloader. so just trust and jump */
- /* Not doing the below line will result in hardfault exception for ARM cortex M */
- //watch : https://www.youtube.com/watch?v=VX_12SjnNhY
- go_address+=1; //make T bit =1
- void (*lets_jump)(void) = (void *)go_address;
- printmsg("BL_DEBUG_MSG: jumping to go address! \n");
- lets_jump();
- }else
- {
- printmsg("BL_DEBUG_MSG:GO addr invalid ! \n");
- //tell host that address is invalid
- bootloader_uart_write_data(&addr_invalid,1);
- }
- }else
- {
- printmsg("BL_DEBUG_MSG:checksum fail !!\n");
- bootloader_send_nack();
- }
- }
复制代码 5. 实战演练
下面是上位机的下令菜单,通过在终端调用Python脚本,然后在终端输入下位机毗连的串标语,即可进入下令界面,目前可支持如下下令:
在上位机中输入下令5,即为在BootLoader中执行地址跳转下令0x55,MCU根据读取跳转地址0xXXXXXXXX的值,来进行跳转,并告诉上位性能否跳转成功,由下图可以看出,当前地址跳转成功。
目前我在STM32中刷了两段步伐,其中BootLoader步伐即为0x0800 0000-0x0800 7FFF处,背面0x08000 8000-0x080F FFFF为APP应用步伐,则当我将跳转地址设为0x0800 8000时,MCU则会从BootLoader跳转进入到APP应用步伐中。
但为什么我们在输入跳转指令地址为0x08008000的时候,并没有跳转到App步伐呢,下面我通过Hex和各人解释一下。
下面我们用STM32官方提供的工具STM32CubeProgrammer工具读取MCU中的Hex信息。
Note:必须在毗连ST-Link的时候才华表现Hex信息。
由上图我们可以看出在0x08008000地址开始处,其中第一个地址0x0800 8000地址中的值时0x20020000,这个值时Reset Handle,即中断向量表的起始地址。第二个地址0x0800 8004中的值才是实际App步伐所在的Flash地址。
以是在下图中,我们执行地址跳转指令0x55,跳转地址设为0x0800 8B16,为什么比0x0800 8B15多1呢,这是由于我们使用的Thumb指令,执行的指令结尾必须+1.
下面我们通过串口监控一下Mcu是否跳转成功,由下图可以看出,MCU成功跳转到App步伐中。
6. 系列文章
STM32 BootLoader 刷新项目 (一) STM32CubeMX UART串口通信工程搭建
STM32 BootLoader 刷新项目 (二) 方案先容
STM32 BootLoader 刷新项目 (三) 步伐框架搭建及刷新演示
STM32 BootLoader 刷新项目 (四) 通信协议
STM32 BootLoader 刷新项目 (五) 获取软件版本号-下令0x51
STM32 BootLoader 刷新项目 (六) 获取帮助-下令0x52
STM32 BootLoader 刷新项目 (七) 获取芯片ID-0x53
STM32 BootLoader 刷新项目 (八) 读取Flash保护ROP-0x54
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |