STM32 BootLoader 刷新项目 (九) 跳转指定地址-命令0x55
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_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发送给下位机,下位机回复地址是否跳转成功的标志。

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

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脚本,然后在终端输入下位机连接的串口号,即可进入命令界面,目前可支持如下命令:

在上位机中输入命令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
相关文章:
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 修改字段类型但是存在视图依赖
其实视图的存在与否在数据库界一直是一个话题。用好视图可以简化程序的很多代码,用不好视图不仅会给维护带来很多的不便,也会造成很大的性能问题。下面我从维护方面给出案例,以及当存在这种问题的时候,如何去解决这个问题。 假设…...
基于.NET 9实现实时进度条功能:前后端完整示例教程
要在基于.NET 9的应用中实现进度条功能,我们可以通过HttpContext.Response来发送实时的进度更新到前端。以下是一个简单的示例,展示了如何在ASP.NET Core应用中实现这一功能。 但是,我在.net framework4.7.2框架下,实际不了HttpC…...
力扣 LeetCode 19. 删除链表的倒数第N个结点(Day2:链表)
解题思路: 快慢指针 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实现声纹识别功能,调用科大讯飞接口: 声纹识别 API 文档 | 讯飞开放平台文档中心 其接口要求音频文件格式为mp3 二、问题产生 在安卓端根据官方文档说明,系统并不支持直接录制mp3格式音频,支持格式如…...
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 框架中广泛运用了多种设计模式,今天让我们来学习一下 1. 单例模式(Singleton Pattern) 用途:在Spring框架中,Bean默认是单例的,也就是说在容器中每种类型的Bean只有一个实例。这个设计可以节省资源…...
提升百度排名的有效策略与技巧解析
内容概要 提升百度排名对于网站的成功至关重要。首先,了解百度排名的基本原则,掌握搜索引擎是如何评估网页质量的,是优化过程中不可或缺的一部分。搜索引擎越来越倾向于将用户需求放在首位,因此提供高质量的内容和良好的用户体验…...
【Linux】Linux下查看cpu信息指令(top/mpstat/iostat/pidstat)说明
top命令 top(1) - Linux manual page (man7.org) top查看总的CPU利用率 us: 用户空间消耗的CPU资源占比,进程在用户态执行函数调用,编解码消耗的都是us sy: 内核空间消耗的CPU资源占比,进程调用系统调用达到内核后会增加sy的消耗 ni&…...
HDLBIts习题(3):使用冒号表示位宽时,冒号两端必须是常量
(1)易错习题1:Circuits - Combinational Logic - Multiplexers - 256-to-1 4bit multiplexer 使用冒号表示位宽时,冒号两端必须是常量,因此如果使用变量,可以使用位拼接的方法。 (2)…...
C++20协程详解
文章目录 什么是协程为什么需要协程什么时候使用协程协程的类别C20的协程协程的使用关键字co_wait框架一阶段完成数据交换co_yieldco_return 什么是协程 我们在学习编程的过程中,逐渐从单线程,到多线程,再到异步编程和并发处理 这些异步与并…...
Chromium 中chrome.system.display扩展接口定义c++
一、chrome.system.display 使用 system.display API 查询展示元数据。 权限 system.display 类型 ActiveState Chrome 117 及更高版本 用于指示系统是否检测到和使用显示屏的枚举。如果系统未检测到显示屏(可能断开连接,或因睡眠模式等原因而被视…...
容器docker的ulimit
Ulimit 在linux里ulimit命令可以对shell生成的进程的资源进行限制。 常用的ulimit限制 打开文件句柄数core文件大小设置进程能够消耗的虚拟内存设置用户能够打开的进程数目 不太常用的ulimit限制 设置数据段的最大值.单位:kbytes 设置创建文件的最大值.单位:blocks 设置在…...
一、HTML
一、基础概念 1、浏览器相关知识 这五个浏览器市场份额都非常大,且都有自己的内核。 什么是内核: 内核是浏览器的核心,用于处理浏览器所得到的各种资源。 例如,服务器发送图片、视频、音频的资源,浏览…...
使用Geekbench6软件对真实和虚拟的苹果桌面系统(macOS)进行打分比较
前言 感觉VMWare安装的MacOS使用起来非常的慢,所以特意用打分软件GeekBench进行了评测。 一、Geekbench的安装 可以从官网直接进行下载, 链接是: 二、Geekbench的直接使用 2.1、真机的信息 2.2、虚拟机的信息 三、打分的比较 3.1、真机…...
lua入门教程:随机数
在Lua中,生成随机数是通过math库中的math.random函数来实现的。这个函数可以生成一个[0, 1)区间内的随机浮点数。如果你需要生成其他范围内的随机数,或者需要整数类型的随机数,可以通过一些简单的数学运算来调整math.random的输出。 以下是如…...
华为大咖说 | 浅谈智能运维技术
本文分享自华为云社区:华为大咖说 | 浅谈智能运维技术-云社区-华为云 本文作者:李文轩 ( 华为智能运维专家 ) 全文约2695字,阅读约需8分钟 在大数据、人工智能等新兴技术的加持下,智能运维(AI…...
深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...
【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...
【Go语言基础【12】】指针:声明、取地址、解引用
文章目录 零、概述:指针 vs. 引用(类比其他语言)一、指针基础概念二、指针声明与初始化三、指针操作符1. &:取地址(拿到内存地址)2. *:解引用(拿到值) 四、空指针&am…...
