51核和ARM核单片机OTA实战解析(一)
文章目录
- 摘要
- 一、51内核
- 1.1 FLASH(APROM区)与DATA Flash
- 1.1.1 物理存储分离
- 1.1.2 访问触发差异
- 二、ARM内核
- 2.1 FLASH(APROM区)与DATA Flash
- 2.1.1 ARM的哈佛结构与总线隔离机制
- 2.1.2 地址重映射(Memory Re-Map)
- 2.1.3 访问权限与存储器保护单元(MPU)
- 2.1.4 ARM方案的底层支持机制
- 三、OTA
摘要
在解析之前先说一下使用的芯片背景,本次使用的是51内核单片机,OTA升级的方式是BOOT型。
后续更新计划包括ARM核OTA,以及AB备份型升级思路,以及在这个过程中是使用EEPROM、使用DATA FLASH。
一、51内核
想理清楚OTA的升级思路,那就必须知道每一个程序所在的位置在什么地方,以及该芯片支持的内存空间有那些,例如是否有EEPROM?、是否有DATA FLASH?这是因为不同的内存我们使用的思路是不一样的。
以芯片CMS80F761x 为例该系列微控制具有如下几种存储器:
◆最大 64KB 的 FLASH 程序存储器(APROM 区)。
◆最大 1KB 的非易失性数据存储器(Data FLASH)。
◆最大 256B 的通用内部数据存储器(RAM)。
◆最大 4KB 的通用外部数据存储器(XRAM)。
◆特殊功能寄存器 SFR。
◆外部特殊功能寄存器 XSFR。
对于FLASH程序存储器:用来存放BOOT程序、APP程序。
Data FLASH:用来存放升级的标志位(这是因为升级包是从WIFI端口下发下来的)。
通过芯片手册可以看出
FLASH空间的内存地址分布如下图所示:
DATA FLASH空间地址内存分布:
笔者在看到这两个空间第一时间的异或是他们两个的逻辑地址竟然是一样的,为什么要这样设计?在访问的时候不会出问题吗
也就是这个问题:CMS80F761x (51内核)程序储存器 FLASH的地址是0000H到FFFFH,而非易失性数据存储器 Data FLASH的地址是0000H到03FFH,这个会产生影响吗?
1.1 FLASH(APROM区)与DATA Flash
在嵌入式系统中,程序存储器(Program FLASH)与非易失性数据存储器(Data FLASH)的地址范围重叠(如CMS80F761x中均为0000H-03FFH
)看似冲突,实则通过物理隔离与总线访问机制实现功能隔离。
代码区只读不写:程序FLASH仅通过烧录器更新,运行时禁止写入。
数据区专用接口:Data FLASH必须通过控制器API访问,避免直接寻址。
1.1.1 物理存储分离
程序FLASH与Data FLASH是独立的物理存储单元,但通过存储器控制器映射到相同的逻辑地址空间(如0000H-03FFH
)。
-
程序FLASH:存储固件代码,通过指令总线(I-Code) 访问,CPU取指时自动路由至此。
-
Data FLASH:存储用户数据,通过数据总线(D-Code) 访问,需显式调用读写指令(如
LDREX/STREX
)。
1.1.2 访问触发差异
-
读取
0000H
地址时:-
若为指令获取(如复位后PC指向
0000H
)→ 访问程序FLASH的代码区。 -
若为数据加载(如
LDR R0, [0x0000]
)→ 访问Data FLASH的数据区。
-
程序FLASH(0000H-FFFFH
)
-
功能:存储中断向量表(前
03FFH
)及主程序代码。 -
关键操作:
-
复位后CPU从
0000H
取中断向量表地址,通过指令总线访问程序FLASH。 -
擦除单位:扇区(512B),写入单位:页(256B)。
-
Data FLASH(0000H-03FFH
)
-
功能:独立存储校准参数、事件日志等数据,不与程序代码共享物理单元。
-
关键操作:
-
-
需通过专用控制器接口(如
FLASH_Write()
函数)访问,避免误操作代码区。 -
支持更小粒度写入(如单字节修改),且内置磨损均衡算法。
-
这里其实可以理解为DATA FLASH的功能和EEPROM作用是一样的,但是他们的内部工艺是不一样。并且上面的FLASH空间我个人可以理解为是CODE FLASH空间。
备注:
-
避免直接指针操作
- 访问Data FLASH必须调用芯片原厂API(如
DATAFLASH_WritePage()
),禁止直接写地址0000H
。
- 访问Data FLASH必须调用芯片原厂API(如
-
中断向量表保护
- Data FLASH的
0000H-03FFH
区域与中断向量表重叠,需确保该区不被意外擦除(如关闭全局中断后再操作Data FLASH)。
- Data FLASH的
关于上述问题,在ARM核中同样存在。**
二、ARM内核
同样是中微芯片,ARM内核的FLASH空间分布:
2.1 FLASH(APROM区)与DATA Flash
ARM单片机通过地址范围严格区分存储区域
存储区域 | 地址范围 |
---|---|
主闪存区 (Program Flash) | 0000_0000H ~ 0001_FFFFH |
数据闪存 (Data Flash) | 0050_0000H ~ 0050_05FFH |
SRAM | 2000_0000H ~ 2000_2FFFH |
外设资源区 | 4000_0000H ~ 4005_FFFFH |
Cortex-M0+专用外设资源区 | E000_0000H ~ E00F_FFFFH |
ARM单片机的方案:
直接采用物理地址隔离,Data Flash拥有独立地址段(如0050_0000H
),与Program Flash(0000_0000H
)分离。
2.1.1 ARM的哈佛结构与总线隔离机制
ARM处理器(尤其是Cortex-M系列)采用改进的哈佛架构,其核心特点是:
-
指令总线(I-Code)与数据总线(D-Code)分离:即使程序Flash和数据Flash映射到相同的逻辑地址范围(如
0x00000000
),CPU通过不同的总线访问指令和数据:-
I-Code总线:专用于从Program Flash取指令。
-
D-Code总线:用于访问Data Flash或RAM中的数据(如常量表或非易失性数据)。
-
-
物理隔离:两条总线独立工作,即使逻辑地址重叠,实际访问的物理存储介质不同(如Program Flash芯片与Data Flash芯片分离),因此不会冲突。
示例:
-
STM32中,Program Flash通常位于
0x08000000
,而Data Flash可能是内部Flash的一部分(如选项字节区)或外部Flash。 -
若两者逻辑地址重叠(如均映射到
0x00000000
),CPU通过I-Code取指时访问Program Flash,通过D-Code读数据时访问Data Flash。
2.1.2 地址重映射(Memory Re-Map)
ARM通过重映射机制动态切换逻辑地址对应的物理存储区域:
-
Boot重映射:复位后,系统将Program Flash的向量表(如中断入口)映射到
0x00000000
,但运行时可将其重映射到SRAM或Data Flash区域。 -
异常向量重映射:中断向量表可动态重定位到不同物理地址(如SRAM或外部Flash),避免与主程序Flash冲突。
-
别名区(Alias Regions):ARM支持位带(Bit-Band)技术,将某地址区的访问重定向到其他物理位置,实现精细控制。
典型场景:
在STM32中,Boot引脚可配置为从主Flash、系统存储器(Bootloader)或SRAM启动。若选择从SRAM启动,则0x00000000
映射到SRAM,而非Flash 。
2.1.3 访问权限与存储器保护单元(MPU)
-
特权级控制:Cortex-M系列支持特权/非特权模式,限制用户代码直接修改关键地址(如Flash控制寄存器)。
-
MPU隔离:通过MPU配置不同存储区域的访问权限(如只读、不可执行),例如:
-
Program Flash设为可执行+只读。
-
Data Flash设为可读写+不可执行,防止代码注入。
-
2.1.4 ARM方案的底层支持机制
虽然通过地址区分,但ARM架构仍依赖以下技术确保访问正确性:
-
总线矩阵路由:CPU通过不同总线(I-Code/D-Code)访问地址,硬件自动路由到对应物理存储。
-
MPU保护:可配置Data Flash为不可执行(阻止代码注入),Program Flash为只读(防止意外篡改)。
如GD32F303
这是整个单片机的存储空间,根据功能不同划分为不同的功能区。
ARM单片机在地址0x0000_0000到0x2000_0000 这个范围统称为是存储代码的,具体里面又分为Boot、APP甚至部分单片机还独立出Data Flash空间。
如中微BAT32G137
但是需要说明的是GD32F303在CODE区域分出了不一样的内存,并且GD32是叫做CODE,但是从实际意义来说属于是上述中微芯片的FLASH区域。这里只需要知道本质是一样的只是定义的名字不一样就行。
GD32F303的Flash架构特点
-
统一Flash存储区:
GD32F303的片上Flash是一个统一编址的连续空间(地址范围通常为
0x0800_0000
起),未像某些芯片(如STM32F4系列)那样明确划分独立的Data Flash区域。程序代码(Code Flash)和用户数据共用同一物理Flash存储器。 -
分区使用逻辑:
虽然物理上未分离,但开发者需手动划分逻辑区域:
- Code Area:存储程序代码(占用Flash起始部分)。
- Data Area:通常保留末尾的若干扇区(如最后1-4页)存储需掉电保存的数据(如参数、配置信息)。
示例:在512KB Flash的GD32F303ZET6中,开发者常将最后两页(
0x0807F800–0x0807FFFF
和0x0807FC00–0x0807FFFF
)作为Data Area。
GD32F303 没有独立Data Flash,但可通过划分主Flash末尾扇区作为数据存储区实现非易失数据保存。
而在GD32F303里面是没有故意划分出Data Flash的,从芯片手册中可以看到,一般是将Main Flash的最后两个或者三个扇区作为Data Flash使用。
必须确保Data Area的地址避开程序代码区,否则会覆盖程序导致崩溃。通常选择末尾扇区(如512KB Flash的最后一页0x0807F800
)
并且在Flash里面划分出Data FLash 其操作流程和Flash的读写一样。
-
按页管理:
Flash的最小擦除单位是扇区(页)(通常2KB/页),写入需按字(32位)操作。数据存储前必须擦除整页,因此需避免频繁写入。
-
操作流程:
-
解锁Flash:通过
fmc_unlock()
发送密钥(0x45670123
和0xCDEF89AB
)。 -
擦除页:调用
fmc_page_erase(address)
擦除目标页。 -
写入数据:使用
fmc_word_program(address, data)
逐字写入。 -
重新上锁:
fmc_lock()
防止误操作。
-
-
读取数据:
直接通过地址访问,如:
uint32_t data = *(__IO uint32_t*)(0x0807F800); // 读取Data Area数据[2,5](@ref)。
一定要注意擦写的过程,避免出现乱擦写操作。
备注:
Data Flash和EEPROM作用在某种程度来说是一样的,毕竟都是存储一些掉电保护的数据,但是他们两个的工艺肯定是不一样的。
所以说主FLASH和Data FLASH有本质区别。
以目前常用的芯片为例(CMS80F761x 、BAT32G137)
我觉得可以叫做Code Flash 和 Data Flash 但是他们都是属于Flash,也就是我们烧写程序的Flash空间。 其实叫Flash也是不准确的(毕竟Flash是整个芯片的存储空间),但是大家都是这样叫的,无伤大雅,那就约定俗称了。但是个人一定要明白这里面的弯弯绕绕。
不管是什么芯片,访问Code Flash和Data Flash 是有本质区别的一个是通过指令总线,一个是通过数据总线。从这个角度来看这两个已经做了隔离。
三、OTA
此外在说一下OTA过程,
先以GD32为例:
在此单片机中,厂商已经预置了一个厂商的Bootloader,这个地方可以认为和我们自己写的Bootloader作用是一样的都是可以给Flash写数据什么的。
“救砖级”固件更新
-
场景:用户程序崩溃、芯片被意外擦除、硬件无调试接口
-
机制:通过串口(UART/USART)、USB、CAN等通用接口,无需下载器即可重烧主闪存(Main Flash)。
启动路径动态切换
- 硬件选择:通过
BOOT0
/BOOT1
引脚电平组合选择启动源:
BOOT1 | BOOT0 | 启动模式 |
---|---|---|
0 | 0 | 主闪存(用户程序) |
0 | 1 | 系统存储器(Bootloader) |
1 | x | SRAM(调试模式) |
也就是这段话:
参考链接ARM单片机启动流程(一)(详细解析)-CSDN博客
通过上图可以看出,主要是根据Boot管脚的配置,来确定是那种方式,主FLASH存储器(开始于0x0800 0000的原始存储空间)或系统存储器(MCU厂商预置的bootloader开始于0x1FFF F000的原始存储空间)被映射到引导存储空间(起始于0x0000 0000)。
通俗一点将就是通过配置Boot1和Boot0的引脚,来确定Aliased to Main Flash or Boot loader将0x0000 0000映射到哪里(可能是Bootloader模式、内置SRAM启动(调试模式)、主闪存启动(正常工作模式))。
直接结果就是:
CPU都是从0地址开始访问的,根据被引导到的地方,有可能直接跳转到Main Flash 0x0800 0000的原始存储空间;也有可能跳转到MCU厂商预置的bootloader开始于0x1FFF F000的原始存储空间。
因此OTA两个思路:
一个是通过厂商的Bootloader进行相关OTA,但是不推荐。
另外一种是通过Code Flash空间划分出我们的Bootloader空间,人为的编写Boot程序。
当然OTA也分为好几种类型,一个是BOOT型,一个是AB备份型。
本文主要使用的是BOOT型讲解。
以51单片机为例
首先是进行底层配置
初始中断位置:
起始地址是3,按照间隔位置是8,则3 4 5 6 7 8 9 A 下一个的中断的其实地址刚好是B,满足表格数据。
地址范围 ≠ 中断服务程序(ISR)实际大小
表格中的 "地址范围" 不是中断服务程序(ISR)的代码长度,而是 中断向量表条目在内存中的分布范围。
中断向量表的物理含义
-
每个中断源在向量表中占用一个 固定条目(例如4字节)。
-
条目内容:存储跳转到对应ISR的指令(如
JMP
或LJMP
指令)或ISR入口地址。 -
条目大小固定:在经典8051架构中,每个中断向量条目通常为 3字节(一条长跳转指令
LJMP
)。 在经典8051架构中,每个中断向量条目通常为 3字节(一条长跳转指令LJMP
)。
为满足对齐要求或预留未用中断位置(如8051的向量表间隔通常为 8字节)。
ARM中断向量表
架构要求(适用于ARM Cortex-M系列等32位MCU):
-
每个中断向量条目存储一个 32位地址(即4字节),指向中断服务程序(ISR)的入口。
-
这是由CPU硬件设计决定的标准化间隔。
地址对齐约束:向量表起始地址必须按 128字节对齐(如起始地址 0x0000_0040
是64字节对齐,符合部分Cortex-M变体的要求)。条目内部严格按4字节对齐,避免内存访问错误。
快速索引机制:发生中断时,CPU通过中断编号(如IRQ号)乘以 4
直接定位条目地址(地址 = 向量表基址 + IRQ号 × 4)。
向量地址的数值特征
-
首个中断(IRQ 0)的地址:
0x0000_0040
(即十六进制0x40
)。 -
对齐验证:
- 128字节 =
0x80
(十六进制)。 - 地址
0x40
的 低7位 为:0x40 & 0x7F = 0
(后7位全零)。
结论:0x0000_0040
是 128字节对齐的起始地址(128的整数倍)。
- 128字节 =
若地址是 N字节对齐,则该地址必须是 N的整数倍(即能被N整除)
128字节对齐:地址需满足 地址 % 128 = 0
,即二进制表示时低7位全为0(因为 128=27)。
- 验证方法:计算
地址 & 0x7F
(0x7F
是二进制的01111111
,用于屏蔽低7位),若结果为0,则对齐。
为什么要进行字节对齐?
(1)硬件强制要求
-
CPU访问效率:
现代处理器(如ARM Cortex-M)对内存访问有严格的对齐要求。例如:-
32位CPU(如Cortex-M3/M4)要求4字节对齐(地址是4的倍数),否则触发硬件异常(HardFault)。
-
向量表对齐:图片中向量地址均为4字节间隔(
0x0040
,0x0044
,0x0048
…),确保CPU能直接通过中断号×4快速定位条目。
-
(2)性能优化
-
减少内存访问次数:
对齐的数据(如4字节对齐的向量地址)可被CPU单次读取,未对齐数据可能需要多次访问。例如:
- 若向量地址为
0x0041
(未对齐),CPU需先读0x0040
,再读0x0044
,拼接出目标值,效率极低。
- 若向量地址为
(3)缓存与预取优化
-
缓存行对齐:
内存缓存行(Cache Line)通常为32/64字节。对齐的向量表(如起始地址
0x0040
)可完整载入缓存行,减少缓存命中失败。
专栏介绍
《嵌入式通信协议解析专栏》
《PID算法专栏》
《C语言指针专栏》
《单片机嵌入式软件相关知识》
《FreeRTOS源码理解专栏》
文章源码获取方式:
如果您对本文的源码感兴趣,欢迎在评论区留下您的邮箱地址。我会在空闲时间整理相关代码,并通过邮件发送给您。由于个人时间有限,发送可能会有一定延迟,请您耐心等待。同时,建议您在评论时注明具体的需求或问题,以便我更好地为您提供针对性的帮助。
【版权声明】
本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议。这意味着您可以自由地共享(复制、分发)和改编(修改、转换)本文内容,但必须遵守以下条件:
署名:您必须注明原作者(即本文博主)的姓名,并提供指向原文的链接。
相同方式共享:如果您基于本文创作了新的内容,必须使用相同的 CC 4.0 BY-SA 协议进行发布。
感谢您的理解与支持!如果您有任何疑问或需要进一步协助,请随时在评论区留言。