一、总线简单说明
地址总线、控制总线、数据总线
什么是8位8051框架结构的微控制器?
数据总线宽度为8位,即CPU一次处理或传输的数据量为8位(1字节)
同时还有一个16位的地址总线,这个地方也刚好对应了为什么能看到内存地址是0000H-03FFH这种字眼。这里就是跟直接寻址内容等强相关,就是说CPU的能力是16位,这个地方就跟内存空间息息相关,因为我们如果想内存大,就不得不扩大我们的内存,而扩大我们的内存就意味着增加了CPU的地址总线访问能力,目前常见的如48位→256TB内存支持。所以嵌入式一般的地址总线并不是很大。
此外需要说明的是不管是0000H(16位)还是FFFFFFFFH(32位)他能存储的数据就是一个字节,之所以这么长,目的只是为了提升存储空间。
这个怎么计算呐?
例如0000H-FFFFH,为什么表示64KB,一个地址可以存储一共表示65536个字节,字节称为Byte(1个字节包含8位,bit)。
65536B = 64KB
那数据总线表示的是可以同时取多少个字节(一个地址存储一个字节,如果是16位数据总线,那么就可以同时取出2个字节,也就是需要同时读出两个地址里面存储的数据,不然怎么实现读出16位数据。这个地方和地址总线大小无关,但是如何执行这些操作肯定是有关系的,那就是后话。)。常用的处理器32位,64位,显卡可能是128位。
如果具有40位数据总线,那么本身是32位处理器,那么相当于是一次能读取4个字节的数据才能最大限度发挥该性能,那么相当于是需要找到四个地址(并不是同时读取这四个地址,毕竟如果同时读取就需要160位,不符合逻辑。这个里面应该是流水线结构。)。
二、单片机内部内存空间理解
◆ 最大 64KB 的 FLASH 程序存储器(APROM 区)。
◆ 最大 1KB 的非易失性数据存储器(Data FLASH)。
◆ 最大 256B 的通用内部数据存储器(RAM)。
◆ 最大 4KB 的通用外部数据存储器(XRAM)。
◆ 特殊功能寄存器 SFR。
◆ 外部特殊功能寄存器 XSFR。
2.1 程序储存器 FLASH
以51单片机为例:
程序存储器 FLASH 用来存放源程序和表格数据,且以程序计数器 PC 作地址指针。PC 为 16 位程序计数器,因此可以寻址
到的地址空间为 64KB,此芯片有 64K bytes 程序存储空间。具体空间是:0000H-FFFFH
芯片复位后,CPU 从 0000H 开始执行。每个中断在程序存储器中都分配有固定的地址,中断使 CPU 跳到该地址开始执行
服务程序。例如外部中断 1,被分配地址为 0013H,如果使用外部中断 1,它的服务程序必须从 0013H 位置开始。如果没有用到该中断,其服务地址作为普通程序存储地址使用。
Flash存储器在正常存储状态下断电不会丢失数据(非易失性特性),但在写入或擦除操作过程中突然断电可能导致数据损坏或丢失。
引入一个问题:
那地址0013H存储的程序是中断服务程序吗
1. 0013H的本质是中断向量入口(非完整服务程序)
- 固定入口地址:在8051单片机中,外部中断1(INT1)的中断向量被硬件固定分配在0013H地址
这是CPU响应中断时自动跳转的目标地址。 - 空间限制:0013H至001BH之间仅有8字节空间(0013H~001AH),不足以存放完整的中断服务程序(ISR) 若强行在此写入长代码,会覆盖后续中断入口(如定时器1中断入口001BH)
2. 0013H通常存储跳转指令(AJMP/LJMP)
跳转设计:因此芯片原厂需在0013H处放置无条件跳转指令(如
AJMP
或LJMP
),将程序流导向实际的中断服务程序地址ORG 0013H ; 外部中断1入口 AJMP INT1_ISR ; 跳转到实际ISR
实际ISR位置:中断服务程序通常存放在程序存储器的其他空闲区域(如0100H之后),避免占用中断向量区。
3. 未使用中断时,0013H可作为普通程序空间
- 灵活性设计:若系统未启用外部中断1,0013H地址可自由存储其他程序代码或数据(如初始化代码、常量表)
- 注意事项:若后续启用该中断,必须确保0013H处已覆盖为正确的跳转指令,否则CPU可能执行无效代码导致崩溃。
4. 中断响应的完整流程
- 触发中断:INT1引脚有效 → CPU检测中断标志 → 自动加载0013H到PC。
- 执行跳转:CPU从0013H读取跳转指令 → 跳转至实际ISR地址 → 执行中断处理逻辑。
- 返回主程序:ISR结尾执行
RETI
指令 → 恢复断点地址,继续主程序。
简单理解一下:
这个地方也就解决了,我之前的一个疑问,就是为什么执行完中断以后,还能回到原来的地方,里面的数据等不会改变,因为CPU执行程序,都是和内存进行交互,一切基于地址,此时在执行其他地址上面的程序,那原本的就不执行了,然后再使用一个跳转执行,恢复到断点地址,继续执行程序。这个里面涉及到保护现场等知识,先不进一步理解。
关于Flash引入另外一个问题
Flash是非易失性数据区域,那么我们在写入EEPROM的时候,是否能写入Flash?
EEPROM(电可擦除可编程只读存储器)不会直接写入Flash存储器。
一、EEPROM与Flash的物理隔离性
独立的存储单元结构
EEPROM:通过浮栅晶体管实现按字节擦写,每个存储单元可独立修改电荷状态,无需整块擦除
Flash:采用更简化的晶体管结构,必须按扇区(Block)擦除(如4KB~128KB),擦除后整块区域变为全1(
0xFF
)才能写入新数据物理隔离:在芯片设计中,EEPROM和Flash通常是独立的物理模块,通过不同总线地址访问(如EEPROM映射到数据存储区,Flash映射到程序存储区)
不同的控制器逻辑
EEPROM控制器支持直接覆盖写入(如写入
0x55
到地址0x20
,旧数据自动擦除)Flash控制器需先发送擦除命令,再执行写入操作,无法跳过擦除步骤。
二、EEPROM的写入机制
直接电荷注入
EEPROM通过隧道效应(Fowler-Nordheim Tunneling) 直接修改目标字节的浮栅电荷:- 写入
0
:向浮栅注入电子,提高阈值电压。 - 写入
1
:抽出电子,降低阈值电压
无需整块擦除:此过程仅影响目标单元,相邻数据保持不变。
- 写入
内部电荷泵支持
即使外部电压为3.3V/5V,EEPROM内置电荷泵可生成高压(12V20V),实现单字节操作
三、Flash的写入限制
- ==只能写0,不能直接写1==
Flash的存储单元特性决定:- 写操作:仅能将
1
变为0
(通过热电子注入)。 - 擦除操作:通过强电场释放浮栅电荷,将整块数据恢复为全
1
(0xFF
)
若未擦除直接写入,原有0
无法变为1
,导致数据错误。
- 写操作:仅能将
- 擦除粒度大
擦除最小单位为扇区(如STM32的Flash扇区为1KB~128KB),即使只修改1字节,也需重写整个扇区。
特性 | EEPROM | Flash |
---|---|---|
最小擦除单位 | 字节(Byte) | 扇区(Block,4KB~128KB) |
写入机制 | 直接覆盖旧数据 | 必须先擦除整块再写入 |
写入速度 | 较慢(ms/字节) | 较快(擦除后批量写入) |
寿命 | 10⁶次擦写 | 10⁴~10⁵次擦写 |
典型应用 | 参数配置(如校准值) | 程序代码、大容量数据存储 |
EEPROM映射到数据存储区,Flash映射到程序存储区
EEPROM的写入操作仅作用于自身物理单元,不会影响Flash存储区。两者在硬件层面完全独立,不存在交叉写入的可能性。若需在Flash中存储数据,必须通过专用Flash控制器执行擦除和写入流程。
2.2 非易失性数据存储器 Data FLASH
非易失性数据存储器 Data FLASH 可用于存放常量数据、校准数据、防护安全相关信息等重要数据。存储在该区域的数据具有在芯片断电或者突然性、意外性断电时,数据不会丢失的特性。
Data FLASH 存储器的读、写、擦除操作通过 FLASH 控制接口实现。
为什么需要Data Flash?
1. 解决Code Flash的局限性
- 擦写寿命瓶颈:Code Flash擦写次数有限(约1万次),频繁写入会缩短寿命。Data Flash通过优化存储单元结构(如NAND型)和磨损均衡算法,支持更高擦写次数(10万次+)。
- 写入效率优化:Code Flash需整块擦除才能写入,操作耗时(如STM32擦除1KB需40ms)。Data Flash支持页写入(如512B),减少无效擦除。
2. 满足数据存储的特定需求
- 动态数据管理:系统运行中产生的参数(如温度校准值)、事件日志需频繁更新,Data Flash提供专用空间避免干扰代码区。
- 高密度低成本存储:Data Flash采用NAND结构,单元密度高(如QLC每单元4bit),成本仅为NOR Flash的1/10,适合大数据存储(如音频、图像)。
3. 系统安全性与可靠性
- 隔离代码与数据:Code Flash写错误可能导致程序崩溃。Data Flash独立操作,通过ECC校验和坏块管理提升数据完整性。
- 掉电保护机制:Data Flash设计支持紧急数据保存(如电容缓冲+CRC校验),在断电前完成关键数据存储。
4. 扩展性与灵活性
- 接口简化:SPI/I²C接口的Data Flash减少引脚占用(仅4~6线),适合空间受限的便携设备(如智能手表)。
- 标准化封装:eMMC、SD卡等Data Flash模块可热插拔,便于存储扩展(如工业相机更换存储卡)。
5.典型应用场景
- Code Flash:
✅ 单片机启动代码(STM32的0x0800_0000
)
✅ 实时操作系统内核(如FreeRTOS) - Data Flash:
✅ 用户配置保存(如Wi-Fi密码)
✅ 黑匣子日志(汽车ECU事件记录)
✅ 固件升级缓存(OTA下载包临时存储)
6.使用注意事项
- 避免混用区域:
Code Flash写错误会导致程序崩溃,Data Flash需严格通过控制器访问(如STM32的HAL_FLASH_Program()
)。 - 磨损均衡必需:
高频写入时需动态分配物理地址(如日志结构写入),延长Data Flash寿命。 - 掉电保护设计:
搭配硬件PVD检测(电压阈值触发中断)和软件原子操作,确保数据完整性
在嵌入式系统中,程序运行时的临时数据通常不会直接存储在Flash中,而是存储在RAM(随机存取存储器)中。Flash主要用于存储程序代码和需要长期保存的非易失性数据(如配置参数),而RAM负责存储运行时的变量、堆栈和动态内存数据。
Flash存储器的物理结构中,“页”和“扇区”是不同层级的操作单元
包含层级
==页(Page) 是Flash的最小写入单位,通常为256字节(如W25Q系列)。==
扇区(Sector) 是Flash的最小擦除单位,通常为4KB(即16页 × 256字节)。
块(Block) 是更高层级单位,通常包含多个扇区(如16扇区=64KB)。
以中微CMS80F761X为例
FLASH 存储器包含程序存储器(APROM)与非易失数据存储器(Data FLASH)。程序存储器空间最大为 64KB,分为 128个扇区,每个扇区包含 512B。数据存储器空间最大为 1KB,分为 2 个扇区,每个扇区包含 512B。
==页(Page) 是Flash的最小写入单位,通常为256B
扇区(Sector) 是Flash的最小擦除单位,通常为512B
Flash 存储器擦除操作仅支持扇区擦除,不支持字节擦除。在修改某个地址的数据之前,建议先将其他数据保存后,再擦除当前扇区,最后进行数据写入操作。
第一部分:连续写入等待Flash操作完成
for(i = 0 ;i< 256 ;i++) //连续256 bytes的写等待Flash执行完成
{
FLASH_Write(FLASH_CODE,0xEFFF, 0xFF); //写地址使用最后的地址(任意地址都可以,建议用使用较少的地址)
}
强制等待Flash内部操作
- Flash写入后需等待内部电荷泵操作完成(通常需数µs至ms级时间)。循环256次写入
0xFF
到固定地址(如0xEFFF
),实质是通过重复写入触发Flash控制器状态检测,确保每次写入后Flash完成内部操作 。 - 为何选择
0xFF
?
0xFF
是擦除后的默认值(全1状态),写入0xFF
不会改变原有数据(Flash只能将1→0
,不能0→1
),因此是安全的“空操作” 。
- Flash写入后需等待内部电荷泵操作完成(通常需数µs至ms级时间)。循环256次写入
地址选择的逻辑
- 使用固定地址(如
0xEFFF
)是为了避免跨页风险。Flash写入受页限制(通常256字节/页),若写入跨越页边界会导致数据回绕错误。末尾地址通常未被程序占用,适合测试 。
- 使用固定地址(如
替代状态寄存器查询
- 部分低端MCU无状态寄存器,需通过写入操作间接同步。但此方法效率低(256次写入耗时≈256ms),且加速Flash磨损(寿命约10⁴次)
第二部分:数据写入与验证
for(addr = 0x1000 ;addr< 0x1010 ;addr++)
{
FLASH_Write(FLASH_CODE,addr, Dtemp++);
temp = FLASH_Read(FLASH_CODE,addr);
}
递增数据写入(
Dtemp++
)在地址
0x1000
至0x100F
(共16字节)写入连续递增的字节数据(如0x01, 0x02…)关键前提:目标区域必须已擦除(全为
0xFF
),否则写入会失败(如尝试将0x00
改为0x01
)
即时读取验证
- 每次写入后立即读取相同地址数据,比对
temp
与Dtemp
是否一致,用于检测写入是否正确或Flash物理损坏
- 每次写入后立即读取相同地址数据,比对
地址范围选择
0x1000
通常是用户数据区起始地址(避开代码区),长度16字节适合小数据测试(如配置参数)
在写入数据的时候,
Flash允许单次写入的数据量小于页大小(256字节),但写入地址必须在同一页内且不跨页边界
写入前:目标区域必须为全
1
(0xFF
),否则无法正确写入(未擦除区域写入非0xFF
数据会导致数据错误)写入后:若需修改已写入的数据(如将
0x00
改为0x01
),必须先擦除整个扇区
一般情况要从其实地址开始写入
但是写入的少的时候,不一定非要是起始地址,只需要再规定的地址写就行了。
数据截断规则
编译器或硬件会自动丢弃高 8 位,保留低 8 位:
擦除函数
void FLASH_Erase(uint8_t FLASHModule, uint16_t Addr)
{
MADRL = Addr;
MADRH = Addr>>8;
if(1==EA)
{
EA=0;
MCTRL = FLASH_ERASE | FLASHModule; //操作FLASH期间不允许被打断
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
while(MCTRL & 0x01);
EA=1;
}
else
{
MCTRL = FLASH_ERASE | FLASHModule;
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
while(MCTRL & 0x01);
}
}
其中MCTRL就是操作,看数据手册,关于寄存器的描述,一定要看关于数据手册的描述,不然就会走到误区,容易走弯路。如
在硬件层面,会自动执行相关操作,
这个函数的Addr,说白了就是为了定位扇区,然后进行擦除。
不然怎么进行扇区擦除。硬件自动的执行一些操作。这就又设计到芯片原厂的操作了。至于为什么,那就暂时就理解到这里。
文章源码获取方式:
如果您对本文的源码感兴趣,欢迎在评论区留下您的邮箱地址。我会在空闲时间整理相关代码,并通过邮件发送给您。由于个人时间有限,发送可能会有一定延迟,请您耐心等待。同时,建议您在评论时注明具体的需求或问题,以便我更好地为您提供针对性的帮助。
【版权声明】
本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议。这意味着您可以自由地共享(复制、分发)和改编(修改、转换)本文内容,但必须遵守以下条件:
署名:您必须注明原作者(即本文博主)的姓名,并提供指向原文的链接。
相同方式共享:如果您基于本文创作了新的内容,必须使用相同的 CC 4.0 BY-SA 协议进行发布。
示例:
如果您在博客或文章中引用了本文的内容,请在显著位置标注类似以下声明:
本文部分内容参考自 [博主名称] 的原创文章,原文链接:[文章链接],遵循 CC 4.0 BY-SA 版权协议。
感谢您的理解与支持!如果您有任何疑问或需要进一步协助,请随时在评论区留言。*