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

【OTA专题】3.实现简单的boot和APP程序逻辑

目录

目标:

1.实现Boot跳转App过程

2.学习代码跳转原理

实现步骤:

第一步:代码分区

第二步:跳转代码

Boot:

APP:

拓展:

1.这里为什么要屏蔽中断后重新设置向量表?

1. __disable_irq():保证跳转过程的 “原子性”

2. NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x8000):保证 APP 中断响应的 “正确性”

2.bootloader size比较大怎么办

可能原因

应对方法

(1)精简功能

(2)分层设计

(3)优化体积

(4)调整内存分区

3.为什么跳转前后用串口可以打印,RTT打印不了?


目标:

1.实现Boot跳转App过程

2.学习代码跳转原理

实现步骤:

第一步:代码分区

前期就需要规定好Bootloader的资源占用空间。

Stm32F411Ceu6(512K flash)

分配:

  Boot(分配32K)

  APP(暂停其余Flash区域均为App)

Stm32F411Flash资源分配:

第二步:跳转代码

参考文章:

STM32单片机实现Bootloader跳转的关键步骤:zhuanlan.zhihu.com

Boot:

Boot_Manager.c

#include "Boot_Manager.h"
#include "main.h"void jump_to_app(void)
{uint32_t JumpAddress;pFunction Jump_To_Application;/* 检查栈顶地址是否合法 */if(((*(__IO uint32_t *)APP_FLASH_ADDR) & 0x2FFE0000) == 0x20000000){/* 屏蔽所有中断,防止在跳转过程中,中断干扰出现异常 */__disable_irq();NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x10000);RCC_DeInit();/* 用户代码区第二个 字 为程序开始地址(复位地址) */JumpAddress = *(__IO uint32_t *) (APP_FLASH_ADDR + 4);/* Initialize user application's Stack Pointer *//* 初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址) */__set_MSP(*(__IO uint32_t *) APP_FLASH_ADDR);/* 类型转换 */Jump_To_Application = (pFunction) JumpAddress;/* 跳转到 APP */Jump_To_Application();}
}

Boot_Manager.h

#include "Boot_Manager.h"
#include "main.h"void jump_to_app(void)
{uint32_t JumpAddress;pFunction Jump_To_Application;/* 检查栈顶地址是否合法 */if(((*(__IO uint32_t *)APP_FLASH_ADDR) & 0x2FFE0000) == 0x20000000){/* 屏蔽所有中断,防止在跳转过程中,中断干扰出现异常 */__disable_irq();NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x8000);RCC_DeInit();/* 用户代码区第二个 字 为程序开始地址(复位地址) */JumpAddress = *(__IO uint32_t *) (APP_FLASH_ADDR + 4);/* Initialize user application's Stack Pointer *//* 初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址) */__set_MSP(*(__IO uint32_t *) APP_FLASH_ADDR);/* 类型转换 */Jump_To_Application = (pFunction) JumpAddress;/* 跳转到 APP */Jump_To_Application();}
}

main.c

要恢复定时器默认中断,不然会卡在定时器相关中断里;

APP:

使用cubemx生成:

生成工程后,编译,LED被成功点亮

然后在keil中设置app的起始地址

main中进行相关的地址偏移

拓展:

1.这里为什么要屏蔽中断后重新设置向量表?

1. __disable_irq():保证跳转过程的 “原子性”

  • 中断的本质是 “异步事件”,若跳转过程中触发中断,CPU 会去执行当前向量表中注册的中断服务函数(此时还是 Bootloader 的中断函数)。

1. 中断服务函数(ISR)依赖 “当前程序的运行环境”

中断服务函数(比如定时器中断、串口中断的处理函数)不是 “独立存在” 的,它需要依赖当前程序的 “运行上下文”,包括:

  • 全局变量与数据结构:ISR 可能操作 Bootloader 自己的全局变量(如 “接收缓冲区”“状态标志”)。

  • 栈空间:ISR 执行时,会把 CPU 寄存器压入当前程序的栈(Bootloader 有自己的栈,APP 也有自己的栈)。

  • 外设配置:ISR 可能基于 Bootloader 对硬件的配置(如 UART 波特率、定时器分频系数)执行逻辑。

2. 跳转时若被中断打断,会出现 “环境不兼容”

当 Bootloader 执行Jump_To_Application()跳转时,APP 的运行环境已经开始初始化(比如栈指针已切换为 APP 的栈顶、内存布局已变为 APP 的全局变量),但中断向量表还没完全切换到 APP 的(或 APP 的中断逻辑还没初始化)。此时如果发生中断:

  • CPU 会根据Bootloader中断向量表,执行Bootloader 的 ISR

  • 但 Bootloader 的 ISR 是为 “Bootloader 的运行环境” 设计的:

    • 它会操作Bootloader 的全局变量,但这些变量的地址可能已被 APP 的变量覆盖,导致数据混乱;

    • 它会使用APP 的栈(因为栈指针已被__set_MSP切换),但 Bootloader 的 ISR 对栈的操作逻辑(如局部变量大小、压栈深度)和 APP 的栈不匹配,可能导致栈溢出、破坏 APP 的栈数据;

    • 它会按照Bootloader 的外设配置操作硬件,但 APP 可能需要不同的外设配置,导致硬件行为异常。

举个直观的例子

假设:

  • Bootloader 用 UART1 接收 “固件升级数据”,中断服务函数会把数据存到bootloader_rx_buf(地址0x20001000)。

  • 跳转到 APP 后,APP 用 UART1 接收 “用户指令”,数据要存到app_rx_buf(地址0x20002000),且已将 UART1 波特率改为另一值。

若跳转时 UART1 触发中断:

  • CPU 执行Bootloader 的 UART ISR,它会往bootloader_rx_buf0x20001000)写数据,但此时0x20001000可能已经是 APP 的某个变量,数据被错误覆盖;

  • 同时,ISR 基于 Bootloader 的 UART 波特率处理数据,而实际硬件波特率已被 APP 修改,导致数据解析错误。

  • 因此,__disable_irq()屏蔽所有全局中断,确保跳转过程 “一气呵成”,不会被任何异步事件干扰。

2. NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x8000):保证 APP 中断响应的 “正确性”

  • STM32 的中断向量表默认位于Flash 起始地址(如0x08000000),但如果 APP 不是从 Flash 起始地址启动(比如代码中 APP 的起始地址是APP_FLASH_ADDR,这里通过0x8000偏移指定 APP 的向量表位置),则需要重新配置向量表的基地址

  • 该函数的作用是告诉 CPU:“后续中断发生时,去Flash基地址 + 0x8000这个位置找中断向量表”(也就是 APP 自己的向量表)。

  • 若不重新设置,CPU 会继续使用 Bootloader 的向量表,导致 APP 的中断(如定时器中断、串口中断)无法找到正确的服务函数,进而引发异常。

这两步操作是 Bootloader 向 APP “交接控制权” 的关键保障:

  • 屏蔽中断 → 确保跳转过程不被干扰

  • 重设向量表 → 确保APP 运行后中断能正确响应

2.bootloader size比较大怎么办

可能原因

Bootloader 本质上就是负责 上电初始化+应用加载+升级机制。理论上它应该非常小(几KB到几十KB)。如果bootloader过大,通常有这些原因:

■ 功能加太多(UI、协议栈、复杂的文件系统等被塞进bootloader)。

■ 没有区分清楚 bootloader和application 的边界。

■ 链接脚本/工程配置不合理,把一些本该属于应用的代码/库链接进bootloader了。

■ 没有优化(比如没开编译优化、没裁剪库函数)。

应对方法

(1)精简功能
  • 只保留 必须的功能:时钟初始化、Flash/IAP、通信接口(UART、USB、CAN、BLE等)。

  • 把升级后的协议解析、UI、业务逻辑 放到application,不要留在bootloader。

(2)分层设计
  • Bootloader 只实现 最小升级能力,例如UART收发bin文件。

  • 如果需要更复杂的升级协议(如OTA、加密校验),可以采用 两级 Bootloader:

    • Stage 1(Primary Bootloader):很小,放在固定地址(几十KB)

    • Stage 2(Secondary Bootloader):放在Flash应用区的一部分,负责复杂升级。

(3)优化体积
  • 打开 -Os 或-02 编译优化。

  • 使用printf 精简版(比如 iprintf 或自己实现 minimal log)。

  • 去掉没用的库(特别是标准库、浮点printf)。

  • 若协议栈太大(如BLE、USB Host),考虑放在application。

(4)调整内存分区

如果 bootloader 功能确实需要比较大:

  • 在链接脚本中扩大 bootloader 占用区,比如从默认的16KB改到32KB、64KB。

  • 只要应用程序的起始地址(App Flash 起始地址)跟bootloader 大小匹配即可。

3.为什么跳转前后用串口可以打印,RTT打印不了?

因为RTT它需要和Jlink上位机RTT_View进行握手(有一块缓冲区),跳转后会清除这块buffer丢失心跳,从而无法

继续打印,一般我们也没有这样的业务场景,这里不需要追求rtt在Bootloader和app的打印,用串口打印即可。

http://www.dtcms.com/a/435266.html

相关文章:

  • 营销单页模板网站怎么买网站域名
  • 织梦做商城网站wordpress 自建邮件
  • 小江网站建设必须重视的问题之一
  • Bella Beauty WordPress Theme — Aesthetic Medical Clinic
  • Java_钻石操作符详解
  • 网站做qq微信微博登录爱做的小说网站
  • 大文件推送到git仓库
  • 对招聘网站页面设计做建议wordpress主题cute
  • Spring cloud快速入门
  • STM32 智能垃圾桶项目笔记(五):语音合成模块(SYN6288)配置与语音播报实现
  • 移动互联网开发应聘四川网站营销seo费用
  • 找北京赛车网站开发wordpress 自定义页面
  • MATLAB信号处理实用指南:从入门到精通
  • 成都住建局官网报名入口网址兴安盟seo
  • 中国建设银行手机网站下载安装托管的服务器如何做网站
  • P13977题解
  • 网络推广岗位职责和任职要求成都做整站优化
  • DAY 38 Dataset和Dataloader类 - 2025.10. 2
  • Privacy Eraser(隐私保护软件)多语便携版
  • C4D R20新增功能概述及体积对象SDF类型深度解析
  • 上海做网站公司推荐简单网上书店网站建设php
  • HarmonyOS应用开发深度解析:ArkTS语法精要与UI组件实践
  • 北京示范校建设网站wordpress快速发布
  • 常用网站布局土巴兔这种网站怎么做
  • toLua[四] Examples 03_CallLuaFunction分析
  • 建设景区网站推文企业网站排名怎么优化
  • 汽车信息安全测试与ISO/SAE 21434标准
  • Hadoop HA 集群安装配置
  • 10.2总结
  • 旅游网站建设最重要的流程如何制作公众号教程