|
大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是恩智浦经典LPC系列MCU内部Flash IAP驱动。, N) S1 L# e* y% Q
LPC 系列 MCU 是恩智浦公司于 2003 年开始推出的非常具有代表性的产品,距今已经有近 20 年的生命。按时间线演进来说,其主要分为三代:
: A2 ]4 R. [/ a, ?7 T- 元老:基于 ARM7/9 内核的 LPC2000/3000 系列
8 A+ r/ @9 i }" F( k' a- 中坚:基于 Cortex-M0/0+/3/4 内核的 LPC800/1100/1200/1300/1500/1700/1800/4000/4300/540006 R4 m$ r5 d! \. B
- 新锐:基于 Cortex-M33 内核的 LPC5500 系列。
# X" t; i- I( m! o其中坚产品即是痞子衡今天要重点聊的经典 MCU,从其第一颗 LPC1800 到至今仍有新型号出来的 LPC800,仍然深受广大开发者喜爱。今天痞子衡想讨论的是内部 Flash 驱动这个对嵌入式软件开发者来说既冷门又不冷门的话题:5 U; N& a H% \+ T, q4 D( K
Note:本文内容主要以 LPC845 这个型号为例,未必完全适用其它经典 LPC 型号,具体需要查看相应手册。一、关于MCU内部Flash的基本概念痞子衡先解释下为什么内部 Flash 驱动这个话题既冷门又不冷门。说它冷门是因为大部分嵌入式软件开发工程师写的应用代码里很少包含 Flash 操作功能(除非应用需要 OTA 升级或者断电保存参数),因此对 Flash 模块的关注度不如其它外设模块。说它不冷门则是在 IDE 中调试或者编程器做量产又离不开 Flash 操作,所以避不可免地关注 Flash 擦写算法、性能、寿命、效率等。' P0 @7 D1 \! u- G, @3 v- b
话说回来,Flash 外设一般由两部分组成:Flash 控制器 + Flash Memory 介质,其 Memory 介质部分从原理上属于并行 NOR Flash,MCU 上电 Flash 外设总是使能的,可以通过 AHB 总线直接读取其映射空间内任意 Flash 地址处的数据/指令,所以其最主要的作用就是存储可执行代码。( T5 Z( C+ U) g! [
如果应用程序需要做 OTA 升级,则需要借助 Flash 控制器完成擦除和写入操作。这里就有一些概念性的东西出现了,比如 Flash 擦除正常是按 Block/Sector 为单元(不排除有些支持按 Page 擦除),并且擦除操作是将 Block/Sector 里全部 bit 从 0 恢复为 1。而 Flash 写入则是按 PUnit 为最小单元的(可能是 1/2/4/8 bytes),一次性最多写入一个 Page 的数据(这里指一次完整命令执行等待过程)。擦除和写入操作都不是立刻就完成的,需要等待 Memory 介质更新完成(读 Flash 控制器相应状态位寄存器)。
# E) o( ^/ L" ?* s r' x! uLPC845 内部 Flash 一共 64KB,划分为 64 个 Sector,每个 Sector 大小为 1KB。每个 Sector 包含 16 个 Page,每个 Page 大小为 64Bytes。支持按 Sector/Page 擦除,IAP 仅支持按 Page 写入(但是控制器底层最小写入单元是 4bytes),不支持 RWW 特性。) x7 X, e% e T; ]& u
64KB N/A N/A 1KB 64Bytes 4Bytes
" U. X6 `7 P0 S( F8 C2 j: qFlash Memory > Flash Bank >= Flash Block > Flash Sector > Flash Page >= Flash PUnit >= Flash Byte5 B @+ s8 P& I" f m
| | | | |
+ y, s K. w2 _0 a RWW单元 擦除单元 擦除单元 最大写入单元 最小写入单元% x' A. O. x% `+ I
关于 Flash 擦写操作,还有一个重要概念叫 Read-While-Write(简称 RWW),因为默认代码是执行在 Flash 里,如果我们这个时候还做 Flash 擦写操作,就会让同一个 Flash 处于又做擦写处理同时也要响应 AHB 总线来的读指令请求,大部分 Flash 是无法支持这个特性的,因此常见的操作是将触发 Flash 擦写命令以及读 Flash 状态的代码重定向到 RAM 里去执行。而 LPC 上不一样的 Flash IAP 驱动设计正是为了解决这个 RWW 限制的。* U! i8 V: o6 @6 ^8 ^
二、一般Flash驱动设计在讲 LPC Flash IAP 特色驱动之前,我们先来看看一般 MCU 上 Flash 驱动设计,就以恩智浦 Kinetis MK60DN512Z 系列为例。它的 Flash 外设是 FTFL (详见参考手册里 Chapter 28 Flash Memory Module (FTFL) 章节),Flash 大小为 512KB,分为两个 256KB Block (这里就相当于Bank),支持 RWW 特性(以 Block 为单元)。每个 Block 包含 128 个 Sector,每个 Sector 大小为 2KB。它其实没有明确的 Page 概念(但是最大写入单元是专用 4KB FLEXRAM 的一半,可以理解为 Page 大小就是 2KB),支持的最小写入单元是 4bytes。
; F: _/ p) ^( n; l, ^" y* x, ]6 b2 L 512KB 256KB 256KB 2KB 2KB 4Bytes$ ^1 q; |2 F: u! R* o( L
Flash Memory > Flash Bank >= Flash Block > Flash Sector >= Flash Page > Flash PUnit >= Flash Byte
3 n6 B: [ e* ?/ s | | | | |
0 i2 s: l9 P }+ g7 T. a RWW单元 擦除单元 擦除单元 最大写入单元 最小写入单元, Z# i: g6 g, J5 s$ J7 O
在官方驱动 \SDK_2_2_0_TWR-K60D100M\devices\MK60D10\drivers\fsl_flash.c 里我们重点关注如下 5 个基本函数,这些函数都是直接操作 FTFL 外设寄存器来完成相应 Flash 擦写功能的。其中 flash_command_sequence() 内部函数设计是核心,每一个 API 基本都会调用它,这里面有一个关于解决 RWW 限制的黑科技设计,后面痞子衡会写文章专门介绍。; k6 t1 r5 V6 {1 a" a1 h
// 一般初始化函数,主要是软件层面初始化
" n. ^# P$ u% f- j" s) q4 Kstatus_t FLASH_Init(flash_config_t *config);
3 A$ _& E6 W1 A// 为了解决 RWW 限制而特殊设计的命令触发执行函数, n8 h5 E' ~! |( m
status_t FLASH_PrepareExecuteInRamFunctions(flash_config_t *config);
) ?* C$ ^0 C5 G. C }! p* T. E+ Y2 x% cstatic status_t flash_command_sequence(flash_config_t *config)
' m0 y% {3 c! l// 擦除函数,长度不限(需要按 Sector 对齐),key 参数是为了降低误擦除风险
/ [- ^4 e. L) w3 i" C7 t. q; pstatus_t FLASH_Erase(flash_config_t *config, uint32_t start, uint32_t lengthInBytes, uint32_t key);
- U; E. J$ T. k// 写入函数,长度不限(仅最小写入单元对齐限制),函数内部自动结合 Page 和 PUnit 写入命令做处理
, _% x2 q( q& Wstatus_t FLASH_Program(flash_config_t *config, uint32_t start, uint32_t *src, uint32_t lengthInBytes);
! M! `1 D3 |/ H# c三、LPC Flash IAP驱动设计原理终于来到本文核心 - LPC Flash IAP 驱动了。按照我们一般经验,首先是翻看 LPC845 用户手册寻找 Flash 外设,但是很遗憾,用户手册里并没有 Flash 外设详细介绍,取而代之的是 Chapter 5: LPC84x ISP and IAP 章节。因为 LPC 全系列都包含 BootROM(映射地址为 0x0F00_0000 - 0x0F00_3FFF),而 BootROM 代码里包含了 Flash 擦写驱动,因此官方直接推荐用户调用 ROM 里的 Flash 驱动 API 来完成操作,而不是按照传统方式提供直接操作 Flash 外设寄存器的 SDK 源码。8 Y/ y& }! L2 x7 K% u
BootROM 提供的 API 不止 Flash IAP 一个,可以在 Boot Process 章节里如下图里找到全部 API。这里我们可以看到 Flash IAP 函数的统一入口地址是 0x0F001FF1,这在 SDK 里 LPC845_features.h 文件里有如下专门宏:
, E- G7 S5 j3 v4 S( L/* @brief Pointer to ROM IAP entry functions */; t9 w2 L; B+ w7 m6 B6 e# o
#define FSL_FEATURE_SYSCON_IAP_ENTRY_LOCATION (0x0F001FF1) |
|