当前位置:首页 > 单片机 > 小麦大叔
[导读]大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是i.MXRT下改造FlexSPIdriver以AHB方式去写入NORFlash。痞子衡前段时间写过一篇《串行NANDFlash的两大特性导致其在i.MXRTFlexSPI下无法XiP》,文章里介绍了NANDFlash...

大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是i.MXRT下改造FlexSPI driver以AHB方式去写入NOR Flash

痞子衡前段时间写过一篇 《串行NAND Flash的两大特性导致其在i.MXRT FlexSPI下无法XiP》,文章里介绍了 NAND Flash 的 Page Read 等待特性(发完 Read 命令后需要回读 Flash 内部状态寄存器 Busy 位来判断 Page 数据是否已准备好)导致其无法像 NOR Flash 那样通过 AHB 方式被便捷访问,仅能在一个 Page 空间里实现 AHB 读(前提是在 IPG 方式发完读命令以及读完状态寄存器确保数据已经准备好后)。

回到 NOR Flash 上,我们可以轻松通过 AHB 方式读取 Flash 数据,但写入 Flash 一般都是调用 FlexSPI 驱动来实现(即 IPG 方式),那么有没有可能也通过 “AHB 方式来写入 Flash” 呢?答案是可以的,但为啥痞子衡加了个引号,且往下看:

本文以恩智浦 MIMXRT1170-EVK 开发板上主芯片 i.MXRT1176 及其配套板载 Flash 芯片 - 芯成 IS25WP128 为例。

一、Flash写操作流程

芯成 IS25WP128 是一颗很典型的四线 QSPI NOR Flash,其写入(编程)时序是符合 JEDEC216 标准的。简单来说,一个完整的写时序包含三个独立子时序:Write Enable 时序 Page Program 时序 Read Status 时序。

先来看打头阵的 Write Enable 子时序,NOR Flash 内部的状态寄存器会有一个位叫 WEL (Write Enable Latch),这个位控制着 Flash 的擦写权限,默认值是 0(即不允许擦写)。如果想要写入 Flash,必须先通过 Write Enable 命令将 WEL 位临时设为 1(这个位会随着当前的擦写命令结束后自动恢复到 0)。

置位了 WEL 后,便可以传输 Page 数据给 Flash,这个子时序便是 Page Program。Page Program 按命令地址和数据传输方式不同分为三种:一线 SPI,四线 SPI,QPI,下面是常用的四线 SPI 的时序图,命令和地址通过 IO0 传输,数据通过 IO[3:0] 传输。

通常来说,在这个时序里,传入的地址应该是 Page 首地址,写入数据长度应该是一个完整的 Page 大小。但从非 Page 首地址处写入小于一个 Page 长度的数据也是可以的,但有一个注意点就是不要在这个时序里出现跨页的现象(如果出现,超出当前页的数据会被放回到该页起始地址处)。

Page Program 子时序结束后,数据还并未真正写入 Flash 内存体中,Flash 内部控制器只是开始处理数据,这时候会有一个等待时间(大概0.2ms),Flash 内部的状态寄存器有一个位叫 WIP (Write In Progress),这个位标志着数据写入状态(默认值是 0,当 Page Program 子时序结束后,WIP 立即跳为 1),用户需要通过 Read Status 子时序来实时读取状态寄存器的值从而获知数据处理情况。

当 Flash 内部状态寄存器中的 WIP 位从 1 跳回到 0,便意味着一次完整的写时序结束了,主机可以发起下一次写时序。

二、FlexSPI对写时序支持

痞子衡旧文 《从头开始认识i.MXRT启动头FDCB里的lookupTable》 里对 FlexSPI 读时序介绍得非常详细,尤其是对 AHB 方式读支持的实现,现在痞子衡再介绍下 FlexSPI 对于写时序的支持。

第一节里介绍的 Flash 写操作的三个子时序,在 FlexSPI 外设里当然都是支持的,SEQ_CTL 组件里都预先实现了这些子时序,比如下面就是 Page Program 的序列:

因为 Flash 写操作需要三个子序列,比 Flash 读操作单序列要复杂得多,并且最关键的是写操作还包含一个不确定的等待周期(Read Status 子时序与 Flash 交互),这就导致 FlexSPI 外设在 AHB 方式写上没法完美支持,这也是为什么写入 Flash 都是通过 IPG 方式来完成的,因为 IPG 方式下,子序列可以随意组合,由用户代码手动调度。

原则上三个写操作子序列可以放在 LUT 中的任何一个 Sequence 位置,因为即使按序放在一起,我们通过 FlexSPI->FLSHxCR2 寄存器(x可取A1/A2/B1/B2,具体根据Flash引脚连接来定)中的 AWRSEQID 位指明写操作第一个子序列在 LUT 中的位置(index) 也无法自动完成 Page 数据的完整写入操作。

但也不要就此放弃,单独 Page Program 子序列还是可以通过 AHB 方式写来替代的,这样也可以让我们过一下 AHB 方式写入 Flash 的瘾,只是需要在 AHB 写入操作前后辅助 IPG 方式下的 Write Enable 和 Read Status 动作,下一节用代码给大家实际演示。

三、FlexSPI driver用法

例程路径:\SDK_2.10.0_MIMXRT1170-EVK\boards\evkmimxrt1170\driver_examples\flexspi\nor\polling_transfer\cm7\iar

3.1 初始化

先来看一下 FlexSPI 初始化函数 flexspi_nor_flash_init(),这个函数需要三个配置变量:分别是 flexspi_config_t 型面向 FlexSPI 外设层的配置 flexspiconfig,flexspi_device_config_t 型面向 Flash 器件端的配置 deviceconfig,以及很核心的 customLUT(这里只列出了跟 Flash 读写操作相关的时序)。

#define NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD     0
#define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE        2
#define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD   4
#define NOR_CMD_LUT_SEQ_IDX_READSTATUSREG      12

#define CUSTOM_LUT_LENGTH        60
const uint32_t customLUT[CUSTOM_LUT_LENGTH] = {
    /* Fast read quad mode - SDR */
    [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xEB, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_4PAD, 0x18),
    [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD   1] = 
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_4PAD, 0x06, kFLEXSPI_Command_READ_SDR, kFLEXSPI_4PAD, 0x04),

    /* Write Enable */
    [4 * NOR_CMD_LUT_SEQ_IDX_WRITEENABLE] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x06, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),

    /* Page Program - quad mode */
    [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x32, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
    [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD   1] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_4PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),

    /* Read status register */
    [4 * NOR_CMD_LUT_SEQ_IDX_READSTATUSREG] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x05, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),
};

flexspi_device_config_t deviceconfig = {
    .flexspiRootClk       = 12000000,
    .flashSize            = 0x4000/* 16Mb/KByte */
    .CSIntervalUnit       = kFLEXSPI_CsIntervalUnit1SckCycle,
    .CSInterval           = 2,
    .CSHoldTime           = 3,
    .CSSetupTime          = 3,
    .dataValidTime        = 0,
    .columnspace          = 0,
    .enableWordAddress    = 0,
    .AWRSeqIndex          = 0,
    .AWRSeqNumber         = 0,
    // 支持 AHB 读的关键配置
    .ARDSeqIndex          = NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD,
    .ARDSeqNumber         = 1,
    .AHBWriteWaitUnit     = kFLEXSPI_AhbWriteWaitUnit2AhbCycle,
    .AHBWriteWaitInterval = 0,
};

void flexspi_nor_flash_init(FLEXSPI_Type *base)
{
    CLOCK_SetRootClockDiv(kCLOCK_Root_Flexspi1, 2);
    CLOCK_SetRootClockMux(kCLOCK_Root_Flexspi1, 0);

    /*Get FLEXSPI default settings and configure the flexspi. */
    flexspi_config_t flexspiconfig;
    FLEXSPI_GetDefaultConfig(
本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭