大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是恩智浦经典LPC系列MCU内部Flash IAP驱动。
LPC 系列 MCU 是恩智浦公司于 2003 年开始推出的非常具有代表性的产品,距今已经有近 20 年的生命。按时间线演进来说,其主要分为三代:
- 元老:基于 ARM7/9 内核的 LPC2000/3000 系列
- 中坚:基于 Cortex-M0/0+/3/4 内核的 LPC800/1100/1200/1300/1500/1700/1800/4000/4300/54000
- 新锐:基于 Cortex-M33 内核的 LPC5500 系列。
其中坚产品即是痞子衡今天要重点聊的经典 MCU,从其第一颗 LPC1800 到至今仍有新型号出来的 LPC800,仍然深受广大开发者喜爱。今天痞子衡想讨论的是内部 Flash 驱动这个对嵌入式软件开发者来说既冷门又不冷门的话题:
- Note:本文内容主要以 LPC845 这个型号为例,未必完全适用其它经典 LPC 型号,具体需要查看相应手册。
痞子衡先解释下为什么内部 Flash 驱动这个话题既冷门又不冷门。说它冷门是因为大部分嵌入式软件开发工程师写的应用代码里很少包含 Flash 操作功能(除非应用需要 OTA 升级或者断电保存参数),因此对 Flash 模块的关注度不如其它外设模块。说它不冷门则是在 IDE 中调试或者编程器做量产又离不开 Flash 操作,所以避不可免地关注 Flash 擦写算法、性能、寿命、效率等。
话说回来,Flash 外设一般由两部分组成:Flash 控制器 + Flash Memory 介质,其 Memory 介质部分从原理上属于并行 NOR Flash,MCU 上电 Flash 外设总是使能的,可以通过 AHB 总线直接读取其映射空间内任意 Flash 地址处的数据/指令,所以其最主要的作用就是存储可执行代码。
如果应用程序需要做 OTA 升级,则需要借助 Flash 控制器完成擦除和写入操作。这里就有一些概念性的东西出现了,比如 Flash 擦除正常是按 Block/Sector 为单元(不排除有些支持按 Page 擦除),并且擦除操作是将 Block/Sector 里全部 bit 从 0 恢复为 1。而 Flash 写入则是按 PUnit 为最小单元的(可能是 1/2/4/8 bytes),一次性最多写入一个 Page 的数据(这里指一次完整命令执行等待过程)。擦除和写入操作都不是立刻就完成的,需要等待 Memory 介质更新完成(读 Flash 控制器相应状态位寄存器)。
LPC845 内部 Flash 一共 64KB,划分为 64 个 Sector,每个 Sector 大小为 1KB。每个 Sector 包含 16 个 Page,每个 Page 大小为 64Bytes。支持按 Sector/Page 擦除,IAP 仅支持按 Page 写入(但是控制器底层最小写入单元是 4bytes),不支持 RWW 特性。
64KB N/A N/A 1KB 64Bytes 4Bytes
Flash Memory > Flash Bank >= Flash Block > Flash Sector > Flash Page >= Flash PUnit >= Flash Byte
| | | | |
RWW单元 擦除单元 擦除单元 最大写入单元 最小写入单元
关于 Flash 擦写操作,还有一个重要概念叫 Read-While-Write(简称 RWW),因为默认代码是执行在 Flash 里,如果我们这个时候还做 Flash 擦写操作,就会让同一个 Flash 处于又做擦写处理同时也要响应 AHB 总线来的读指令请求,大部分 Flash 是无法支持这个特性的,因此常见的操作是将触发 Flash 擦写命令以及读 Flash 状态的代码重定向到 RAM 里去执行。而 LPC 上不一样的 Flash IAP 驱动设计正是为了解决这个 RWW 限制的。
在讲 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。
512KB 256KB 256KB 2KB 2KB 4Bytes
Flash Memory > Flash Bank >= Flash Block > Flash Sector >= Flash Page > Flash PUnit >= Flash Byte
| | | | |
RWW单元 擦除单元 擦除单元 最大写入单元 最小写入单元
在官方驱动 \SDK_2_2_0_TWR-K60D100M\devices\MK60D10\drivers\fsl_flash.c 里我们重点关注如下 5 个基本函数,这些函数都是直接操作 FTFL 外设寄存器来完成相应 Flash 擦写功能的。其中 flash_command_sequence() 内部函数设计是核心,每一个 API 基本都会调用它,这里面有一个关于解决 RWW 限制的黑科技设计,后面痞子衡会写文章专门介绍。
// 一般初始化函数,主要是软件层面初始化
status_t FLASH_Init(flash_config_t *config);
// 为了解决 RWW 限制而特殊设计的命令触发执行函数
status_t FLASH_PrepareExecuteInRamFunctions(flash_config_t *config);
static status_t flash_command_sequence(flash_config_t *config)
// 擦除函数,长度不限(需要按 Sector 对齐),key 参数是为了降低误擦除风险
status_t FLASH_Erase(flash_config_t *config, uint32_t start, uint32_t lengthInBytes, uint32_t key);
// 写入函数,长度不限(仅最小写入单元对齐限制),函数内部自动结合 Page 和 PUnit 写入命令做处理
status_t FLASH_Program(flash_config_t *config, uint32_t start, uint32_t *src, uint32_t lengthInBytes);
终于来到本文核心 - 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 源码。
BootROM 提供的 API 不止 Flash IAP 一个,可以在 Boot Process 章节里如下图里找到全部 API。这里我们可以看到 Flash IAP 函数的统一入口地址是 0x0F001FF1,这在 SDK 里 LPC845_features.h 文件里有如下专门宏:
/* @brief Pointer to ROM IAP entry functions */
#define FSL_FEATURE_SYSCON_IAP_ENTRY_LOCATION (0x0F001FF1)
有了 IAP 入口地址,调用起来就简单了,芯片用户手册里直接给了参考 C 代码,可以看到 API 设计上将全部支持的 13 个函数集中在一起了,复用了输入参数列表 command_param 和输出结果列表 status_result。痞子衡之前写过一篇 《二代 Kinetis 上的 Flash IAP 设计》,那个 API 接口设计更偏向现代嵌入式软件开发者的习惯,而 LPC Flash IAP 接口设计是 2008 年推出来的,那时候看是超前时代。
unsigned int command_param[5];
unsigned int status_result[5];
typedef void (*IAP)(unsigned int [],unsigned int[]);
#define IAP_LOCATION *(volatile unsigned int *)(0x0F001FF1)
IAP iap_entry=(IAP) IAP_LOCATION;
iap_entry (command_param,status_result);
最后看一下官方驱动 \SDK_2_13_0_LPCXpresso845MAX\devices\LPC845\drivers\fsl_iap.c ,这相当于将 Flash IAP 做了二次封装,我们重点关注如下 6 个基本函数。其中 iap_entry() 最终调用的是 ROM 中代码,直接执行在 ROM 区域,不会和 Flash 访问冲突,天然没有 RWW 限制问题。
擦除函数 IAP_ErasePage()/IAP_EraseSector() 没什么好说的,就是这个写入函数 IAP_CopyRamToFlash() 命名有点绕,不符合一般习惯,然后需要特别注意的是写入长度 numOfBytes 必须是 Page 倍数,且不能超过一个 Sector 大小(但是实测可以横跨两个 Sector 一次性写入多个 Page 数据,所以这仅仅是软件代码人为规定,不是 Flash 控制器限制)。最后还有一个注意点就是擦写操作都是所谓的 two step process,就是需要先调用一下 IAP_PrepareSectorForWrite() 函数才行,这个设计其实是为了降低程序跑飞出现误擦写的风险。
// 一般初始化函数,主要是配置 Flash 访问时间
void IAP_ConfigAccessFlashTime(uint32_t accessTime);
// 进入 ROM IAP 的入口函数
static inline void iap_entry(uint32_t *cmd_param, uint32_t *status_result);
// 擦除和写入前准备函数
status_t IAP_PrepareSectorForWrite(uint32_t startSector, uint32_t endSector);
// 擦除函数,按 Page/Sector 为单位
status_t IAP_ErasePage(uint32_t startPage, uint32_t endPage, uint32_t systemCoreClock);
status_t IAP_EraseSector(uint32_t startSector, uint32_t endSector, uint32_t systemCoreClock);
// 写入函数,长度最大限定为一个 Sector
status_t IAP_CopyRamToFlash(uint32_t dstAddr, uint32_t *srcAddr, uint32_t numOfBytes, uint32_t systemCoreClock);
至此,恩智浦经典LPC系列MCU内部Flash IAP驱动入门痞子衡便介绍完毕了,掌声在哪里~~~
文章会同时发布到我的 博客园主页、CSDN主页、知乎主页、微信公众号 平台上。
微信搜索"痞子衡嵌入式"或者扫描下面二维码,就可以在手机上第一时间看了哦。

按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。问题1)我想知道rubyonrails是否有功能类似于primefaces的gem。我问的原因是如果您使用primefaces(http://www.primefaces.org/showcase-labs/ui/home.jsf),开发人员无需担心javascript或jquery的东西。据我所知,JSF是一个规范,基于规范的各种可用实现,prim
本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01 客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02 数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit
目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称
@作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors 1、什么是behaviors 2、behaviors的工作方式 3、创建behavior 4、导入并使用behavior 5、behavior中所有可用的节点 6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors 1、什么是behaviorsbehaviors是小程序中,用于实现
遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg
ES一、简介1、ElasticStackES技术栈:ElasticSearch:存数据+搜索;QL;Kibana:Web可视化平台,分析。LogStash:日志收集,Log4j:产生日志;log.info(xxx)。。。。使用场景:metrics:指标监控…2、基本概念Index(索引)动词:保存(插入)名词:类似MySQL数据库,给数据Type(类型)已废弃,以前类似MySQL的表现在用索引对数据分类Document(文档)真正要保存的一个JSON数据{name:"tcx"}二、入门实战{"name":"DESKTOP-1TSVGKG","cluster_name":"elasticsear
文章目录1.任务背景2.任务目标3.相关知识点4.任务实操4.1安装配置JDK4.2启动FISCOBCOS4.3下载解压WeBASE-Front4.4拷贝sdk证书文件4.5启动节点4.6访问节点4.7检查运行状态5.任务总结1.任务背景FISCOBCOS其实是有控制台管理工具,用来对区块链系统进行各种管理操作。但是对于初学者来说,还是可视化界面更友好,本节就来介绍WeBASE管理平台,这是一款微众银行开源的自研区块链中间件平台,可以降低区块链使用的门槛,大幅提高区块链应用的开发效率。微众银行是腾讯牵头设立的民营银行,在国内民营银行里还是比较出名的。微众银行参与FISCOBCOS生态建设,一定
TCL脚本语言简介•TCL(ToolCommandLanguage)是一种解释执行的脚本语言(ScriptingLanguage),它提供了通用的编程能力:支持变量、过程和控制结构;同时TCL还拥有一个功能强大的固有的核心命令集。TCL经常被用于快速原型开发,脚本编程,GUI和测试等方面。•实际上包含了两个部分:一个语言和一个库。首先,Tcl是一种简单的脚本语言,主要使用于发布命令给一些互交程序如文本编辑器、调试器和shell。由于TCL的解释器是用C\C++语言的过程库实现的,因此在某种意义上我们又可以把TCL看作C库,这个库中有丰富的用于扩展TCL命令的C\C++过程和函数,所以,Tcl是
文章目录一、项目场景二、基本模块原理与调试方法分析——信源部分:三、信号处理部分和显示部分:四、基本的通信链路搭建:四、特殊模块:interpretedMATLABfunction:五、总结和坑点提醒一、项目场景 最近一个任务是使用simulink搭建一个MIMO串扰消除的链路,并用实际收到的数据进行测试,在搭建的过程中也遇到了不少的问题(当然这比vivado里面的debug好不知道多少倍)。准备趁着这个机会,先以一个很基本的通信链路对simulink基础和相关的debug方法进行总结。 在本篇中,主要记录simulink的基本原理和基本的SISO通信传输链路(QPSK方式),计划在下篇记
我使用的是最新版本的Chrome(32.0.1700.107)和Chrome驱动程序(V2.8)。但是当我在Ruby中使用以下代码运行示例测试时:require'selenium-webdriver'WAIT=Selenium::WebDriver::Wait.new(timeout:100)$driver=Selenium::WebDriver.for:chrome$driver.manage.window.maximize$driver.navigate.to'https://www.google.co.in'defapps_hoverele_hover=$driver.find_