深入解析Tiva™ C系列Flash内存:配置、预取与保护机制实战
1. 项目概述:为什么我们需要深入理解Flash内存?
在嵌入式开发领域,尤其是基于ARM Cortex-M内核的微控制器项目里,Flash内存是我们每天都要打交道的“老朋友”。它静静地躺在芯片里,存储着我们辛辛苦苦写出来的程序代码和那些至关重要的常量数据。但很多时候,我们只是把它当作一个简单的、掉电不丢数据的“硬盘”来用,烧录完程序就很少再去关心它了。直到某一天,你发现系统在120MHz主频下跑起来偶尔会卡顿,或者你想保护一段核心算法代码不被轻易读取,又或者需要实现固件的在线升级(OTA)时,才会意识到:原来Flash内存的学问这么深。
Tiva™ C系列微控制器(现在多归属于TI的TM4C系列)作为工业控制和物联网领域的热门选择,其Flash子系统设计得相当精巧。它不仅仅是存储介质,更是一个包含预取缓冲、地址交错访问、多种保护机制和灵活配置选项的复杂子系统。理解它,意味着你能榨干芯片的每一分性能,构建出更稳定、更安全、更高效的系统。简单来说,搞懂Flash内存,是从“能用”到“用好”嵌入式微控制器的关键一步。
本文将以Tiva™ C系列为蓝本,拆解Flash内存的三大核心:配置、预取与保护。我不会照本宣科地复述数据手册,而是结合我过去在电机控制、通信网关等实际项目中的踩坑经验,告诉你这些寄存器配置背后的“为什么”,分享那些数据手册里不会写的调试技巧和注意事项。无论你是正在优化产品性能的资深工程师,还是刚开始接触TM4C系列的新手,相信都能从中找到可以直接“抄作业”的干货。
2. Flash内存的物理架构与配置逻辑
要驾驭Flash,首先得知道它的“物理地图”和“交通规则”。Tiva™ C系列的Flash内存设计,核心目标是解决一个矛盾:CPU速度越来越快,但Flash的读取速度相对较慢。为了解决这个矛盾,TI采用了一套组合拳。
2.1 交错式存储架构:化串行为并行
根据数据手册,以TM4C129x为例,其1MB的Flash被组织成4个Bank(存储体),每个Bank容量为256KB。关键之处在于,这4个Bank是两两交错(Two-way Interleaved)的。
你可以把它想象成一条双向四车道的高速公路。Bank 0和Bank 1构成“低区”512KB,Bank 2和Bank 3构成“高区”512KB。在每个区内,两个Bank是并行工作的。当CPU需要读取一个256位(32字节)的数据块时,内存控制器会同时从两个Bank中各读取128位,然后拼合成一个完整的256位数据块,通过128位宽的总线一次性送给预取缓冲区。
这种交错访问的好处是显而易见的:它极大地提高了数据吞吐的带宽。对于顺序执行的代码,这种架构能实现接近零等待状态的读取效率。但这也带来一个重要的操作特性:擦除操作的最小单位是16KB。因为一次擦除会同时作用于一个区内的两个Bank的对应8KB扇区,所以实际擦除的是两个8KB扇区,总计16KB。在编写Flash驱动时,这一点必须牢记。
2.2 时序配置(MEMTIM0):让Flash跟上CPU的脚步
Flash内存单元完成一次读取或写入操作需要一定的时间,这个时间通常比CPU时钟周期长。如果CPU以很高的频率直接访问Flash,就会因为Flash“反应不过来”而插入等待周期,导致性能下降。因此,我们必须根据CPU的工作频率,手动配置Flash的访问时序。
这个配置的核心寄存器是MEMTIM0 (Memory Timing Parameter Register 0)。它主要控制三个参数,我们需要根据CPU频率查表配置:
| CPU频率范围 (MHz) | 周期时间范围 (ns) | 等待状态 (FWS) | 时钟边沿 (FBCE) | 时钟高电平时间 (FBCHT) |
|---|---|---|---|---|
| ≤ 16 | ≥ 62.5 | 0x0 | 1 | 0x0 |
| 16 < f ≤ 40 | 62.5 > t ≥ 25 | 0x1 | 0 | 0x2 |
| 40 < f ≤ 60 | 25 > t ≥ 16.67 | 0x2 | 0 | 0x3 |
| 60 < f ≤ 80 | 16.67 > t ≥ 12.5 | 0x3 | 0 | 0x4 |
| 80 < f ≤ 100 | 12.5 > t ≥ 10 | 0x4 | 0 | 0x5 |
| 100 < f ≤ 120 | 10 > t ≥ 8.33 | 0x5 | 0 | 0x6 |
参数解读与配置实操:
- 等待状态 (FWS):这是最重要的参数。它定义了在Flash数据准备好之前,CPU需要插入多少个额外的时钟周期进行等待。频率越高,所需的等待状态越多。例如,在120MHz下,需要配置5个等待状态(0x5)。
- 时钟高电平时间 (FBCHT):这个参数控制Flash内部时钟的高电平持续时间,与Flash工艺相关,通常跟随FWS一起根据表格配置即可。
- 时钟边沿 (FBCE):仅在16MHz及以下频率时设置为1(使用上升沿和下降沿),其他频率设置为0(仅使用上升沿)。这有助于在低频下优化时序。
配置代码示例与关键步骤:
#include <stdint.h> #include “inc/tm4c1294ncpdt.h” // 假设使用TM4C1294 void FlashTimingConfig(uint32_t sysClockFreq) { uint32_t memtim0Value; // 根据CPU频率计算MEMTIM0值 if (sysClockFreq <= 16000000) { memtim0Value = (0x0 << 12) | (1 << 10) | (0x0 << 8); // FWS=0, FBCE=1, FBCHT=0 } else if (sysClockFreq <= 40000000) { memtim0Value = (0x1 << 12) | (0 << 10) | (0x2 << 8); // FWS=1, FBCE=0, FBCHT=2 } else if (sysClockFreq <= 60000000) { memtim0Value = (0x2 << 12) | (0 << 10) | (0x3 << 8); // FWS=2, FBCE=0, FBCHT=3 } else if (sysClockFreq <= 80000000) { memtim0Value = (0x3 << 12) | (0 << 10) | (0x4 << 8); // FWS=3, FBCE=0, FBCHT=4 } else if (sysClockFreq <= 100000000) { memtim0Value = (0x4 << 12) | (0 << 10) | (0x5 << 8); // FWS=4, FBCE=0, FBCHT=5 } else { // 100 < f <= 120 MHz memtim0Value = (0x5 << 12) | (0 << 10) | (0x6 << 8); // FWS=5, FBCE=0, FBCHT=6 } // 注意:EEPROM等待状态(EWS)必须与Flash等待状态(FWS)设置为相同的值 memtim0Value |= ((memtim0Value >> 12) & 0xF); // 将FWS的值复制给EWS位域 // 更新MEMTIM0前,必须设置RSCLKCFG寄存器中的MEMTIMU位 SYSCTL->RSCLKCFG |= SYSCTL_RSCLKCFG_MEMTIMU; // 写入新的时序配置 SYSCTL->MEMTIM0 = memtim0Value; }关键提示:在修改
MEMTIM0寄存器之前,必须先将RSCLKCFG寄存器中的MEMTIMU位置1。这个位是一个“更新使能”位,目的是防止运行时误操作导致Flash访问时序错乱,引发系统崩溃。这是一个非常容易忽略但至关重要的安全步骤。
一个真实的坑:我曾经在调试一个80MHz的系统时,发现偶尔会有指令取指错误,导致程序跑飞。排查了很久,最后发现是系统初始化代码中,在PLL锁相环配置完成、系统时钟切换到高频后,忘记调用FlashTimingConfig函数来更新MEMTIM0。CPU跑在80MHz,却还用着16MHz时的时序配置,Flash访问必然出错。所以,务必在系统时钟升频后,立即重新配置Flash时序。
3. 预取缓冲区:消除性能瓶颈的智能缓存
即使配置了正确的等待状态,每次取指都访问Flash也会带来延迟。预取缓冲区就是为了解决这个问题而生的“智能缓存”。它的工作原理是:预测CPU接下来可能需要什么指令,并提前从Flash中读取出来,存到缓冲区里。
3.1 缓冲区的工作模式:四缓冲与双缓冲
Tiva™ C系列的预取缓冲区有两种配置模式,由FLASHCONF寄存器中的SPFE位控制:
- 四缓冲区模式(默认,SPFE=0):这是4个独立的256位缓冲区。控制器采用“最近最少使用”(LRU)算法管理它们。当CPU需要的数据不在缓冲区中(即“未命中”)时,控制器会从Flash中读取一个256位的数��行,填充到那个“最近最少使用”的缓冲区里。这种模式能很好地适应带有分支和跳转的复杂代码流,缓存命中率高,是绝大多数应用场景下的推荐配置。
- 双缓冲区模式(SPFE=1):只有2个256位缓冲区,以确定性的乒乓模式工作。这种模式牺牲了缓存灵活性,但带来了确定性的访问时序。这意味着,你可以精确计算出任何一段代码执行所需的时钟周期数,不受缓存命中/未命中的影响。这种模式仅用于对实时性要求极端苛刻、需要严格周期确定性的场景,例如某些数字信号处理(DSP)控制循环。
如何选择?除非你在做电机FOC控制这类对中断响应时间有纳秒级要求的应用,并且已经将关键循环代码精心安排到连续地址并做了对齐优化,否则请老老实实使用默认的四缓冲区模式。确定性模式的性能在一般代码下通常更差。
3.2 预取过程深度解析与优化技巧
让我们结合数据手册的流程图,看看一次完整的“未命中-填充-命中”过程:
- 未命中(Miss):CPU请求的指令地址不在任何一个缓冲区的标签(Tag)范围内。
- 发起填充:内存控制器立即向Flash子系统发起一个256位的读取请求。得益于交错架构,这个请求会同时从两个Bank读取数据。
- 等待与填充:根据
MEMTIM0设置的等待状态,经过若干周期后,256位数据被取回,并填充到LRU缓冲区中,同时更新该缓冲区的标签。 - 数据送达:目标指令字(32位)在写入缓冲区的下一个周期就被送到CPU,此时可能仍需等待(如果该字不是256位行的第一个字)。之后,CPU顺序访问该行内的其他指令字,都是零等待状态。
- 预取下一行:一个聪明的设计是,当CPU访问到当前缓冲行的Word 3(即第4个32位字)时,控制器就会提前发起对下一行指令的预取请求。这样,当CPU执行完当前行的Word 7时,下一行数据很可能已经在缓冲区里准备好了,从而实现流水线式的连续执行,最大化隐藏Flash访问延迟。
基于此机制的优化技巧:
- 代码对齐:数据手册明确建议,为了获得最优的预取性能,应尽量将关键循环或函数的入口地址对齐到8字边界(即32字节对齐)。这样能确保每次循环开始都从一个新行的起始处读取,最大化预取效率。在链接器脚本(
.ld文件)中,可以使用ALIGN(32)来约束关键段的起始地址。 - 减少字面量(Literals):编译器经常将常量(如
const table[])直接放在代码段(.text)中。CPU用LDR指令加载这些常量时,会产生一次数据总线(DCode)访问。如果这段代码所在的Flash区域被设置为“仅执行”保护(后面会讲),这次访问会被禁止,导致程序错误。更糟糕的是,即使没有保护,这种穿插在代码中的常量访问也会打断指令流的连续性,造成预取缓冲区失效,带来性能惩罚。因此,在编译时,应使用-mliteral-pool等选项,或者手动将常量数据集中放置到单独的、未受保护的数据段中。 - 理解“20MHz现象”:数据手册提到一个有趣的点:从16MHz提升到20MHz,系统性能提升,但功耗可能反而有优化。这是因为在16MHz时,预取缓冲区可能并未被充分激活或效率较低,而在20MHz以上,预取机制开始高效运行,虽然频率高了,但单位任务完成的时钟周期数可能减少更多,使得CPU更快进入休眠状态,从而降低平均功耗。
3.3 缓冲区的管理与失效
预取缓冲区并非一直有效,在某些操作后,其内容会失效,标签会被清除:
- 系统复位。
- 对
FLASHCONF寄存器的修改(如切换SPFE模式、手动关闭缓冲区)。 - 设置
FLASHCONF寄存器中的CLRTV位(手动清除标签)。 - 发生ROM访问、总线错误或系统中止。
- 镜像模式切换时(非常重要!)。
手动管理场景:当你通过DMA或软件在后台修改了Flash内容(例如固件更新),然后要跳转回Flash执行新代码时,必须在跳转前手动清除预取缓冲区标签(设置CLRTV位)。否则,CPU可能还在执行缓冲区里旧的、已被修改的指令副本,导致不可预知的行为。这是一个在实现OTA功能时极易忽略的致命细节。
4. Flash内存保护机制:构筑代码安全的防火墙
对于商业产品,保护知识产权和防止固件被恶意篡改至关重要。Tiva™ C系列提供了一套粒度可调的硬件保护机制。
4.1 保护策略寄存器:FMPPE与FMPRE
保护机制的核心是两组寄存器:FMPPEn(保护编程/擦除)和FMPREn(保护读取/执行)。它们以2KB为粒度控制Flash的访问权限。
- FMPPEn (Flash Memory Protection Program Enable):某位为1,表示对应的2KB块允许编程和擦除;为0,则表示禁止。需要注意的是,要实现“仅执行”保护,必须以16KB为单位进行设置,即需要连续清除8个2KB块对应的
FMPPEn位(即一个字节的所有位)。 - FMPREn (Flash Memory Protection Read Enable):某位为1,表示对应的2KB块允许被读取(作为数据)和执行(作为代码);为0,则表示禁止读取,但允许执行(如果同时
FMPPEn对应位也为0)。
通过组合这两者的值,我们可以实现四种保护策略:
| 保护策略 | FMPREn | FMPPEn | 说明与应用场景 |
|---|---|---|---|
| 仅执行 (Execute-Only) | 0 | 0 | 代码只能被CPU执行,无法通过调试器或软件(如memcpy)读取其内容。用于保护核心算法。 |
| 禁止读取 (No Read) | 0 | 1 | 可写、可擦除、可执行,但不能读。此组合极少使用。 |
| 只读 (Read-Only) | 1 | 0 | 代码可读、可执行,但不可写、不可擦除。用于保护已稳定的引导程序或库函数,防止误修改。 |
| 无保护 (No Protection) | 1 | 1 | 完全开放访问。出厂默认状态。 |
4.2 “仅执行”保护的陷阱与解决方案
“仅执行”保护听起来很完美,但它有一个巨大的陷阱,直接关系到程序能否正常运行:字面量(Literal Data)问题。
在C语言中,像const uint32_t my_const = 0x12345678;这样的全局常量,或者大型的立即数,编译器通常会将其放入代码段(.text),与指令混在一起。当CPU执行到LDR R0, =my_const这样的指令时,它实际上是通过程序计数器(PC)相对寻址,发起一次数据读取(DCode总线)来获取这个常量的值。如果这段代码所在的Flash块被设置为“仅执行”(FMPREn=0),那么这次数据读取就会被硬件禁止,引发总线错误(HardFault)!
解决方案有三种,需要你在编译和链接阶段处理:
- 分离字面量池(推荐):使用编译器选项,强制将所有常量收集到一个独立的“字面量池”段中。然后,在链接器脚本里,将这个段放置在一个仅设置“只读”保护(FMPREn=1, FMPPEn=0)的Flash区域。同时,需要确保
LDR指令的PC相对偏移量能够到达这个池子,或者改用寄存器基址寻址。- GCC编译器示例:使用
-msingle-pic-base或-mpic-data-is-text-relative等选项,并配合自定义的链接脚本,将.rodata段或特定的字面量段放到可读区域。
- GCC编译器示例:使用
- 使用立即数构造:对于简单的常量,鼓励编译器使用
MOV指令加立即数的方式,或者通过算术指令组合生成,避免产生内存访问。这通常需要比较“聪明”的编译器优化(如-O2及以上),并且只适用于较小的常数。 - 汇编语言手动管理:如果编译器不支持上述高级功能,你只能在汇编层面,手动将常量数据定义在可读的区域,并通过绝对地址或特定的基址寄存器来访问。
实操建议:在规划Flash布局时,就要提前设计好。例如,将Flash前16KB作为“仅执行”区,存放最核心的加密算法;紧接着的16KB作为“只读”区,存放算法所需的常量表和字面量;其余区域作为“无保护”区,存放应用程序和可更新数据。这需要在链接脚本(.ld)中精确定义各个段的地址范围。
4.3 保护寄存器的编程与“提交”
修改FMPREn和FMPPEn寄存器并不能立即生效。它们属于“Flash常驻寄存器”,修改后需要执行一个“提交”(Commit)操作,才能将设置永久保存到非易失性存储器中。
编程流程如下:
- 写入目标值:直接对
FMPREn或FMPPEn寄存器赋值。注意,这些寄存器只能将位从1变成0,不能从0变回1(除非通过后门序列全擦除)。 - 提交操作:通过
Flash Memory Control (FMC)寄存器进行提交。- 将目标寄存器的特定地址写入
FMA寄存器。 - 将
FMC寄存器中的WRKEY(写密钥,通常为0xA442)和COMT位一起置位(即写入0xA442.0008)。 - 轮询
FMC寄存器,直到COMT位被硬件清除,表示提交完成。
- 将目标寄存器的特定地址写入
重要警告:
- 提交过程不可断电:在提交操作进行期间,如果发生断电,可能导致寄存器处于不确定状态,部分保护功能失效。产品设计需考虑电源稳定性。
- BOOTCFG寄存器特殊:包含调试接口禁用(
DBG0/DBG1)等关键配置的BOOTCFG寄存器,其提交需要一次上电复位(POR)后才能生效。而且,它的配置无法在提交前测试,一旦提交(尤其是禁用调试),如果代码有问题,芯片可能“变砖”,只能通过特定的恢复序列(见数据手册“Recovering a ‘Locked’ Microcontroller”章节)来解锁,这个序列通常会触发整片Flash的擦除。 - 永久性:提交后的保护设置是永久的,直到你执行一次“Mass Erase”(通常与解锁序列绑定)才能恢复。设置保护前务必三思,并确保你的引导程序(Bootloader)有办法在保护生效后更新其他区域。
5. 高级功能:镜像模式与DMA访问
5.1 镜像模式:实现无缝固件升级
镜像模式是Tiva™ C系列一个非常强大的功能,它允许你将512KB的“高区”Flash作为“低区”的镜像。你可以这样利用它:
- 系统正常运行在“低区”的应用程序A。
- 通过通信接口(如UART、以太网)将新的应用程序B下载到“高区”。
- 校验无误后,设置
FLASHCONF寄存器中的FMME位。 - 硬件会立即进行地址重映射,将原本指向“低区”的访问,转到“高区”。这个过程是“热交换”的,CPU无需复位,指令流无缝衔接。
- 现在,系统运行的是“高区”的应用程序B,而“低区”的旧程序A可以被擦除,用于存储下一次升级的程序C。
实现关键点:
- 引导程序必须镜像:两个512KB区域的开头都必须有相同的引导程序代码,因为交换后,CPU是从相同的逻辑地址(低区地址)开始取指的。
- 代码地址必须一致:应用程序B在编译链接时,其链接地址必须与应用程序A在“低区”时完全相同,因为交换的是物理存储介质,而不是逻辑地址。
- 操作物理地址:交换后,当你需要擦除或编程“低区”的物理空间时,你必须使用它原本的“高区”物理地址。例如,逻辑地址
0x0003FE8在交换后对应的是物理Bank 2/3的区域,你要擦除这个逻辑位置的内容,实际需要操作地址0x0803FE8。 - 清除预取缓冲区:在执行热交换(设置
FMME)后、执行新代码前,必须设置CLRTV位清除预取缓冲区标签,否则CPU可能执行缓存的旧代码。
5.2 µDMA访问Flash
微直接存储器访问(µDMA)控制器可以直接从Flash读取数据,减轻CPU负担。但它的访问受到严格限制:
- 仅限运行模式:在睡眠、深度睡眠等低功耗模式下,µDMA无法访问Flash。
- 可配置访问窗口:通过
FLASHDMAST和FLASHDMASZ寄存器,你可以定义一个连续的Flash区域(2KB对齐)允许µDMA访问。这提供了一个额外的安全层,即使某块Flash是可读的,如果不在DMA允许的窗口内,DMA访问也会触发总线错误。 - 受保护策略约束:µDMA的访问同样受到
FMPREn寄存器策略的限制。如果目标区域是“仅执行”的,DMA读取也会被禁止。
这个功能常用于需要将Flash中的大量数据(如图形资源、字体库、音频样本)快速搬运到SRAM或外设(如LCD控制器、DAC)的场景。合理设置DMA访问窗口,可以在提供便利的同时,不削弱整体的代码保护强度。
6. Flash编程实操与常见问题排查
理解了原理,最终要落到操作上。Tiva™ C系列的Flash编程接口相对简洁,主要涉及三个寄存器:FMA(地址)、FMD(数据)、FMC(控制)。
6.1 基本编程与擦除流程
编程一个32位字:
- 将数据写入
FMD寄存器。 - 将目标地址(必须4字节对齐)写入
FMA寄存器。 - 向
FMC寄存器写入密钥和WRITE命令(0xA442.0001)。 - 轮询
FMC寄存器的WRITE位,直到硬件将其清除,表示操作完成。
擦除一个16KB扇区:
- 将一个16KB对齐的地址(即地址的低14位为0)写入
FMA寄存器。擦除的是包含该地址的整个扇区。 - 向
FMC寄存器写入密钥和ERASE命令(0xA442.0002)。 - 轮询
ERASE位直到完成,或使能编程完成中断(PMASK)来异步通知。
批量擦除(Mass Erase):
- 向
FMC寄存器写入密钥和MERASE命令(0xA442.0004)。 - 轮询
MERASE位直到完成。警告:此操作会擦除整个主Flash阵列,慎用!
6.2 32字写缓冲区加速
对于需要连续写入多个字的情况,使用32字写缓冲区可以大幅提升效率。其原理是将最多32个字(128字节)的数据先缓存在FWBn寄存器组中,然后一次性编程到Flash。
使用流程:
- 向
FWB0到FWB31寄存器写入数据。只有被写入的寄存器对应的FWBVAL位才会被置位。 - 将一个128字节对齐的地址(地址低7位为0)写入
FMA。 - 向
FMC2寄存器写入密钥和WRBUF命令(0xA442.0001)。 - 硬件会自动将
FWBVAL中标记为有效的所有32位字,编程到以FMA为起始的连续Flash地址中。耗时与编程16个单独的字相当,效率提升近一倍。
6.3 常见问题与排查实录
在实际开发中,Flash操作出错是家常便饭。下面是我总结的一些典型问题及排查思路:
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| 编程/擦除操作失败,FMC寄存器命令位不清除 | 1. 目标地址未对齐(编程需4字节,擦除需16KB)。 2. 目标区域受 FMPPEn保护。3. 在编程/擦除期间,尝试读取同一Bank的代码(导致总线冲突)。 4. Flash电压泵异常( VOLTRIS中断触发)。 | 1. 检查FMA地址是否符合对齐要求。2. 检查 FMPPEn寄存器对应位是否为1(允许编程)。3.确保执行Flash操作的代码位于SRAM中。这是最容易被忽略的一点!在Flash上执行擦写自身所在Bank的代码,会导致死锁。 4. 检查电源电压是否稳定,特别是VDDCORE。查看 FCRIS寄存器是否有电压错误标志。 |
| 系统在Flash操作后跑飞或HardFault | 1. 预取缓冲区中残留了旧的、已被修改的指令。 2. 中断向量表在Flash操作期间被破坏。 3. 操作了非法地址(如ROM区)。 | 1. 在Flash操作完成并跳转回Flash执行前,设置FLASHCONF.CLRTV位。2. 将中断向量表重定位到SRAM,或者在Flash操作期间全局禁用中断。 3. 确认操作的地址在用户可用的Flash地址范围内。 |
| “仅执行”保护开启后,程序在访问常量时触发HardFault | 字面量(常量)被放在“仅执行”区域,CPU无法通过DCode总线读取。 | 1. 使用编译选项将常量分离到独立的段。 2. 修改链接脚本,将该段链接到“只读”保护或“无保护”的Flash区域。 3. 使用 -fno-common等选项,避免编译器将未初始化的全局变量当作常量处理。 |
| 镜像模式切换后,程序行为异常 | 1. 新旧应用程序的链接地址不一致。 2. 切换后未清除预取缓冲区。 3. 试图擦写当前正在运行的镜像区域。 | 1. 确保两个镜像使用完全相同的链接地址(只是烧录到不同的物理位置)。 2. 切换后立即执行 FLASHCONF.CLRTV = 1。3. 更新非活动镜像。若要更新当前运行镜像,需先跳转到SRAM中的引导程序,由引导程序完成擦写。 |
| 调试接口被禁用后,无法再次连接 | BOOTCFG寄存器中的DBG0或DBG1位被提交为0,永久禁用了JTAG/SWD。 | 执行数据手册中描述的“恢复锁定微控制器”序列。注意:此操作通常会触发整片Flash的擦除,包括用户代码和所有保护设置,全部恢复出厂状态。 |
最后分享一个个人调试心得:在编写任何Flash操作函数(特别是擦写函数)时,我习惯将其放在一个单独的.c文件里,并在链接脚本中强制将这个文件的所有代码和用到的全局变量都放到SRAM中执行。这样可以彻底避免“代码在Flash A中运行,却要去擦写Flash A”的悖论情况。虽然牺牲了一点SRAM空间,但换来了极高的可靠性。对于TM4C1294这种有256KB以上SRAM的芯片来说,这点开销是完全可以接受的。
