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

news2024/11/8 10:24:22

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_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发送给下位机,下位机回复地址是否跳转成功的标志。

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2235747.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VTK知识学习(2)-环境搭建

1、c方案 1.1下载源码编译 官网获取源码。 利用Cmake进行项目构建。 里面要根据实际使用的情况配置相关的模块哟,这个得你自行研究下了。 CMAKEINSTALLPREFIX--这个选项的值表示VTK的安装路径,默认的路径是C:/Program Files/VTK。该选项的值可不作更…

Chrome(谷歌浏览器中文版)下载安装(Windows 11)

目录 Chrome_10_30工具下载安装 Chrome_10_30 工具 系统:Windows 11 下载 官网:https://chrome.google-zh.com/,点击立即下载 下载完成(已经下过一遍所以点了取消) 安装 解压,打开安装包 点击下一步…

如何在算家云搭建Aatrox-Bert-VITS2(音频生成)

一、模型介绍 ‌ Aatrox - Bert -VITS2 模型是一种基于深度学习的语音合成系统,结合了 BERT 的预训练能力和 VITS2 的微调技术,旨在实现高质量的个性化语音合成。 二、模型搭建流程 1. 创建容器实例 进入算家云的“应用社区”,点击搜索找到…

232转485模块测试

概述 常用的PLC一般会有两个左右的232口,以及两个左右的485口,CAN口等,但是PLC一般控制的设备可能会有很多,会超出通讯口的数量,此时我们一般会采用一个口接多个设备,这种情况下要注意干扰等因素&#xff0…

静态数组类型无法用“=“给整个静态数组赋值

基础知识: 什么是静态数组类型? 在 C 中,静态数组是一种在编译时大小固定的数组。这意味着一旦声明,静态数组的大小就不能改变。 ------ 你可以声明一个静态数组并选择性地初始化它: int arr[10]; // 声明一个包…

使用 Python 调用云 API 实现批量共享自定义镜像

本文介绍如何通过 Python SDK 调用 API 接口,通过子用户批量共享云服务器自定义镜像。若您具备类似需求,或想了解如何使用 SDK,可参考本文进行操作。 前提条件 已创建子用户,并已具备云服务器及云 API 所有权限。 创建子用户请…

【旷视科技-注册/登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击,存在如下安全问题: 暴力破解密码,造成用户信息泄露短信盗刷的安全问题,影响业务及导致用户投诉带来经济损失,尤其是后付费客户,风险巨大,造成亏损无底洞…

GISBox VS ArcGIS:分别适用于大型和小型项目的两款GIS软件

在现代地理信息系统(GIS)领域,有许多大家耳熟能详的GIS软件。它们各自具有独特的优势,适用于不同的行业需求和使用场景。在众多企业和开发者面前,如何选择合适的 GIS 软件成为了一个值得深入思考的问题。今天&#xff…

【Spring】Spring Web MVC基础入门~(含大量例子)

阿华代码,不是逆风,就是我疯 你们的点赞收藏是我前进最大的动力!! 希望本文内容能够帮助到你!! 目录 一:什么是Spring Web MVC 1:Servlet 2:总结 二:MVC …

数组类算法【leetcode】

704. 二分查找 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。 二分查找 用于有序数组中,没有重复的数组。…

24/11/7 算法笔记 PCA主成分分析

假如我们的数据集是n维的,共有m个数据(x,x,...,x)。我们希望将这m个数据的维度从n维降到k维,希望这m个k维的数据集尽可能的代表原始数据集。我们知道数据从n维降到k维肯定会有损失,但是我们希望损失尽可能的小。那么如何让这k维的数据尽可能表…

wxWidgets布局管理及XRC界面使用解惑

wxWidgets布局,通常就是wxBoxSizer,当然还有别的Sizer,就像Qt中的Layout一样,各种布局管理类。只是如今的wxWidgets尚未发展起来,一般其他的你用的比较少,因为这个最简单,最容易布局&#xff0c…

【开源免费】基于SpringBoot+Vue.JS医院管理系统(JAVA毕业设计)

博主说明:本文项目编号 T 062 ,文末自助获取源码 \color{red}{T062,文末自助获取源码} T062,文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析…

【循环引用及格式化输出】

垃圾回收机制 当一个值在内存中直接引用跟间接引用的量为0时,(即这个值没有任何入口可以找到它)那么这个值就会被清空回收♻️,释放内存空间; 列表在内存中的存储方式 1)引用计数的两种方式 x "ea…

31.7K+ Star!AgentGPT:一个在浏览器中运行的Agent

AgentGPT 简介 AgentGPT[1] 是一个可以让你在浏览器中组装、配置和部署自主AI代理的项目。你可以为你的自定义AI命名,并让它去实现任何你想象中目标。它将尝试通过思考要执行的任务、执行它们并从结果中学习来达成目标。 项目特点 主要特点 自主AI代理:用户可以自定义AI并赋…

【大模型】通过Crew AI 公司的崛起之路学习 AI Agents 的用法

AI 技术的迅猛发展正以前所未有的速度重塑商业格局,而 AI Agents,作为新一代的智能自动化工具,正逐步成为创新型公司的核心力量。在本文中,我们将探讨如何利用 AI Agents 构建一家 AI 驱动的公司,并详细了解 Crew AI 创…

【Uniapp】Uniapp Android原生插件开发指北

前言 在uniapp开发中当HBuilderX中提供的能力无法满足App功能需求,需要通过使用Andorid/iOS原生开发实现时,或者是第三方公司提供的是Android的库,这时候可使用App离线SDK开发原生插件来扩展原生能力。 插件类型有两种,Module模…

网页版五子棋——用户模块(服务器开发)

前一篇文章:网页版五子棋—— WebSocket 协议-CSDN博客 目录 前言 一、编写数据库代码 1.数据库设计 2.配置 MyBatis 3.创建实体类 4.创建 UserMapper 二、前后端交互接口 1.登录接口 2.注册接口 3.获取用户信息 三、服务器开发 1.代码编写 2.测试后端…

Jenkins声明式Pipeline流水线语法示例

系列文章目录 docker搭建Jenkins2.346.3版本及常用工具集成配置(ldap、maven、ansible、npm等) docker安装低版本的jenkins-2.346.3,在线安装对应版本插件失败的解决方法 文章目录 系列文章目录jenkins流水线基础1、pipeline1.1、什么是pipeline?1.2、为什么使用pi…

【NLP】使用 SpaCy、ollama 创建用于命名实体识别的合成数据集

命名实体识别 (NER) 是自然语言处理 (NLP) 中的一项重要任务,用于自动识别和分类文本中的实体,例如人物、位置、组织等。尽管它很重要,但手动注释大型数据集以进行 NER 既耗时又费钱。受本文 ( https://huggingface.co/blog/synthetic-data-s…