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

ARM单片机OTA解析(一)

文章目录

  • 一、单片机烧写程序的几种方法
  • 二、Bootloader如何加载启动App


一、单片机烧写程序的几种方法

![[Pasted image 20250704203159.png]]

在线应用编程,由开发者实现Bootloader功能,比如ARM单片机的Code分区中的Flash本是存储用户应用程序的区间(上电从此处执行用户代码),开发者可以将自己实现的Bootloader和应用程序都存放到Flash区间MCU上电启动先执行用户的Bootloader代码,该代码可为用户应用程序的下载、校验、升级、启动等提供支持,进而实现OTA远程升级功能。

一般用于给用户远程升级,或者是烧写程序不方便的时候。

![[Pasted image 20250709184234.png]]

复位以后进入Bootloader程序,

![[Pasted image 20250709195510.png]]

需要说明的是这个地方就是我们程序烧写的其实地址,

这是因为我们要把前面的空间流出来给BOOT程序。

ARM单片机启动流程(一)(详细解析)-CSDN博客 在本人的这篇文章里面可以看到我们的Main Flash的物理其实地址就是0x08000000,因此我需要把BOOT程序烧写到这里,因为CPU执行程序最先
CPU都是从0地址开始访问的,根据被引导到的地方,有可能直接跳转到Main Flash 0x0800 0000的原始存储空间;也有可能跳转到MCU厂商预置的bootloader开始于0x1FFF F000的原始存储空间。至于怎么跳转这两个以及默认是什么,连接文章在这一块进行了详细分析。

一般我们默认就是下面顺序:我们就是直接跳转到Main Flash 0x0800 0000的原始存储空间;
![[Pasted image 20250709200438.png]]

(CPU通过程序计数器(PC)获取下一条指令的地址。在无分支的情况下,PC自动增加(如 PC_new = PC_old + 指令长度),实现物理地址的顺序执行)

int main(void)
{	InitIrqAfterBoot();DrvInit();AppInit();while (1){TaskHandler();}
}void InitIrqAfterBoot(void)
{nvic_vector_table_set(NVIC_VECTTAB_FLASH, 0x3000);__enable_irq();
}#define NVIC_VECTTAB_FLASH          ((uint32_t)0x08000000) /*!< Flash base address */

值得注意的是在APP程序里面,需要配置中断向量表里面的初始地址,这是因为原本我们默认的程序初始地址是0x08000000,但是由于BOOT程序的作用导致我们的初始地址变成了((uint32_t)0x08000000)+偏移地址0x3000,中断向量表的初始位置。

如果我们常规的程序,是CPU直接加载的,就不需要我们初始化,就是内核会默认帮我们搞好,从0x08000000开始。

但是我们现在的程序是从BOOT里面启动的,那就意味着我们需要自己配置一个中断向量表初始地址,所以我们需要在应用程序里面做一些CPU需要做的事情,并且在BOOT程序里面一样需要做一些CPU需要做的事情,说白了就是手动干一些CPU的事情。

同样需要烂熟于心的还有0x3000表示12KB,0x1000表示4KB,也就是4096个字节(Byte)。

另外需要一点注意的:

        .property = R | W,.address = 0x0004,          // 触发系统复位 01 06 00 04 00 01.minValue = 0,.maxValue = 1,.WriteCb = ModbusResetSystem,static void ModbusResetSystem(uint16_t value)
{ResetToBoot();
}void ResetToBoot(void)
{__disable_irq();    //关闭所有中断NVIC_SystemReset(); //复位函数,需要一些执行的时间
}

在APP程序里面,触发复位的时候,我们一定要先关闭所有中断,如果不关闭中断可能会导致我们的BOOT程序会出现错误,因为BOOT里面也需要中断。

此外需要联系之前的知识在做一次对比:

代码段1:

__Vectors0x08000000:    20000428    (..     DCD    5368719760x08000004:    08000145    E...    DCD    1342180530x08000008:    0800014d    M...    DCD    1342180610x0800000c:    0800014f    O...    DCD    1342180630x08000010:    08000151    Q...    DCD    1342180650x08000014:    08000153    S...    DCD    1342180670x08000018:    08000155    U...    DCD    1342180690x0800001c:    00000000    ....    DCD    00x08000020:    00000000    ....    DCD    00x08000024:    00000000    ....    DCD    00x08000028:    00000000    ....    DCD    00x0800002c:    08000157    W...    DCD    1342180710x08000030:    08000159    Y...    DCD    1342180730x08000034:    00000000    ....    DCD    00x08000038:    0800015b    [...    DCD    1342180750x0800003c:    0800015d    ]...    DCD    1342180770x08000040:    0800015f    _...    DCD    1342180790x08000044:    0800015f    _...    DCD    1342180790x08000048:    0800015f    _...    DCD    1342180790x0800004c:    0800015f    _...    DCD    1342180790x08000050:    0800015f    _...    DCD    1342180790x08000054:    0800015f    _...    DCD    1342180790x08000058:    0800015f    _...    DCD    134218079

代码段2:

    __Vectors0x08003000:    20000738    8..     DCD    5368727600x08003004:    08003145    E1..    DCD    1342303410x08003008:    08003d21    !=..    DCD    1342333770x0800300c:    08003965    e9..    DCD    1342324210x08003010:    08003c69    i<..    DCD    1342331930x08003014:    0800356b    k5..    DCD    1342314030x08003018:    08004531    1E..    DCD    1342354410x0800301c:    00000000    ....    DCD    00x08003020:    00000000    ....    DCD    00x08003024:    00000000    ....    DCD    00x08003028:    00000000    ....    DCD    00x0800302c:    0800402d    -@..    DCD    1342341570x08003030:    0800365b    [6..    DCD    1342316430x08003040:    0800315f    _1..    DCD    1342303670x08003044:    0800315f    _1..    DCD    1342303670x08003048:    0800315f    _1..    DCD    1342303670x0800304c:    0800315f    _1..    DCD    1342303670x08003050:    0800315f    _1..    DCD    1342303670x08003054:    0800315f    _1..    DCD    134230367

可以明显看出初始的中断向量地址就是0x08003000:开始的,跟之前的0x08000000:有明显的地址偏移量,而这个地址偏移量就是我们自己设计的0x3000

二、Bootloader如何加载启动App

在这里插入图片描述
首先我们看一下ROM空间的分布,

相当于前面12KB划分给了BOOT,后面的500KB空间给了APP。并且本例程使用的GD32这个单片机一共的FLASH空间就是512KB。

首先是读取 0x08000000:获取栈顶地址,

接着是读取 0x08000004:获取复位函数的地址

然后跳转到复位函数地址 0x08000004:执行复位函数的代码指令,这些是CPU自动完成的。

这里需要说明的为什么CPU需要执行复位函数?
建立可预测的初始状态,消除不确定性,确保系统行为可预测。
CPU上电或复位时,寄存器、程序计数器(PC)、状态标志等内部状态是随机的或残留前次运行的错误值。复位函数会强制将其清零或设为预设值(如PC指向复位向量地址0xFFFF0),使CPU从已知起点开始执行。
程序计数器(PC)清零​:复位后PC指向固定的启动地址(如ARM的0x00000000或x86的0xFFFF0),加载第一条指令
寄存器初始化​:通用寄存器、状态寄存器(如EFLAGS)恢复默认值,避免残留数据干扰新程序。

此外当系统遇到致命错误时,复位是恢复运行的终极手段:

  • 软件错误​:如堆栈溢出、死循环、空指针访问等,通过看门狗定时器触发复位。看门狗超时未清零则强制复位,脱离卡死状态。
  • 硬件故障​:内存错误、总线冲突、电源欠压等触发复位以保护硬件。例如,STM32的欠压检测(VBOR)会直接复位CPU。
  • 中断与死锁​:多核系统中核心间死锁可通过内核复位(如ARM的VECTRESET)局部恢复,避免全系统重启。

那如何启动APP?

我们使用BOOT启动APP的时候也是需要干这些动作,只不过这些动作不是CPU帮助我们自动完成了,而是需要我们自己手动完成。

代码详细解析:

static void BootToApp(void)
{uint32_t stackTopAddr = *(volatile uint32_t*)APP_ADDR_IN_FLASH; if (stackTopAddr > RAM_START_ADDRESS && stackTopAddr < (RAM_START_ADDRESS + RAM_SIZE)) //判断栈顶地址是否在合法范围内{__disable_irq();__set_MSP(stackTopAddr);uint32_t resetHandlerAddr = *(volatile uint32_t*) (APP_ADDR_IN_FLASH + 4);/* Jump to user application */pFunction Jump_To_Application = (pFunction) resetHandlerAddr; // int *p = (int *)0x8003145/* Initialize user application's Stack Pointer */Jump_To_Application();}NVIC_SystemReset();
}

文章源码获取方式:
如果您对本文的源码感兴趣,欢迎在评论区留下您的邮箱地址。我会在空闲时间整理相关代码,并通过邮件发送给您。由于个人时间有限,发送可能会有一定延迟,请您耐心等待。同时,建议您在评论时注明具体的需求或问题,以便我更好地为您提供针对性的帮助。

【版权声明】
本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议。这意味着您可以自由地共享(复制、分发)和改编(修改、转换)本文内容,但必须遵守以下条件:
署名:您必须注明原作者(即本文博主)的姓名,并提供指向原文的链接。
相同方式共享:如果您基于本文创作了新的内容,必须使用相同的 CC 4.0 BY-SA 协议进行发布。

感谢您的理解与支持!如果您有任何疑问或需要进一步协助,请随时在评论区留言。

http://www.lryc.cn/news/583650.html

相关文章:

  • whitt算法之特征向量的尺度
  • 数据结构之位图和布隆过滤器
  • 详解CAN总线的位填充机制
  • 数据结构——深度优先搜索与广度优先搜索的实现
  • [附源码+数据库+毕业论]基于Spring Boot+mysql+vue结合内容推荐算法的学生咨询系统
  • RabbitMQ 4.1.1-Local random exchange体验
  • C++如何进行性能优化?
  • 19-C#静态方法与静态类
  • 【WEB】Polar靶场 21-25题 详细笔记
  • 从0开始学习R语言--Day42--LM检验
  • 异地组网
  • 数据分析框架和方法
  • Mac电脑,休眠以后,发现电量一直在减少,而且一个晚上,基本上是没了,开机都需要插电源的简单处理
  • 卫星通信终端天线的5种对星模式之二:功率检测型载波跟踪
  • 【PyTorch】PyTorch中数据准备工作(AI生成)
  • 深度学习——损失函数
  • Hexo + Butterfly + Vercel 完整个人Blog部署指南
  • Flask3.1打造极简CMS系统
  • 自动化Trae Apollo参数解释的批量获取
  • 股权结构解析
  • SpringBoot集成文件 - 大文件的上传(异步,分片,断点续传和秒传)
  • 专题一_双指针_查找总价格为目标值的两个商品
  • 拼多多正在错失即时零售?
  • ECR仓库CloudFormation模板完整指南
  • 【每日算法】专题六_模拟
  • WPF学习笔记(27)科学计算器
  • 1、专栏介绍以及目录
  • 周立功汽车软件ZXDoc深度解析:新能源汽车开发新基建的破局之道
  • eggNOG数据库注释文件
  • 以太网基础④IP 协议介绍与 IP 校验和算法实现