STM32 BootLoader 刷新项目 (九) 跳转指定地址-下令0x55

打印 上一主题 下一主题

主题 1733|帖子 1733|积分 5199

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)来跳转到应用步伐的入口点。
下面是一个具体的代码示例,包括注释,解释每一步的作用:
  1. #include "stm32f10x.h" // 包含STM32F10x系列的头文件
  2. // 假设应用程序的入口地址存储在特定的Flash地址
  3. #define APP_ENTRY_ADDR   (0x08005000) // 应用程序的入口地址
  4. // 跳转到应用程序的函数
  5. void Jump_To_Application(void) {
  6.     volatile uint32_t *appEntryAddr; // 指向应用程序入口地址的指针
  7.     void (*Reset_Handler)(void);      // 应用程序的Reset_Handler函数指针
  8.     // 将appEntryAddr指向存储应用程序入口地址的位置
  9.     appEntryAddr = (uint32_t *) APP_ENTRY_ADDR;
  10.     // 检查应用程序入口地址是否有效
  11.     // 这里简单地检查地址是否在Flash范围内,实际应用中可能需要更复杂的检查
  12.     if ((*appEntryAddr & 0x2FFE0000) == 0x08000000) {
  13.         // 读取应用程序的入口地址
  14.         uint32_t appStartAddr = *appEntryAddr;
  15.         // 将Reset_Handler函数指针指向应用程序的Reset_Handler
  16.        Reset_Handler = (void (*)(void)) appStartAddr;
  17.         // 设置堆栈指针为应用程序的初始堆栈值
  18.         // 这通常是应用程序入口地址的下一个地址
  19.         __set_MSP(*((volatile uint32_t *) appStartAddr + 1));
  20.         // 关闭所有中断
  21.         NVIC->ICER[0] = 0xFFFFFFFF; // 禁用所有中断
  22.         NVIC->ICPR[0] = 0xFFFFFFFF; // 清除所有中断挂起位
  23.         // 跳转到应用程序的Reset_Handler
  24.         Reset_Handler();
  25.     } else {
  26.         // 应用程序入口地址无效,可以在这里处理错误
  27.         while(1) {
  28.             // 错误处理代码
  29.         }
  30.     }
  31. }
复制代码
代码解释:



  • 包罗头文件:包罗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)函数。
  1. void  bootloader_uart_read_data(void)
  2. {
  3.     uint8_t rcv_len=0;
  4.     printmsg_Host("BL_DEBUG_MSG: Receive CMD\n\r");
  5.     while (1)
  6.     {
  7.             HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);
  8.             memset(bl_rx_buffer, 0, 200);
  9.             //here we will read and decode the commands coming from host
  10.             //first read only one byte from the host , which is the "length" field of the command packet
  11.             HAL_UART_Receive(C_UART,bl_rx_buffer,1,HAL_MAX_DELAY);
  12.             rcv_len= bl_rx_buffer[0];
  13.             HAL_UART_Receive(C_UART,&bl_rx_buffer[1],rcv_len,HAL_MAX_DELAY);
  14.             switch(bl_rx_buffer[1])
  15.             {
  16.                     case BL_GET_VER:
  17.                             bootloader_handle_getver_cmd(bl_rx_buffer);
  18.                             break;
  19.             case BL_GET_HELP:
  20.                 bootloader_handle_gethelp_cmd(bl_rx_buffer);
  21.                 break;
  22.             case BL_GET_CID:
  23.                 bootloader_handle_getcid_cmd(bl_rx_buffer);
  24.                 break;
  25.             case BL_GET_RDP_STATUS:
  26.                 bootloader_handle_getrdp_cmd(bl_rx_buffer);
  27.                 break;
  28.             case BL_GO_TO_ADDR:
  29.                 bootloader_handle_go_cmd(bl_rx_buffer);
  30.                 break;
  31.             default:
  32.                     printmsg("BL_DEBUG_MSG:Invalid command code received from host \n");
  33.                     break;
  34.             }
  35.     }
  36. }
复制代码
下面是地址跳转函数,通过过去输入的Buffer进行解析出跳转地址,判断跳转地址是否在精确的跳转范围内,假如在精确的跳转范围内,则向上位机发送可以或许跳转的回复,之后执行地址跳转。假如不在跳转范围内,则向上位机回复,地址不精确,请重新输入。
  1. /*Helper function to handle BL_GO_TO_ADDR command */
  2. void bootloader_handle_go_cmd(uint8_t *pBuffer)
  3. {
  4.     uint32_t go_address=0;
  5.     uint8_t addr_valid = ADDR_VALID;
  6.     uint8_t addr_invalid = ADDR_INVALID;
  7.     printmsg("BL_DEBUG_MSG:bootloader_handle_go_cmd\n");
  8.     //Total length of the command packet
  9.         uint32_t command_packet_len = bl_rx_buffer[0]+1 ;
  10.         //extract the CRC32 sent by the Host
  11.         uint32_t host_crc = *((uint32_t * ) (bl_rx_buffer+command_packet_len - 4) ) ;
  12.         if (! bootloader_verify_crc(&bl_rx_buffer[0],command_packet_len-4,host_crc))
  13.         {
  14.         printmsg("BL_DEBUG_MSG:checksum success !!\n");
  15.         bootloader_send_ack(pBuffer[0],1);
  16.         //extract the go address
  17.         go_address = *((uint32_t *)&pBuffer[2] );
  18.         printmsg("BL_DEBUG_MSG:GO addr: %#x\n",go_address);
  19.         if( verify_address(go_address) == ADDR_VALID )
  20.         {
  21.             //tell host that address is fine
  22.             bootloader_uart_write_data(&addr_valid,1);
  23.             /*jump to "go" address.
  24.             we dont care what is being done there.
  25.             host must ensure that valid code is present over there
  26.             Its not the duty of bootloader. so just trust and jump */
  27.             /* Not doing the below line will result in hardfault exception for ARM cortex M */
  28.             //watch : https://www.youtube.com/watch?v=VX_12SjnNhY
  29.             go_address+=1; //make T bit =1
  30.             void (*lets_jump)(void) = (void *)go_address;
  31.             printmsg("BL_DEBUG_MSG: jumping to go address! \n");
  32.             lets_jump();
  33.                 }else
  34.                 {
  35.             printmsg("BL_DEBUG_MSG:GO addr invalid ! \n");
  36.             //tell host that address is invalid
  37.             bootloader_uart_write_data(&addr_invalid,1);
  38.                 }
  39.         }else
  40.         {
  41.         printmsg("BL_DEBUG_MSG:checksum fail !!\n");
  42.         bootloader_send_nack();
  43.         }
  44. }
复制代码
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企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

我爱普洱茶

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表