加入星計劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴散
  • 作品版權(quán)保護
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
    • 一、關(guān)于MCU內(nèi)部Flash的基本概念
    • 二、一般Flash驅(qū)動設(shè)計
    • 三、LPC Flash IAP驅(qū)動設(shè)計原理
    • 四、LPC Flash IAP驅(qū)動快速上手
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

為什么說內(nèi)部Flash驅(qū)動是個既冷門又不冷門的話題

2023/03/30
1690
閱讀需 16 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

大家好,我是痞子衡,是正經(jīng)搞技術(shù)的痞子。今天痞子衡給大家介紹的是恩智浦經(jīng)典LPC系列MCU內(nèi)部Flash IAP驅(qū)動。

LPC 系列 MCU 是恩智浦公司于 2003 年開始推出的非常具有代表性的產(chǎn)品,距今已經(jīng)有近 20 年的生命。按時間線演進來說,其主要分為三代:

- 元老:基于 ARM7/9 內(nèi)核的 LPC2000/3000 系列

- 中堅:基于 Cortex-M0/0+/3/4 內(nèi)核的 LPC800/1100/1200/1300/1500/1700/1800/4000/4300/54000

- 新銳:基于 Cortex-M33 內(nèi)核的 LPC5500 系列。

其中堅產(chǎn)品即是痞子衡今天要重點聊的經(jīng)典 MCU,從其第一顆 LPC1800 到至今仍有新型號出來的 LPC800,仍然深受廣大開發(fā)者喜愛。今天痞子衡想討論的是內(nèi)部 Flash 驅(qū)動這個對嵌入式軟件開發(fā)者來說既冷門又不冷門的話題:

  • Note:本文內(nèi)容主要以 LPC845 這個型號為例,未必完全適用其它經(jīng)典 LPC 型號,具體需要查看相應(yīng)手冊。

一、關(guān)于MCU內(nèi)部Flash的基本概念

痞子衡先解釋下為什么內(nèi)部 Flash 驅(qū)動這個話題既冷門又不冷門。說它冷門是因為大部分嵌入式軟件開發(fā)工程師寫的應(yīng)用代碼里很少包含 Flash 操作功能(除非應(yīng)用需要 OTA 升級或者斷電保存參數(shù)),因此對 Flash 模塊的關(guān)注度不如其它外設(shè)模塊。說它不冷門則是在 IDE 中調(diào)試或者編程器做量產(chǎn)又離不開 Flash 操作,所以避不可免地關(guān)注 Flash 擦寫算法、性能、壽命、效率等。

話說回來,F(xiàn)lash 外設(shè)一般由兩部分組成:Flash 控制器 + Flash Memory 介質(zhì),其 Memory 介質(zhì)部分從原理上屬于并行 NOR Flash,MCU 上電 Flash 外設(shè)總是使能的,可以通過 AHB 總線直接讀取其映射空間內(nèi)任意 Flash 地址處的數(shù)據(jù)/指令,所以其最主要的作用就是存儲可執(zhí)行代碼。

如果應(yīng)用程序需要做 OTA 升級,則需要借助 Flash 控制器完成擦除和寫入操作。這里就有一些概念性的東西出現(xiàn)了,比如 Flash 擦除正常是按 Block/Sector 為單元(不排除有些支持按 Page 擦除),并且擦除操作是將 Block/Sector 里全部 bit 從 0 恢復(fù)為 1。而 Flash 寫入則是按 PUnit 為最小單元的(可能是 1/2/4/8 bytes),一次性最多寫入一個 Page 的數(shù)據(jù)(這里指一次完整命令執(zhí)行等待過程)。擦除和寫入操作都不是立刻就完成的,需要等待 Memory 介質(zhì)更新完成(讀 Flash 控制器相應(yīng)狀態(tài)位寄存器)。

LPC845 內(nèi)部 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單元        擦除單元        擦除單元      最大寫入單元    最小寫入單元

關(guān)于 Flash 擦寫操作,還有一個重要概念叫 Read-While-Write(簡稱 RWW),因為默認代碼是執(zhí)行在 Flash 里,如果我們這個時候還做 Flash 擦寫操作,就會讓同一個 Flash 處于又做擦寫處理同時也要響應(yīng) AHB 總線來的讀指令請求,大部分 Flash 是無法支持這個特性的,因此常見的操作是將觸發(fā) Flash 擦寫命令以及讀 Flash 狀態(tài)的代碼重定向到 RAM 里去執(zhí)行。而 LPC 上不一樣的 Flash IAP 驅(qū)動設(shè)計正是為了解決這個 RWW 限制的。

二、一般Flash驅(qū)動設(shè)計

在講 LPC Flash IAP 特色驅(qū)動之前,我們先來看看一般 MCU 上 Flash 驅(qū)動設(shè)計,就以恩智浦 Kinetis MK60DN512Z 系列為例。它的 Flash 外設(shè)是 FTFL (詳見參考手冊里?Chapter 28 Flash Memory Module (FTFL)?章節(jié)),F(xiàn)lash 大小為 512KB,分為兩個 256KB Block (這里就相當(dāng)于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單元        擦除單元        擦除單元      最大寫入單元    最小寫入單元

在官方驅(qū)動 SDK_2_2_0_TWR-K60D100MdevicesMK60D10driversfsl_flash.c 里我們重點關(guān)注如下 5 個基本函數(shù),這些函數(shù)都是直接操作 FTFL 外設(shè)寄存器來完成相應(yīng) Flash 擦寫功能的。其中 flash_command_sequence() 內(nèi)部函數(shù)設(shè)計是核心,每一個 API 基本都會調(diào)用它,這里面有一個關(guān)于解決 RWW 限制的黑科技設(shè)計,后面痞子衡會寫文章專門介紹。

//?一般初始化函數(shù),主要是軟件層面初始化
status_t?FLASH_Init(flash_config_t?*config);
//?為了解決?RWW?限制而特殊設(shè)計的命令觸發(fā)執(zhí)行函數(shù)
status_t?FLASH_PrepareExecuteInRamFunctions(flash_config_t?*config);
static?status_t?flash_command_sequence(flash_config_t?*config)
//?擦除函數(shù),長度不限(需要按?Sector?對齊),key?參數(shù)是為了降低誤擦除風(fēng)險
status_t?FLASH_Erase(flash_config_t?*config,?uint32_t?start,?uint32_t?lengthInBytes,?uint32_t?key);
//?寫入函數(shù),長度不限(僅最小寫入單元對齊限制),函數(shù)內(nèi)部自動結(jié)合?Page?和?PUnit?寫入命令做處理
status_t?FLASH_Program(flash_config_t?*config,?uint32_t?start,?uint32_t?*src,?uint32_t?lengthInBytes);

三、LPC Flash IAP驅(qū)動設(shè)計原理

終于來到本文核心 - LPC Flash IAP 驅(qū)動了。按照我們一般經(jīng)驗,首先是翻看 LPC845 用戶手冊尋找 Flash 外設(shè),但是很遺憾,用戶手冊里并沒有 Flash 外設(shè)詳細介紹,取而代之的是?Chapter 5: LPC84x ISP and IAP?章節(jié)。因為 LPC 全系列都包含 BootROM(映射地址為 0x0F00_0000 - 0x0F00_3FFF),而 BootROM 代碼里包含了 Flash 擦寫驅(qū)動,因此官方直接推薦用戶調(diào)用 ROM 里的 Flash 驅(qū)動 API 來完成操作,而不是按照傳統(tǒng)方式提供直接操作 Flash 外設(shè)寄存器的 SDK 源碼。

BootROM 提供的 API 不止 Flash IAP 一個,可以在?Boot Process?章節(jié)里如下圖里找到全部 API。這里我們可以看到 Flash IAP 函數(shù)的統(tǒng)一入口地址是 0x0F001FF1,這在 SDK 里 LPC845_features.h 文件里有如下專門宏:

/*?@brief?Pointer?to?ROM?IAP?entry?functions?*/
#define?FSL_FEATURE_SYSCON_IAP_ENTRY_LOCATION?(0x0F001FF1)

有了 IAP 入口地址,調(diào)用起來就簡單了,芯片用戶手冊里直接給了參考 C 代碼,可以看到 API 設(shè)計上將全部支持的 13 個函數(shù)集中在一起了,復(fù)用了輸入?yún)?shù)列表 command_param 和輸出結(jié)果列表 status_result。痞子衡之前寫過一篇?《二代 Kinetis 上的 Flash IAP 設(shè)計》,那個 API 接口設(shè)計更偏向現(xiàn)代嵌入式軟件開發(fā)者的習(xí)慣,而 LPC Flash IAP 接口設(shè)計是 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);

四、LPC Flash IAP驅(qū)動快速上手

最后看一下官方驅(qū)動 SDK_2_13_0_LPCXpresso845MAXdevicesLPC845driversfsl_iap.c ,這相當(dāng)于將 Flash IAP 做了二次封裝,我們重點關(guān)注如下 6 個基本函數(shù)。其中 iap_entry() 最終調(diào)用的是 ROM 中代碼,直接執(zhí)行在 ROM 區(qū)域,不會和 Flash 訪問沖突,天然沒有 RWW 限制問題。

擦除函數(shù) IAP_ErasePage()/IAP_EraseSector() 沒什么好說的,就是這個寫入函數(shù) IAP_CopyRamToFlash() 命名有點繞,不符合一般習(xí)慣,然后需要特別注意的是寫入長度 numOfBytes 必須是 Page 倍數(shù),且不能超過一個 Sector 大小(但是實測可以橫跨兩個 Sector 一次性寫入多個 Page 數(shù)據(jù),所以這僅僅是軟件代碼人為規(guī)定,不是 Flash 控制器限制)。最后還有一個注意點就是擦寫操作都是所謂的 two step process,就是需要先調(diào)用一下 IAP_PrepareSectorForWrite() 函數(shù)才行,這個設(shè)計其實是為了降低程序跑飛出現(xiàn)誤擦寫的風(fēng)險。

//?一般初始化函數(shù),主要是配置?Flash?訪問時間
void?IAP_ConfigAccessFlashTime(uint32_t?accessTime);
//?進入?ROM?IAP?的入口函數(shù)
static?inline?void?iap_entry(uint32_t?*cmd_param,?uint32_t?*status_result);
//?擦除和寫入前準備函數(shù)
status_t?IAP_PrepareSectorForWrite(uint32_t?startSector,?uint32_t?endSector);
//?擦除函數(shù),按?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);
//?寫入函數(shù),長度最大限定為一個?Sector
status_t?IAP_CopyRamToFlash(uint32_t?dstAddr,?uint32_t?*srcAddr,?uint32_t?numOfBytes,?uint32_t?systemCoreClock);

至此,恩智浦經(jīng)典LPC系列MCU內(nèi)部Flash IAP驅(qū)動入門痞子衡便介紹完畢了,掌聲在哪里~~~

恩智浦

恩智浦

恩智浦半導(dǎo)體創(chuàng)立于2006年,其前身為荷蘭飛利浦公司于1953年成立的半導(dǎo)體事業(yè)部,總部位于荷蘭埃因霍溫。恩智浦2010年在美國納斯達克上市。恩智浦2010年在美國納斯達克上市。恩智浦半導(dǎo)體致力于打造全球化解決方案,實現(xiàn)智慧生活,安全連結(jié)。

恩智浦半導(dǎo)體創(chuàng)立于2006年,其前身為荷蘭飛利浦公司于1953年成立的半導(dǎo)體事業(yè)部,總部位于荷蘭埃因霍溫。恩智浦2010年在美國納斯達克上市。恩智浦2010年在美國納斯達克上市。恩智浦半導(dǎo)體致力于打造全球化解決方案,實現(xiàn)智慧生活,安全連結(jié)。收起

查看更多

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

碩士畢業(yè)于蘇州大學(xué)電子信息學(xué)院,目前就職于恩智浦(NXP)半導(dǎo)體MCU系統(tǒng)部門,擔(dān)任嵌入式系統(tǒng)應(yīng)用工程師。痞子衡會定期分享嵌入式相關(guān)文章