1. 项目概述与核心价值
在嵌入式开发的日常里,和微控制器的Flash与EEPROM打交道是家常便饭。无论是存储用户配置、记录运行日志,还是实现固件的在线升级,都离不开对这两种非易失性存储器的精准操控。然而,很多开发者,尤其是刚入行的朋友,面对数据手册里那一页页密密麻麻的寄存器描述,常常感到无从下手。寄存器配置对了,一切顺风顺水;配置错了,轻则数据丢失,重则芯片“变砖”,调试起来更是让人头疼。
今天,我就以TI的Tiva™ TM4C123GH6ZRB这款经典的Cortex-M4内核微控制器为例,把它的Flash控制器和EEPROM模块里几个最核心、也最容易让人困惑的寄存器,掰开揉碎了讲清楚。我们重点聊聊Flash控制器中断屏蔽(FCIM)、Flash控制器可屏蔽中断状态和清除(FCMISC)、Flash存储器控制2(FMC2),以及EEPROM相关的几个关键寄存器。这些寄存器就像是控制器的“开关”和“状态灯”,理解了它们,你就能实现安全、高效的Flash编程和灵活的中断响应,而不是在出问题时对着芯片干瞪眼。
这篇文章适合所有正在或即将使用Tiva™ C系列微控制器的嵌入式软件工程师、电子爱好者以及相关专业的学生。我会从寄存器每一位的具体含义讲起,结合我实际项目中的配置代码和踩过的坑,告诉你为什么要这么配置,以及怎么安全地操作。我们的目标很明确:让你看完就能用,用了不出错。
2. 核心寄存器深度解析与设计思路
要安全高效地操作Flash和EEPROM,不能只知其然,更要知其所以然。Tiva™微控制器的存储器控制器设计得非常精细,通过一系列寄存器实现了从地址指定、数据缓冲、操作触发到状态监控和中断管理的完整链条。理解这个链条上每个环节的作用和关联,是避免操作失败和系统崩溃的关键。
2.1 Flash控制器中断管理:FCIM与FCMISC寄存器
中断是嵌入式系统实现实时响应的灵魂。对于Flash操作这类耗时且可能出错的任务,采用中断方式可以极大解放CPU,避免轮询带来的资源浪费。Tiva™的Flash控制器中断系统主要由三个寄存器协同工作:原始中断状态寄存器(FCRIS)、中断屏蔽寄存器(FCIM)和中断状态与清除寄存器(FCMISC)。输入资料详细给出了FCIM和FCMISC的位定义,这正是中断管理的“配置端”和“处理端”。
为什么需要FCIM(中断屏蔽)?想象一下,Flash控制器就像一个车间,可能产生多种“告警”(中断),比如“编程完成”(PROG)、“访问违规”(ACCESS)、“电压错误”(VOLT)等。FCIM寄存器的作用,就是为车间经理(CPU)安装一个“告警筛选面板”。经理可能只关心“编程完成”和“严重错误”,而不想被“电压轻微波动”这种次要告警频繁打扰。通过设置FCIM相应的位为1,你就打开了对应告警的通道;设置为0,则屏蔽了该告警,即使事件发生,也不会通知到CPU的中断控制器(NVIC)。这种设计赋予了软件极大的灵活性,允许开发者根据应用场景,只启用必要的中断源,减少不必要的中断响应开销。
FCMISC(中断状态与清除)的双重角色如果说FCIM是“开关”,那么FCMISC就是“状态灯”兼“复位按钮”。当一个被FCIM允许(即未被屏蔽)的中断事件发生时,首先会在FCRIS寄存器中置起对应的原始状态位。同时,如果该中断在FCIM中被使能,那么FCMISC寄存器中对应的位也会被硬件置1,标志着“有一个有效的中断正在等待处理”。 当CPU进入中断服务程序(ISR)后,第一件事就是查明中断源。这时,读取FCMISC寄存器,检查哪一位被置1,就能快速定位是哪个Flash操作引发了中断。更关键的是,FCMISC的位是“写1清除”(W1C)类型。这意味着在ISR中,必须向该位写入1,才能将其清零,同时也连带清除了FCRIS中的原始状态位。这个“清除”动作至关重要,它告诉硬件:“这个中断我已经处理完了”,从而为下一次中断的发生做好准备。如果忘记清除,会导致中断持续触发,系统陷入死循环。
关键位解析与实战关联
- AMASK/PMASK (位0, 位1): 分别控制访问违规和编程完成中断。对于大多数Flash写入应用,
PMASK是必须打开的,这样你才能知道写操作何时完成。AMASK则在调试保护功能时非常有用。 - EMASK (位2): EEPROM操作完成中断。如果你同时使用片内EEPROM,这个位需要关注。
- VOLTMASK/INVDMASK/ERMASK (位9, 10, 11): 分别对应电压错误、无效数据错误和一般性错误中断。在可靠性要求高的应用中,建议打开
ERMASK,以便捕获未知错误。VOLTMASK在电池供电或电源不稳的场景下很有用。 - PROGMASK (位13): 编程验证错误中断。在执行了“编程验证”操作后,如果发现写入的数据与预期不符,会触发此中断。对于要求数据绝对正确的场合(如固件升级),应启用此中断。
注意:在配置FCIM之前,务必先通过
SYSCTL_RCGC0或类似寄存器使能Flash控制器的时钟。同时,所有保留位(Reserved)必须保持其复位值,通常通过“读-修改-写”操作来确保不改变它们。
2.2 Flash编程操作的核心引擎:FMC2与FWBn寄存器
对Flash进行写入或擦除,不是一个简单的内存赋值操作,而是一个需要特定时序和电压的物理过程。Tiva™通过Flash存储器地址寄存器(FMA)、Flash写缓冲器(FWBn)和Flash存储器控制2(FMC2)这三个寄存器,构建了一个安全且高效的编程流水线。
操作流程的“为什么”
- 设置目标地址(FMA): 告诉控制器:“我要往哪里写”。地址必须对齐到4字节(32位字)边界。
- 填充写缓冲区(FWBn): Flash编程通常以多字(如32字)为单位进行,以提高效率。
FWB0到FWB31这32个寄存器构成了一个临时数据区。你只需要更新你想写入的那些字对应的FWBn寄存器。FWBVAL寄存器的相应位会自动置1,标记该缓冲区数据有效。 - 发出写命令(FMC2): 这是最后一步,也是最关键的一步。向
FMC2寄存器的WRBUF位写1,并同时在WRKEY域写入正确的密钥(0xA442或0x71D5),整个编程操作才会真正启动。
FMC2.WRKEY:不可或缺的安全锁WRKEY域是防止软件跑飞或指针错误导致意外写入Flash的最后一道防线。你必须同时满足两个条件:WRBUF=1且WRKEY等于特定值。这个密钥值由BOOTCFG寄存器的KEY位决定。这个设计极大地提高了系统的鲁棒性。在编程代码中,这通常体现为一个宏定义:
#define FLASH_FMC_WRKEY 0xA442 // 假设BOOTCFG.KEY为0然后你的写触发代码是:
HWREG(FLASH_FMC2) = (FLASH_FMC_WRKEY << 16) | FLASH_FMC2_WRBUF;FWBn与FWBVAL的协同FWBVAL寄存器是一个位图,指示了哪个FWBn缓冲区自上次写操作后被更新过。硬件在每次写操作完成后会自动清零FWBVAL。这个机制的精妙之处在于:
- 效率:你无需每次都填满全部32个字。比如只改一个字,就只更新
FWB0,FWBVAL[0]置1,然后触发写操作。硬件只会将FWB0的数据写入FMA指定的地址。 - 重复编程:如果你想将同一批数据写入Flash的另一个地址,在第一次写操作后,
FWBVAL被清零,但FWBn寄存器里的数据还在。此时,你只需设置新的FMA地址,然后手动���FWBVAL对应位置1,再次触发FMC2即可。这避免了重复加载数据。 - 取消操作:如果你更新了某个
FWBn但又不想写入,可以在触发写操作前,手动清除FWBVAL中对应的位。
实操心得:在启动写操作(写
FMC2)后,必须等待操作完成。最可靠的方式是轮询FCRIS寄存器的PRIS(编程原始中断状态)位,或者如果使能了中断,则在中断服务程序中处理。绝对不要在写操作进行期间(即PRIS为0或中断未发生前)去访问正在被编程的Flash扇区,这会导致读取到错误数据或引发访问错误。
3. 关键操作流程与寄存器配置实战
理解了原理,我们来看如何将这些寄存器组合起来,完成一次完整的Flash写入操作,并妥善处理EEPROM的读写。我会给出代码片段和详细的步骤说明,你可以直接应用到自己的项目中。
3.1 Flash单字编程操作步骤详解
假设我们需要将0x12345678这个数据写入Flash的0x00010000地址。以下是基于TivaWare驱动库风格和直接寄存器操作两种方式的详细步骤。
步骤一:准备工作与解锁在操作Flash前,必须确保其时钟已使能,并且如果目标扇区被保护,可能需要解锁。
#include <stdint.h> #include “inc/hw_flash.h” #include “inc/hw_types.h” #include “driverlib/sysctl.h” // 1. 使能Flash控制器时钟(假设使用TivaWare库) SysCtlPeripheralEnable(SYSCTL_PERIPH_FLASH); // 2. 等待Flash控制器就绪(非必须,但建议) while(!(HWREG(FLASH_FMC) & FLASH_FMC_WRKEY_M)) {} // 3. 检查并解锁目标地址所在的扇区(如果需要) // 通过FMPPEn寄存器进行保护管理,这里假设未保护。步骤二:配置地址与数据
// 4. 设置目标地址。地址必须是字对齐的(低2位为0)。 HWREG(FLASH_FMA) = 0x00010000; // 5. 将数据写入Flash写缓冲器0(FWB0)。 HWREG(FLASH_FWB0) = 0x12345678; // 6. 手动设置FWBVAL[0],表明FWB0中的数据是新的、待写入的。 HWREG(FLASH_FWBVAL) |= 0x00000001; // 将第0位置1步骤三:触发编程操作并等待完成
// 7. 触发编程操作。使用正确的写密钥(此处以0xA442为例)。 HWREG(FLASH_FMC2) = (0xA442 << 16) | FLASH_FMC2_WRBUF; // 8. 轮询等待编程完成。检查FCRIS寄存器的PRIS位。 while(!(HWREG(FLASH_FCRIS) & FLASH_FCRIS_PRIS)) {} // 9. 清除编程完成原始中断状态(如果不需要中断,则直接清除)。 HWREG(FLASH_FCMISC) = FLASH_FCMISC_PMISC; // 写1清除PMISC位和PRIS位 // 10. 可选:进行读取验证。 uint32_t read_data = HWREG(0x00010000); if(read_data != 0x12345678) { // 验证失败,处理错误(可检查FCRIS中的错误位,如INVDRIS) }步骤四:中断方式处理如果你使能了中断,步骤会有所不同:
// a. 配置FCIM,使能编程完成中断。 HWREG(FLASH_FCIM) |= FLASH_FCIM_PMASK; // b. 在NVIC中使能Flash控制器中断。 IntEnable(INT_FLASH); // TivaWare库函数 // c. 编写中断服务程序。 void FlashIntHandler(void) { uint32_t status = HWREG(FLASH_FCMISC); // 读取中断状态 if(status & FLASH_FCMISC_PMISC) { // 编程完成中断 // ... 进行后续处理,如更新状态标志 ... HWREG(FLASH_FCMISC) = FLASH_FCMISC_PMISC; // 清除中断 } // 检查并处理其他中断源... } // d. 触发写操作(同上一步骤7)。之后CPU可执行其他任务,无需轮询。 // e. 在中断服务程序中处理完成事件。3.2 EEPROM读写操作流程解析
Tiva™ TM4C123GH6ZRB内部集成了2KB的EEPROM,其操作接口比Flash更简单,但同样需要遵循特定的流程。
初始化与状态检查
#include “inc/hw_eeprom.h” // 1. 使能EEPROM模块时钟 SysCtlPeripheralEnable(SYSCTL_PERIPH_EEPROM0); // 2. 等待至少3个系统时钟周期(通常用空操作或短延时) __asm(“ NOP\n NOP\n NOP”); // 3. 等待EEPROM模块初始化完成(轮询EEDONE寄存器的WORKING位) while(HWREG(EEPROM_EEDONE) & EEPROM_EEDONE_WORKING) {}基本读写操作EEPROM操作以“块”(16个字)和“字”为单位。你需要先选择块,再选择块内的字偏移。
// 4. 选择要操作的块(例如块0) HWREG(EEPROM_EEBLOCK) = 0; // 5. 选择块内的字偏移(例如偏移0,即该块第一个字) HWREG(EEPROM_EEOFFSET) = 0; // 6. 写入数据到EERDWR寄存器,写操作自动开始 HWREG(EEPROM_EERDWR) = 0xDEADBEEF; // 7. 等待写操作完成(轮询EEDONE的WORKING位) while(HWREG(EEPROM_EEDONE) & EEPROM_EEDONE_WORKING) {} // 8. 检查是否出错(检查EEDONE寄存器的ERROR位) if(HWREG(EEPROM_EEDONE) & EEPROM_EEDONE_ERROR) { // 处理错误,可能是写保护或访问冲突 } // 9. 读取数据:设置好EEBLOCK和EEOFFSET后,直接读取EERDWR寄存器 uint32_t read_value = HWREG(EEPROM_EERDWR);连续写入技巧使用EERDWRINC寄存器可以实现地址自动递增的连续写入,非常适合初始化或批量存储数据。
// 10. 设置起始块和偏移 HWREG(EEPROM_EEBLOCK) = 0; HWREG(EEPROM_EEOFFSET) = 0; // 11. 向EERDWRINC寄存器连续写入,地址会自动递增(在块内循环) uint32_t data_array[4] = {0x11111111, 0x22222222, 0x33333333, 0x44444444}; for(int i = 0; i < 4; i++) { HWREG(EEPROM_EERDWRINC) = data_array[i]; while(HWREG(EEPROM_EEDONE) & EEPROM_EEDONE_WORKING) {} // 等待每次写入完成 // 注意:写入EERDWRINC后,EEOFFSET寄存器的值会自动更新,指向下一个位置。 }4. 常见问题排查与实战避坑指南
在实际项目中,仅仅知道如何配置寄存器是不够的,更重要的是知道当事情不按预期发展时该如何排查。下面是我在多年开发中总结的几个典型问题及其解决方法。
4.1 Flash编程失败问题排查
问题现象:向FMC2写入后,PRIS位永远置不起来,或者FCRIS寄存器中的错误位(如INVDRIS,VOLTRIS)被置位。
排查步骤与思路:
- 检查时钟与电源:这是最基础也最容易被忽视的一点。确保系统主频在芯片允许的Flash编程频率范围内(参考数据手册的“Flash Memory Programming Specifications”章节)。对于TM4C123,在高于一定频率(如20MHz)时,可能需要插入等待周期。同时,确保供电电压稳定,特别是
VDDA(模拟电源),因为Flash编程对电压敏感。 - 验证地址与对齐:确认
FMA寄存器中的地址是有效的Flash地址(不是SRAM或外设地址空间),并且是字对齐的(地址的低2位必须为0)。尝试对一个已知的、未使用的Flash扇区末尾地址进行写入测试。 - 检查写保护:通过
FMPPEn寄存器检查目标扇区是否被写保护。如果是,你需要先解锁该扇区(如果允许的话)。解锁操作通常涉及向FMPPEn寄存器写入特定的密钥。 - 确认缓冲区与触发顺序:
- 确保在写
FMC2之前,已经正确设置了FMA。 - 确保目标数据已写入
FWBn寄存器,并且对应的FWBVAL[n]位为1。你可以通过读取FWBVAL寄存器来确认。 - 确认
FMC2的写入操作是“原子”的,即WRKEY和WRBUF在同一句写操作中完成。像HWREG(FLASH_FMC2) = (KEY << 16) | 1;这样的语句是正确的。
- 确保在写
- 解读错误状态:立即检查
FCRIS寄存器。INVDRIS(无效数据):通常意味着你试图将1写成0。Flash位只能从1擦除成0,再从0编程成1。如果目标位置原本是0,你想把它写成1,必须先执行扇区擦除操作,将该扇区所有位恢复为1。VOLTRIS(电压错误):系统电压可能低于Flash编程所需的最低电压。检查电源,或等待系统稳定。ERRIS(一般错误):可能发生了其他内部错误,尝试复位Flash控制器(通过系统控制模块)后重试。
- 扇区擦除操作:在编程前,如果目标区域不是空白状态(全0xFF),必须先擦除。擦除操作通过
FMC寄存器(不是FMC2)触发,同样需要写密钥。擦除是以扇区为单位的,一个扇区通常是1KB或更大,擦除后整个扇区变为全0xFF。
4.2 EEPROM访问异常问题排查
问题现象:写入EEPROM后读取的值不正确,或者写入操作后EEDONE寄存器的ERROR位被置位。
排查步骤与思路:
- 初始化与延时:确保在使能EEPROM时钟后,等待了足够的时间(至少3个系统时钟,通常建议延时更多,如10个周期)再访问其寄存器。这是很多驱动库源码里会做的操作。
- 等待WORKING位清零:在执行任何EEPROM操作(包括读写寄存器配置)前,必须确保
EEDONE.WORKING位为0。任何在前一个操作未完成时发起的访问都可能导致失败。 - 检查块与偏移地址有效性:
- 读取
EESIZE寄存器,获取BLKCNT(块数量)和WORDCNT(总字数)。确保你设置的EEBLOCK值小于BLKCNT。 - 确保
EEOFFSET值在0到15之间(因为每个块16个字)。 - 注意,通过
EEHIDE寄存器隐藏的块(块0除外)也是无效的,访问它们会导致EEBLOCK被硬件重置为0。
- 读取
- 写保护检查:EEPROM支持块级别的写保护。通过
EEPROT、EEPASS0/1/2寄存器进行保护设置。如果你试图向一个被保护的块写入数据,操作会失败,并置位EEDONE.ERROR。你需要使用正确的密码来解锁或确认该块是否允许写入。 - 操作间隔与寿命:EEPROM有写入次数寿命(通常为10万到100万次)。虽然TM4C的EEPROM模块内部有磨损均衡算法,但过于频繁的写入(例如在高速循环中不断写同一位置)仍会加速其损坏。设计上应避免频繁写入,可以考虑使用RAM缓存,定期批量写入的策略。
4.3 中断相关调试技巧
问题现象:配置了中断,但中断服务程序从未被调用;或者中断服务程序被反复调用,无法退出。
排查步骤与思路:
- 中断使能链检查:这是一个经典的“三层使能”问题。
- 第一层(外设级):
FCIM寄存器中对应的屏蔽位(如PMASK)是否置1?这是源头。 - 第二层(NVIC级):CPU的嵌套向量中断控制器(NVIC)中,Flash控制器的中断是否使能?通常使用
IntEnable(INT_FLASH)(TivaWare)或直接操作NVIC_ISER寄存器。 - 第三层(全局级):CPU的全局中断是否打开?在Cortex-M中,通过
__enable_irq()或设置PRIMASK/FAULTMASK寄存器来控制。
- 第一层(外设级):
- 中断标志清除:在中断服务程序中,是否清除了
FCMISC寄存器中对应的中断标志?如果没清除,硬件会认为中断一直未处理,从而导致中断持续触发。清除方法是向FCMISC的对应位写1。 - 中断优先级:检查Flash中断的优先级是否被其他更高优先级的中断长时间屏蔽。可以通过设置一个合适的优先级来避免。
- 向量表与函数名:确保中断向量表正确指向你的中断服务函数,并且函数名与启动文件中的弱定义一致(例如,对于TM4C123,Flash中断服务程序通常命名为
Flash0_Handler或IntFlashHandler,具体取决于你的开发环境)。
避坑技巧:在调试中断问题时,一个非常有效的方法是在中断服务程序入口处设置一个GPIO引脚翻转。用示波器或逻辑分析仪观察这个引脚,可以直观地判断中断是否被触发、触发频率如何,从而快速定位是“不进中断”还是“出不中断”的问题。
5. 高级应用与配置建议
掌握了基础操作和问题排查后,我们可以探讨一些更深入的应用场景和优化配置,让你的代码更加健壮和高效。
5.1 实现安全的固件在线升级(IAP)
固件在线升级是Flash操作最典型的应用之一。其核心是在Flash中划分出两个或多个区域:引导程序区(Bootloader)和多个应用程序区(App)。安全是关键中的关键。
设计要点与寄存器应用:
- 扇区保护(FMPPEn寄存器):利用Flash存储器保护编程使能寄存器,对Bootloader所在的扇区进行写保护,防止应用程序跑飞后意外擦写Bootloader,导致系统“变砖”。在Bootloader启动时解锁自身扇区进行更新,更新完成后立即重新上锁。
- 完整性校验与回滚:在新固件编程完成后,不要立即跳转。应读取整个新固件区域,计算CRC或哈希值,与预期的校验和对比。只有校验通过,才更新一个在EEPROM或另一个受保护的Flash扇区中存储的“有效标志”,然后复位跳转。如果校验失败,则保持旧固件标志不变,实现回滚。
- 中断与状态管理:在Bootloader进行大块数据写入时,使用编程完成中断(
PMASK)而非轮询,可以节省CPU资源。同时,务必启用错误中断(如ERMASK),一旦发生编程错误(如电压不稳),能立即进入错误处理流程,记录日志或尝试恢复。 - 双缓冲区(Double Buffer)策略:这是高级Bootloader的常用技巧。将Flash划分为A、B两个应用程序区。系统从A区运行,新固件下载到B区。B区编程验证成功后,更新引导标志指向B区,然后复位。下次启动就从B区运行。这样即使B区升级失败,A区仍然是完好的可运行版本。这需要精细地管理
FMA和FWBn,在A区和B区之间切换编程目标。
5.2 EEPROM模拟与数据管理
尽管TM4C123自带EEPROM,但在某些型号没有EEPROM或EEPROM空间不足时,我们可能需要用Flash来模拟EEPROM。同时,对于自带的EEPROM,良好的数据管理策略也至关重要。
用Flash模拟EEPROM的挑战:
- 擦除单位大:Flash擦除以扇区(如1KB)为单位,而EEPROM可以按字修改。模拟时需要实现一个“日志式”或“扇区轮转”的软件层。
- 寿命管理:Flash的擦写寿命(约1万次)远低于EEPROM。模拟算法需要包含磨损均衡,确保所有扇区被均匀使用。
- 关键寄存器操作:你需要频繁使用
FMA、FWBn、FMC2进行编程,使用FMC进行扇区擦除。必须妥善处理INVDRIS错误(尝试写0到1),这意味着在写入新数据前,必须判断目标地址是否已为0xFF,否则需触发擦除。
自带EEPROM的最佳实践:
- 数据结构化:不要将变量直接散乱地存储。定义一个配置参数结构体,将所有需要保存的参数打包。存储时,将整个结构体连续写入EEPROM的一个块中。读取时,一次性读回整个结构体。这简化了地址管理。
- 版本与默认值:在存储的数据结构开头,包含一个“数据版本”字段和“CRC校验”字段。每次上电读取配置后,先校验CRC。如果校验失败或版本不匹配,则使用程序内定义的默认值初始化数据结构,并写回EEPROM。这可以防止数据损坏导致系统异常。
- 减少写操作:在
EEBLOCK和EEOFFSET确定后,如果需要修改同一块内的多个字,使用EERDWRINC寄存器进行连续写入是最有效率的方式,因为它减少了重复设置地址的开销。对于不频繁更改的数据,可以考虑在RAM中缓存,每隔一段时间或特定事件(如关机)时再同步到EEPROM。
5.3 低功耗模式下的考量
在电池供电的物联网设备中,微控制器经常进入深度睡眠模式以省电。Flash/EEPROM操作与低功耗模式之间存在一些需要注意的交互。
注意事项:
- ��作完成前勿休眠:在发起Flash编程或擦除、EEPROM写入操作后,必须等待操作完成(
PRIS置位或EEDONE.WORKING清零),才能让芯片进入深度睡眠模式。因为在这些低功耗模式下,Flash控制器的时钟可能被关闭或大幅降频,导致操作失败甚至损坏数据。 - 唤醒后的恢复:从深度睡眠唤醒后,外设时钟可能需要重新使能。虽然Flash控制器时钟可能保持使能,但为了安全起见,在计划进行Flash/EEPROM操作前,最好重新执行一遍初始化流程:使能时钟、短暂延时、检查就绪状态。
- 中断唤醒:你可以配置Flash编程完成中断或EEPROM操作完成中断,将芯片从睡眠模式唤醒。这在需要将数据存入非易失性存储器后再进入深度睡眠的场景下非常有用。确保中断优先级和唤醒配置正确。
6. 寄存器速查与配置模板
为了方便日常开发快速参考,我将核心寄存器的关键信息、常用配置值整理成表格,并提供一个基础的Flash写入函数模板。
6.1 关键寄存器位定义速查表
| 寄存器名称 | 地址偏移 | 关键位/域 | 类型 | 功能描述 | 常用配置值/操作 |
|---|---|---|---|---|---|
| FCIM | 0x010 | PMASK (位13) | R/W | 编程验证错误中断屏蔽 | 1: 使能中断 |
| ERMASK (位11) | R/W | 一般错误中断屏蔽 | 1: 使能中断(推荐) | ||
| INVDMASK (位10) | R/W | 无效数据中断屏蔽 | 1: 使能中断 | ||
| VOLTMASK (位9) | R/W | 电压错误中断屏蔽 | 根据电源稳定性选择 | ||
| EMASK (位2) | R/W | EEPROM中断屏蔽 | 使用EEPROM时设为1 | ||
| PMASK (位1) | R/W | 编程完成中断屏蔽 | 1: 使能中断 | ||
| AMASK (位0) | R/W | 访问违规中断屏蔽 | 调试保护时设为1 | ||
| FCMISC | 0x014 | PROGMISC (位13) | R/W1C | 编程验证错误状态/清除 | ISR中写1清除 |
| ERMISC (位11) | R/W1C | 一般错误状态/清除 | ISR中写1清除 | ||
| INVDMISC (位10) | R/W1C | 无效数据状态/清除 | ISR中写1清除 | ||
| VOLTMISC (位9) | R/W1C | 电压错误状态/清除 | ISR中写1清除 | ||
| EMISC (位2) | R/W1C | EEPROM状态/清除 | ISR中写1清除 | ||
| PMISC (位1) | R/W1C | 编程完成状态/清除 | ISR中写1清除 | ||
| AMISC (位0) | R/W1C | 访问违规状态/清除 | ISR中写1清除 | ||
| FMC2 | 0x020 | WRKEY (位31:16) | WO | 写操作密钥 | 0xA442 或 0x71D5 |
| WRBUF (位0) | R/W | 启动缓冲写操作 | 与WRKEY一同写1启动 | ||
| FWBVAL | 0x030 | FWB[n] (位31:0) | R/W | 写缓冲器有效标志 | 写入数据后手动置1 |
| EESIZE | 0x000 | BLKCNT (位26:16) | RO | EEPROM块数量 | 读取以确定最大块号 |
| WORDCNT (位15:0) | RO | EEPROM总字数 | 读取以确定总容量 | ||
| EEBLOCK | 0x004 | BLOCK (位15:0) | R/W | 当前选择的EEPROM块 | 设置为0~(BLKCNT-1) |
| EEOFFSET | 0x008 | OFFSET (位3:0) | R/W | 当前块内的字偏移 | 设置为0~15 |
| EERDWR | 0x010 | VALUE (位31:0) | R/W | EEPROM读写数据 | 读写操作的目标数据 |
6.2 Flash单字编程函数模板(轮询方式)
这是一个经过实践检验的、健壮性较高的基础Flash写入函数,包含了基本的错误检查。
/** * @brief 向指定Flash地址写入一个32位字(轮询方式)。 * @param ui32Address: 目标地址,必须字对齐(4字节倍数)。 * @param ui32Data: 要写入的数据。 * @return 0 成功,非0 错误码。 */ int32_t Flash_ProgramWord(uint32_t ui32Address, uint32_t ui32Data) { // 1. 检查地址对齐 if(ui32Address & 0x3) { return -1; // 错误码:地址未对齐 } // 2. 检查地址是否在有效Flash范围内(示例:256KB) if(ui32Address >= 0x00040000) { // 假设Flash结束地址为0x0003FFFF return -2; // 错误码:地址越界 } // 3. 等待Flash控制器就绪(检查FMC.WRKEY是否为非0,表示就绪) // 注意:这是一个简化的检查,更严谨应检查FMC寄存器状态。 while((HWREG(FLASH_FMC) & FLASH_FMC_WRKEY_M) == 0) { // 可选:增加超时机制,避免死循环 } // 4. 设置目标地址 HWREG(FLASH_FMA) = ui32Address; // 5. 写入数据到缓冲器0 HWREG(FLASH_FWB0) = ui32Data; // 6. 标记缓冲器0数据有效 HWREG(FLASH_FWBVAL) = 0x00000001; // 注意:这里是赋值,不是或操作,确保只写一个字。 // 7. 触发编程操作(假设写密钥为0xA442) HWREG(FLASH_FMC2) = (0xA442 << 16) | FLASH_FMC2_WRBUF; // 8. 轮询等待操作完成 while(!(HWREG(FLASH_FCRIS) & FLASH_FCRIS_PRIS)) { // 可选:增加超时机制 // 可选:检查是否有错误发生 if(HWREG(FLASH_FCRIS) & (FLASH_FCRIS_INVDRIS | FLASH_FCRIS_VOLTRIS | FLASH_FCRIS_ERRIS)) { // 发生错误,清除错误状态并返回错误码 HWREG(FLASH_FCMISC) = (FLASH_FCMISC_INVDMISC | FLASH_FCMISC_VOLTMISC | FLASH_FCMISC_ERMISC); return -3; // 错误码:编程过程错误 } } // 9. 清除编程完成状态 HWREG(FLASH_FCMISC) = FLASH_FCMISC_PMISC; // 10. 可选:验证写入的数据 if(HWREG(ui32Address) != ui32Data) { return -4; // 错误码:验证失败 } return 0; // 成功 }使用这个模板时,你需要根据自己芯片的实际情况调整Flash大小、写密钥值,并强烈建议添加超时机制,防止在异常情况下函数永不返回。对于更复杂的多字编程或扇区擦除,可以在此基础上进行扩展,核心的寄存器操作逻辑是相通的。记住,操作Flash时谨慎总是没错的,尤其是在产品代码中,充分的错误检查和恢复机制是稳定性的基石。