大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是恩智浦i.MXRT官方SDK里关于串行Flash相关的驱动与例程资源

  经常有同事以及 i.MXRT 客户咨询痞子衡,咱们恩智浦官方 SDK 里有哪些串行 Flash 相关的示例,每一次痞子衡都是按照询问需求将 SDK 里相应资源路径发给对方。看来那句俗话说得也不尽然对,酒香也怕巷子深,今天痞子衡就给大家全面梳理一下 SDK 里和串行 Flash 相关的全部资源。

  • Note 1: 本文内容主要以 SDK_2_15_100_EVKB-IMXRT1050 软件包里的资源为例
  • Note 2: 本文共分为上下两篇,本篇是上篇,主要介绍 drivers 和 components

一、Flash相关驱动概览

  首先直接开门见山,痞子衡把 SDK 里和 Flash 有关系的驱动全部罗列如下,分布在 device driver、components、middleware、azure-rtos 里。 这四个目录下的驱动关系简单概括如下:

1. device driver 就是芯片底层外设的 HAL 级驱动,代码里直接操作外设寄存器。
2. components 是基于 device driver 而设计的面向某个外围器件/具体小功能的驱动,代码里直接调用 devcie driver 里的 API。
3. middleware 则是面向某类具体功能的更大型的软件库,代码里一般调用 components 里的 API,强调与芯片外设底层隔离,主打平台无关的通用性。
4. azure-rtos 本身是硬件无关的 RTOS,但是其也同时包含了一些类似 middleware 功能实现,这些 middlware 会调用 components 里的 API。

痞子衡嵌入式:浅聊恩智浦i.MXRT官方SDK里关于串行Flash相关的驱动与例程资源(上篇)_API

二、Flash驱动解释及例程

2.1 drivers

2.1.1 flexspi

  fsl_flexspi 驱动是芯片外设 FlexSPI 的 HAL 级驱动,其是操作 FlexSPI 寄存器以实现 1-8bit SPI 类主设备方式数据收发,其 API 本身和 Flash 芯片操作并没有直接的联系,这从如下主要 API 原型可以看出来:

void FLEXSPI_Init(FLEXSPI_Type *base, const flexspi_config_t *config);
void FLEXSPI_SetFlashConfig(FLEXSPI_Type *base, flexspi_device_config_t *config, flexspi_port_t port);
void FLEXSPI_UpdateLUT(FLEXSPI_Type *base, uint32_t index, const uint32_t *cmd, uint32_t count);
status_t FLEXSPI_WriteBlocking(FLEXSPI_Type *base, uint8_t *buffer, size_t size);
status_t FLEXSPI_ReadBlocking(FLEXSPI_Type *base, uint8_t *buffer, size_t size);
status_t FLEXSPI_TransferBlocking(FLEXSPI_Type *base, flexspi_transfer_t *xfer);
status_t FLEXSPI_TransferNonBlocking(FLEXSPI_Type *base, flexspi_handle_t *handle, flexspi_transfer_t *xfer);

  如下配套例程调用 fsl_flexspi 驱动里 API 实现了官方 RT1050-EVKB 板卡上的 hyperflash (S26KS512S) 和四线 nor flash (IS25WP064A) 的读写功能验证。

例程路径: \SDK_2_15_100_EVKB-IMXRT1050\boards\evkbimxrt1050\driver_examples\flexspi
  - Hyper Flash 驱动: \driver_examples\flexspi\hyper_flash\polling_transfer\flexspi_hyper_flash_ops.c
  - Nor Flash 驱动:   \driver_examples\flexspi\nor\polling_transfer\flexspi_nor_flash_ops.c

从 API 命名来看其实现了面向 Flash 的读写擦功能,但是 API 里的代码实现比较简洁,没有过度设计,对于不同厂商/类型的 Flash 支持,主要依赖用户定义的 LUT 表里的命令序列。

void flexspi_nor/hyper_flash_init(FLEXSPI_Type *base);
status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address);
status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t dstAddr, const uint32_t *src);
status_t flexspi_nor_flash_read(FLEXSPI_Type *base, uint32_t dstAddr, const uint32_t *src, uint32_t length);
2.1.2 lpspi

  fsl_lpspi 驱动是芯片外设 LPSPI 的 HAL 级驱动,其是操作 LPSPI 寄存器以实现 1bit(4bit) SPI 主/从设备方式数据收发,API 本身和 Flash 芯片也没有直接的联系。

void LPSPI_MasterInit(LPSPI_Type *base, const lpspi_master_config_t *masterConfig, uint32_t srcClock_Hz);
void LPSPI_SlaveInit(LPSPI_Type *base, const lpspi_slave_config_t *slaveConfig);
status_t LPSPI_MasterTransferBlocking(LPSPI_Type *base, lpspi_transfer_t *transfer);
status_t LPSPI_MasterTransferNonBlocking(LPSPI_Type *base, lpspi_master_handle_t *handle, lpspi_transfer_t *transfer);
status_t LPSPI_SlaveTransferNonBlocking(LPSPI_Type *base, lpspi_slave_handle_t *handle, lpspi_transfer_t *transfer);

  该驱动的配套例程是单纯的 SPI 总线传输,并没有针对 Flash,这里就不展开了,但是它会在 components/flash 里被用到,后文会提及。

2.1.3 romapi

  fsl_romapi 驱动是芯片固化 BootROM 所导出的通用 Flash API,在 BootROM 里集成了基于 FlexSPI 外设驱动而写成的通用 Flash 驱动,这个 Flash 驱动设计差不多是 components 级别,具体源代码原则上不可见,但其实我们可以在 middleware/mcu_bootloader 里大概知道。

例程路径: \SDK_2_15_100_EVKB-IMXRT1050\boards\evkbimxrt1050\driver_examples\fsl_romapi

既有面向 Flash 的写擦功能,也有偏 FlexSPI 外设 HAL 级别的接口。前者相比 flexspi 驱动配套的例程里对于 Flash 的支持就强大多了,用户完全可以仅靠 API 定义的简化参数来支持不同厂商/类型的 Flash;而后者存在的意义是为了让用户能够进一步设计面向 Flash 的功能函数。

status_t ROM_FLEXSPI_NorFlash_Init(uint32_t instance, flexspi_nor_config_t *config);
status_t ROM_FLEXSPI_NorFlash_ProgramPage(uint32_t instance, flexspi_nor_config_t *config, uint32_t dstAddr, const uint32_t *src);
status_t ROM_FLEXSPI_NorFlash_EraseSector(uint32_t instance, flexspi_nor_config_t *config, uint32_t address);
status_t ROM_FLEXSPI_NorFlash_CommandXfer(uint32_t instance, flexspi_xfer_t *xfer);
status_t ROM_FLEXSPI_NorFlash_UpdateLut(uint32_t instance, uint32_t seqIndex, const uint32_t *lutBase, uint32_t seqNumber);

  总结一下,使用 fsl_romapi 例程相比 fsl_flexspi 例程去操作 Flash,好处是省代码空间且不需要考虑 Read-While-Write 限制(仅对驱动本身执行而言,无需代码重定向,但是全局中断问题仍要考虑),坏处是源代码是个黑盒子,出问题不容易定位。

2.2 components

2.2.1 mx25r_flash

  mx25r_flash 组件其实是为 LPC54114 板卡上的旺宏宽电压四线 NOR Flash MX25R 系列而设计的,其在 \SDK_2_xxx_LPCXpresso54114\boards\lpcxpresso54114\driver_examples\spi\polling_flash 例程里有被调用,而在 i.MXRT 系列 SDK 里并没有相关例程使用它(不要疑问为啥会出现在软件包里,多就是好)。这个组件设计得挺有意思,其代码实现完全与芯片具体外设隔离,外设接口传输函数是通过 callback 形式传入的,充满了代码抽象(面向对象)的味道,有兴趣可以查看源码。

mx25r_err_t mx25r_init(struct mx25r_instance *instance, transfer_cb_t callback, void *callback_prv);
mx25r_err_t mx25r_cmd_read(struct mx25r_instance *instance, uint32_t address, uint8_t *buffer, uint32_t size);
mx25r_err_t mx25r_cmd_write(struct mx25r_instance *instance, uint32_t address_256_align, uint8_t *buffer, uint32_t size_256_max);
mx25r_err_t mx25r_cmd_sector_erase(struct mx25r_instance *instance, uint32_t address);
2.2.2 internal_flash

  internal_flash 组件从名字上看像是为片内 Flash 而设计的,但是 i.MXRT 系列并无片内 Flash(RT1024/1064 只是 SIP 了串行 NOR Flash,本质上还是片外)。大家不要被这个名字骗了,这个组件最早确实是用于恩智浦 Kinetis/LPC 系列片内 Flash 的,但是在 i.MX RT 上因为配套 EVK 上有支持 XIP 的外置 NOR Flash,所以这个组件也沿用给这些外置 NOR Flash 了,因此其是基于 flexspi 驱动的组件。

  在 \components\internal_flash\fsl_adapter_flash.h 文件里一共定义了 10 个 API 接口,其中如下 4 个是必须要实现的,其余 6 个可以不用实现(跟 Kinetis/LPC 片内 Flash 特性紧相关)。因为 RT1050-EVKB 默认连接的 hyperflash,所以该组件也仅为其做了相应实现 \components\internal_flash\hyper_flash。这个组件代码实现跟 flexspi 驱动配套例程里对于 Flash 的支持差不多。

hal_flash_status_t HAL_FlashInit(void);
hal_flash_status_t HAL_FlashProgram(uint32_t dest, uint32_t size, uint8_t *pData);
hal_flash_status_t HAL_FlashEraseSector(uint32_t dest, uint32_t size);
hal_flash_status_t HAL_FlashRead(uint32_t src, uint32_t size, uint8_t *pData);

internal_flash 组件设计的意义在于 SDK 其它例程中如果有 IAP 操作或者存储运行参数需求,均可以调用这个统一接口来实现,当然客户应用有相应需求,也一样可以使用。

2.2.3 flash

  flash 组件里一共有三个: nornandmflash,咱们一个个来说:

2.2.3.1 nor

  先来介绍 nor 组件,从 \components\flash\nor\fsl_nor_flash 文件里的 API 命名来看,肯定是面向 NOR Flash 的读写擦功能,接口设计上对于底层外设采用了轻度抽象的方法,形参不涉及具体外设,但是函数实现里不同外设需要不同的实现,这也是为什么我们能看到 \nor\flexspi 和 \nor\lpspi 两个文件夹里的源代码。虽然底层外设不同,但是它们要操作的均是相同的串行 NOR Flash。

status_t Nor_Flash_Init(nor_config_t *config, nor_handle_t *handle);
status_t Nor_Flash_Read(nor_handle_t *handle, uint32_t address, uint8_t *buffer, uint32_t length);
status_t Nor_Flash_Page_Program(nor_handle_t *handle, uint32_t address, uint8_t *buffer);
status_t Nor_Flash_Erase_Sector(nor_handle_t *handle, uint32_t address);
status_t Nor_Flash_Is_Busy(nor_handle_t *handle, bool *isBusy);

\nor\flexspi 里的代码实现跟 romapi 驱动实现方式有点像,其会从 Flash 里读取 SFDP 表进行解析从而自动获取所需操作命令,不依赖用户填充 LUT 命令。

例程路径: \SDK_2_15_000_EVKB-IMXRT1050\boards\evkbimxrt1050\component_examples\flash_component\flexspi_nor

  \nor\lpspi 里的代码实现则比较简单,因为 LPSPI 外设本身主要支持 1bit SPI 传输,所以其也仅实现了一线方式对 Flash 进行读写擦,这部分命令是通用的,也无需用户填充 LUT。

2.2.3.2 nand

  再来介绍 nand 组件,从 \components\flash\nand\fsl_nand_flash.h 文件里的 API 命名来看,肯定是面向 NAND Flash 的读写擦功能,接口设计上对于底层外设同样采用了轻度抽象的方法,形参不涉及具体外设,但是函数实现里不同外设需要不同的实现,这也是为什么我们能看到 \nand\flexspi 和 \nor\semc 两个文件夹里的源代码。不过 flexspi 外设和 semc 外设所支持的 NAND 不是一个产品,前者是串行 NAND,后者是并行 NAND,完全是两类不同的存储器标准。

  \nand\flexspi 里的代码实现则比较简洁,因为串行 NAND 发展不如串行 NOR 那样丰富多样,所以其使用了固定 LUT 里的预设命令序列,基本能够支持华邦等主流四线串行 NAND 产品。

status_t Nand_Flash_Init(nand_config_t *config, nand_handle_t *handle);
status_t Nand_Flash_Read_Page(nand_handle_t *handle, uint32_t pageIndex, uint8_t *buffer, uint32_t length);
status_t Nand_Flash_Page_Program(nand_handle_t *handle, uint32_t pageIndex, const uint8_t *src, uint32_t length);
status_t Nand_Flash_Erase_Block(nand_handle_t *handle, uint32_t blockIndex);
2.2.3.3 mflash

  最后要重点介绍 mflash 组件,其分为 drv 层和 file 层两种不同类型的 API,drv 层提供基于芯片外设的底层 Flash 操作(详见 \mflash\mimxrt1052 文件夹下代码),file 层则是基于 drv 层里的 API 而设计的轻量级静态文件系统,简单理解就是将 Flash 虚拟成一个由具有固定最大长度的预定义命名文件集组成的存储空间,我们可以将小数据以文件名索引的方式写入 Flash,适用于需要存储运行参数或者设备配置数据的场合。

// 来自 \components\flash\mflash\mflash_file.h
bool mflash_is_initialized(void);
status_t mflash_init(const mflash_file_t *dir_template, bool init_drv);
status_t mflash_file_save(char *path, uint8_t *data, uint32_t size);
status_t mflash_file_mmap(char *path, uint8_t **pdata, uint32_t *psize);

// 来自 \components\flash\mflash\mflash_common.h
int32_t mflash_drv_init(void);
int32_t mflash_drv_sector_erase(uint32_t sector_addr);
int32_t mflash_drv_page_program(uint32_t page_addr, uint32_t *data);
int32_t mflash_drv_read(uint32_t addr, uint32_t *buffer, uint32_t len);
void *mflash_drv_phys2log(uint32_t addr, uint32_t len);
uint32_t mflash_drv_log2phys(void *ptr, uint32_t len);

  mflash 组件会在 middleware 以及 rtos 里被广泛使用,这个痞子衡将会在下篇里再具体介绍。

  至此,恩智浦i.MXRT官方SDK里关于串行Flash相关的驱动与例程资源痞子衡便介绍完毕了,掌声在哪里~~~

欢迎订阅

微信搜索"痞子衡嵌入式"或者扫描下面二维码,就可以在手机上第一时间看了哦。

痞子衡嵌入式:浅聊恩智浦i.MXRT官方SDK里关于串行Flash相关的驱动与例程资源(上篇)_嵌入式_02