当前位置: 首页 > news >正文

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

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

前面我们讲述了几种BootLoader中的命令,包括获取软件版本号、获取帮助、获取芯片ID、读取Flash保护Level。

下面我们来介绍一下BootLoader中最重要的功能之一—跳转!就像BootLoader词汇中的Boot一词一样,就是启动跳转。

首先我们来介绍一下Boot跳转的应用。

1. BootLoader跳转指定地址的应用

STM32的BootLoader中跳转指定地址的应用场景主要包括以下几个方面:

  1. 固件升级(Firmware Upgrade)

    • BootLoader允许在不改变硬件连接的情况下进行固件的在线升级。当新的固件版本需要部署到设备上时,BootLoader可以接收新的固件并将其烧录到指定的Flash地址,然后跳转到新固件的执行地址,从而实现固件的无缝更新。
  2. 多应用管理(Multi-Application Management)

    • 在一些复杂的应用中,可能需要在同一个设备上运行多个应用程序。BootLoader可以通过跳转到不同的地址来选择性地加载和执行不同的应用程序,实现多应用的管理。
  3. 系统恢复(System Recovery)

    • 如果设备在运行过程中出现软件故障,BootLoader可以作为一个恢复点,通过跳转到备份的固件地址来恢复系统的正常运行。
  4. 安全启动(Secure Boot)

    • 在安全敏感的应用中,BootLoader可以检查固件的合法性,确保只有经过认证的固件才能被加载执行。这可以通过跳转到经过签名验证的固件地址来实现。
  5. 调试和测试(Debugging and Testing)

    • 在开发和测试阶段,BootLoader可以方便地进行程序的调试和测试。开发者可以通过BootLoader跳转到不同的测试固件地址,快速验证新功能或修复bug。
  6. 节省资源(Resource Saving)

    • 对于资源受限的嵌入式系统,BootLoader可以减少对外部存储器的需求,通过内部Flash存储固件,节省成本和空间。
  7. 产品差异化(Product Differentiation)

    • 通过BootLoader,制造商可以为不同的市场或客户定制不同的固件版本,通过跳转到不同的固件地址来实现产品的差异化。
  8. 远程维护(Remote Maintenance)

    • BootLoader支持远程固件更新,使得设备的维护和升级可以在不接触硬件的情况下完成,这对于远程或难以接触的设备尤为重要。

这些应用场景展示了BootLoader在STM32微控制器中的灵活性和重要性,它们使得设备能够更加智能、安全和易于维护。

2. 函数跳转的方法

在STM32的BootLoader中跳转到指定地址通常涉及到以下几个步骤:

  1. 验证目标地址:确保目标地址是有效的,并且位于应用程序的合法执行区域内。
  2. 设置堆栈指针:将堆栈指针(MSP)设置为应用程序的初始堆栈值。
  3. 跳转到应用程序:使用函数指针或者直接修改程序计数器(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_HandlerReset_Handler = (void (*)(void)) appStartAddr;// 设置堆栈指针为应用程序的初始堆栈值// 这通常是应用程序入口地址的下一个地址__set_MSP(*((volatile uint32_t *) appStartAddr + 1));// 关闭所有中断NVIC->ICER[0] = 0xFFFFFFFF; // 禁用所有中断NVIC->ICPR[0] = 0xFFFFFFFF; // 清除所有中断挂起位// 跳转到应用程序的Reset_HandlerReset_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发送给下位机,下位机回复地址是否跳转成功的标志。

image-20241108071213198

下面是整个程序执行地址跳转的流程图:

BootLoader_Flow_Go_address

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 packetHAL_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 packetuint32_t command_packet_len = bl_rx_buffer[0]+1 ;//extract the CRC32 sent by the Hostuint32_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 addressgo_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 finebootloader_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 thereIts 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_12SjnNhYgo_address+=1; //make T bit =1void (*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 invalidbootloader_uart_write_data(&addr_invalid,1);}}else{printmsg("BL_DEBUG_MSG:checksum fail !!\n");bootloader_send_nack();}
}

5. 实战演练

下面是上位机的命令菜单,通过在终端调用Python脚本,然后在终端输入下位机连接的串口号,即可进入命令界面,目前可支持如下命令:

image-20240713104433991

在上位机中输入命令5,即为在BootLoader中执行地址跳转命令0x55,MCU根据读取跳转地址0xXXXXXXXX的值,来进行跳转,并告诉上位机能否跳转成功,由下图可以看出,当前地址跳转成功。

image-20241108080343013

目前我在STM32中刷了两段程序,其中BootLoader程序即为0x0800 0000-0x0800 7FFF处,后面0x08000 8000-0x080F FFFF为APP应用程序,则当我将跳转地址设为0x0800 8000时,MCU则会从BootLoader跳转进入到APP应用程序中。

image-20240621072216733

但为什么我们在输入跳转指令地址为0x08008000的时候,并没有跳转到App程序呢,下面我通过Hex和大家解释一下。

下面我们用STM32官方提供的工具STM32CubeProgrammer工具读取MCU中的Hex信息。

Note:必须在连接ST-Link的时候才能显示Hex信息。

image-20241108075809602

由上图我们可以看出在0x08008000地址开始处,其中第一个地址0x0800 8000地址中的值时0x20020000,这个值时Reset Handle,即中断向量表的起始地址。第二个地址0x0800 8004中的值才是实际App程序所在的Flash地址。

所以在下图中,我们执行地址跳转指令0x55,跳转地址设为0x0800 8B16,为什么比0x0800 8B15多1呢,这是因为我们使用的Thumb指令,执行的指令结尾必须+1.

image-20241108080707845

下面我们通过串口监控一下Mcu是否跳转成功,由下图可以看出,MCU成功跳转到App程序中。

image-20241108081055946

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

相关文章:

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

STM32 BootLoader 刷新项目 (九) 跳转指定地址-命令0x55 前面我们讲述了几种BootLoader中的命令,包括获取软件版本号、获取帮助、获取芯片ID、读取Flash保护Level。 下面我们来介绍一下BootLoader中最重要的功能之一—跳转!就像BootLoader词汇中的Boot…...

【Linux篇】面试——用户和组、文件类型、权限、进程

目录 一、权限管理 1. 用户和组 (1)相关概念 (2)用户命令 ① useradd(添加新的用户账号) ② userdel(删除帐号) ③ usermod(修改帐号) ④ passwd&…...

PET-文件包含

include发生错误报warning,继续执行。require发生错误直接error,不继续执行 无视扩展名,只要能解析,就能当可执行文件执行,哪怕文件后缀或没后缀 1 条件竞争 pass17 只需要知道tmp的路径。把xieshell.jpg上传&…...

实现uniapp-微信小程序 搜索框+上拉加载+下拉刷新

pages.json 中的配置 { "path": "pages/message", "style": { "navigationBarTitleText": "消息", "enablePullDownRefresh": true, "onReachBottomDistance": 50 } }, <template><view class…...

PostgreSQL 修改字段类型但是存在视图依赖

其实视图的存在与否在数据库界一直是一个话题。用好视图可以简化程序的很多代码&#xff0c;用不好视图不仅会给维护带来很多的不便&#xff0c;也会造成很大的性能问题。下面我从维护方面给出案例&#xff0c;以及当存在这种问题的时候&#xff0c;如何去解决这个问题。 假设…...

基于.NET 9实现实时进度条功能:前后端完整示例教程

要在基于.NET 9的应用中实现进度条功能&#xff0c;我们可以通过HttpContext.Response来发送实时的进度更新到前端。以下是一个简单的示例&#xff0c;展示了如何在ASP.NET Core应用中实现这一功能。 但是&#xff0c;我在.net framework4.7.2框架下&#xff0c;实际不了HttpC…...

力扣 LeetCode 19. 删除链表的倒数第N个结点(Day2:链表)

解题思路&#xff1a; 快慢指针 class Solution {public ListNode removeNthFromEnd(ListNode head, int n) {ListNode dummy new ListNode(-1);dummy.next head;ListNode fast dummy;ListNode slow dummy;for (int i 0; i < n; i) {fast fast.next;}while (fast.ne…...

音频格式转换

一、场景 项目需求需要App实现声纹识别功能&#xff0c;调用科大讯飞接口&#xff1a; 声纹识别 API 文档 | 讯飞开放平台文档中心 其接口要求音频文件格式为mp3 二、问题产生 在安卓端根据官方文档说明&#xff0c;系统并不支持直接录制mp3格式音频&#xff0c;支持格式如…...

npm list @types/node 命令用于列出当前项目中 @types/node 包及其依赖关系

文章目录 作用示例常用选项示例命令注意事项 1、实战举例**解决方法**1. **锁定唯一的 types/node 版本**2. **清理依赖并重新安装**3. **设置 tsconfig.json 的 types**4. **验证 Promise 类型支持** **总结** npm list types/node 命令用于列出当前项目中 types/node 包及其…...

【Spring】Spring框架中有有哪些常见的设计模式

Spring 框架中广泛运用了多种设计模式&#xff0c;今天让我们来学习一下 1. 单例模式&#xff08;Singleton Pattern&#xff09; 用途&#xff1a;在Spring框架中&#xff0c;Bean默认是单例的&#xff0c;也就是说在容器中每种类型的Bean只有一个实例。这个设计可以节省资源…...

提升百度排名的有效策略与技巧解析

内容概要 提升百度排名对于网站的成功至关重要。首先&#xff0c;了解百度排名的基本原则&#xff0c;掌握搜索引擎是如何评估网页质量的&#xff0c;是优化过程中不可或缺的一部分。搜索引擎越来越倾向于将用户需求放在首位&#xff0c;因此提供高质量的内容和良好的用户体验…...

【Linux】Linux下查看cpu信息指令(top/mpstat/iostat/pidstat)说明

top命令 top(1) - Linux manual page (man7.org) top查看总的CPU利用率 us: 用户空间消耗的CPU资源占比&#xff0c;进程在用户态执行函数调用&#xff0c;编解码消耗的都是us sy: 内核空间消耗的CPU资源占比&#xff0c;进程调用系统调用达到内核后会增加sy的消耗 ni&…...

HDLBIts习题(3):使用冒号表示位宽时,冒号两端必须是常量

&#xff08;1&#xff09;易错习题1&#xff1a;Circuits - Combinational Logic - Multiplexers - 256-to-1 4bit multiplexer 使用冒号表示位宽时&#xff0c;冒号两端必须是常量&#xff0c;因此如果使用变量&#xff0c;可以使用位拼接的方法。 &#xff08;2&#xff09;…...

C++20协程详解

文章目录 什么是协程为什么需要协程什么时候使用协程协程的类别C20的协程协程的使用关键字co_wait框架一阶段完成数据交换co_yieldco_return 什么是协程 我们在学习编程的过程中&#xff0c;逐渐从单线程&#xff0c;到多线程&#xff0c;再到异步编程和并发处理 这些异步与并…...

Chromium 中chrome.system.display扩展接口定义c++

一、chrome.system.display 使用 system.display API 查询展示元数据。 权限 system.display 类型 ActiveState Chrome 117 及更高版本 用于指示系统是否检测到和使用显示屏的枚举。如果系统未检测到显示屏&#xff08;可能断开连接&#xff0c;或因睡眠模式等原因而被视…...

容器docker的ulimit

Ulimit 在linux里ulimit命令可以对shell生成的进程的资源进行限制。 常用的ulimit限制 打开文件句柄数core文件大小设置进程能够消耗的虚拟内存设置用户能够打开的进程数目 不太常用的ulimit限制 设置数据段的最大值.单位:kbytes 设置创建文件的最大值.单位:blocks 设置在…...

一、HTML

一、基础概念 1、浏览器相关知识 这五个浏览器市场份额都非常大&#xff0c;且都有自己的内核。 什么是内核&#xff1a; 内核是浏览器的核心&#xff0c;用于处理浏览器所得到的各种资源。 例如&#xff0c;服务器发送图片、视频、音频的资源&#xff0c;浏览…...

使用Geekbench6软件对真实和虚拟的苹果桌面系统(macOS)进行打分比较

前言 感觉VMWare安装的MacOS使用起来非常的慢&#xff0c;所以特意用打分软件GeekBench进行了评测。 一、Geekbench的安装 可以从官网直接进行下载&#xff0c; 链接是&#xff1a; 二、Geekbench的直接使用 2.1、真机的信息 2.2、虚拟机的信息 三、打分的比较 3.1、真机…...

lua入门教程:随机数

在Lua中&#xff0c;生成随机数是通过math库中的math.random函数来实现的。这个函数可以生成一个[0, 1)区间内的随机浮点数。如果你需要生成其他范围内的随机数&#xff0c;或者需要整数类型的随机数&#xff0c;可以通过一些简单的数学运算来调整math.random的输出。 以下是如…...

华为大咖说 | 浅谈智能运维技术

本文分享自华为云社区&#xff1a;华为大咖说 | 浅谈智能运维技术-云社区-华为云 本文作者&#xff1a;李文轩 &#xff08; 华为智能运维专家 &#xff09; 全文约2695字&#xff0c;阅读约需8分钟 在大数据、人工智能等新兴技术的加持下&#xff0c;智能运维&#xff08;AI…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

生成xcframework

打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式&#xff0c;可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

C++_核心编程_多态案例二-制作饮品

#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为&#xff1a;煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例&#xff0c;提供抽象制作饮品基类&#xff0c;提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?

Golang 面试经典题&#xff1a;map 的 key 可以是什么类型&#xff1f;哪些不可以&#xff1f; 在 Golang 的面试中&#xff0c;map 类型的使用是一个常见的考点&#xff0c;其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

Java + Spring Boot + Mybatis 实现批量插入

在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法&#xff1a;使用 MyBatis 的 <foreach> 标签和批处理模式&#xff08;ExecutorType.BATCH&#xff09;。 方法一&#xff1a;使用 XML 的 <foreach> 标签&#xff…...

day36-多路IO复用

一、基本概念 &#xff08;服务器多客户端模型&#xff09; 定义&#xff1a;单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用&#xff1a;应用程序通常需要处理来自多条事件流中的事件&#xff0c;比如我现在用的电脑&#xff0c;需要同时处理键盘鼠标…...

解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist

现象&#xff1a; android studio报错&#xff1a; [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决&#xff1a; 不要动CMakeLists.…...

Python 训练营打卡 Day 47

注意力热力图可视化 在day 46代码的基础上&#xff0c;对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...