一、概述
RT595 集成了 FlexSPI 电路,用于实现高速串行外部存储器与主控经片之间的通信,FlexSPI代表Flexible Serial Peripheral Interface,它提供了一种灵活的方式来连接和操作外部闪存设备,例如 Flash、Hyper RAM … 等,主要用于支援各种透过 Standard SPI、Dual SPI、Quad SPI、Octal SPI 进行通讯的设备,RT595 的 FlexSPI 架构如图 1,借由 IO_CTL对 IO 引脚进行控制,再对应 SPI Bus port 引脚将其配置到外部 Flash 进行通讯,以 NXP MIMXRT595-EVK 为例,如图 2 透过对应的 SPI Bus port ( FLEXSPI0 Port ) 与外部 Flash 进行通讯,本文将着重于透过 RT595 的 FlexSPI 模组对外部 Flash 的设定、擦除 / 读写进行解析进行解说。
二、 需求物件:
2.1 硬体
2.1.1 NXP RT595 EVK 详细规格如下列网址所示
https://www.nxp.com/design/development-boards/i-mx-evaluation-and-development-boards/i-mx-rt595-evaluation-kit:MIMXRT595-EVK
2.1.2 Type A to mini B USB Cable : 1 pcs
2.2 软体
2.2.1 MCUXPRESSO ( IDE ) 软体开发环境如下列网址所示
https://www.nxp.com/design/software/development-software/mcuxpresso-software-and-tools-/mcuxpresso-integrated-development-environment-ide:MCUXpresso-IDE
2.2.2 NXP RT595 SDK 原厂提供的 RT595 EVK Sample Code 如下列网址所示
https://mcuxpresso.nxp.com/en/welcome
三、 操作方式:
3.1 开启 evkmimxrt595_flexspi_octal_polling_transfer 的范例,并将范例烧录到 EVK 上,NXP RT595 EVK 板上配置有烧录功能的 IC,如图 3 红框
3.2 将 USB Cable 连结到模组板 USB Connector,USB Cable 另一端连结到PC 如图 4
3.3 执行 MCUXPRESSO 并将 NXP 的 SDK 导入到 IDE 中,操作流程如图 5、6
3.3.1 点击 import SDK example(s)
3.3.2 弹出 SDK import Wizard 视窗,点选 RT595
3.3.3 点选 Next
3.3.4 点选 sample code
3.3.5 点选 Finish 完成 Project 建立
3.4 Pin define
3.4.1 开启 SDK 建立好的专案 evkmimxrt595_flexspi_octal_polling_transfer
3.4.2 点选 flexspi_octal_polling_transfer.c ( 如图 7 )
3.4.3 查看 BOARD_InitPins 中的 Code ( 如图 7 )
3.4.4 查看 BOARD_InitPins 将在分页中开启 pin_mux.c
3.4.5 pin_mux.c 将会看到针对 FlexSPI 使用到的 Pin 设定
3.4.6 宣告一变数 port1_pin18_config 将对 P1_18 ( FLEXSPI0_SCLK ) 的相关参数储存其中,参数如下 ( 如图 8 ):
a ) IOPCTL_PIO_FUNC1:选择 IOPCTL function 为 1 ( FLEXSPI0_SCLK 功能 )
b ) IOPCTL_PIO_PUPD_DI: Pull down 设定
c ) IOPCTL_PIO_PULLDOWN_EN:Pullup / Pulldown 功能 Enable
d ) IOPCTL_PIO_INBUF_EN:对应 Reference Manual 建议,在 SPI 应用下开启此功能
e ) IOPCTL_PIO_SLEW_RATE_NORMAL:对应 Datasheet建议,在 SPI 应用下使用 Standard mode
f ) IOPCTL_PIO_FULLDRIVE_EN:Full output drive mode
g ) IOPCTL_PIO_ANAMUX_DI:Analog multiplexor disabled
h ) IOPCTL_PIO_PSEDRAIN_DI:Normal push-pull output
i ) IOPCTL_PIO_INV_DI:Input function is not inverted
3.4.7 IOCON_PinMuxSet function 中有 4 个参数需填入如下
a ) IOCON : 对应到 RT595 的 IOPCTL register address ( 0x40004000 )
b ) 1U : 对应到硬体规划的 GPIO Port 上 ( 这边宣告为 Port 1 )
c ) 18U : 对应到硬体规划的 GPIO Port Pin 上 ( 这边宣告为 Port 1 Pin 18 )
d ) port1_pin18_config : 该参数用来定义 IOPCTL register 的参数,详细参数说明如 3.4.6 所示
其馀 FLEXSPI0 Pin 的设定方式类似,这边就不再叙述
3.5 FLEXSPI0 通讯时脉设定 function
3.5.1 透过 BOARD_SetFlexspiClock function 中的参数进行通讯时脉设定,参数定义如下:
a ) FLEXSPI0 : 对应到 RT595 的 FLEXSPI0 register address ( 0x40134000 )
b ) 2 : 将 FLEXSPI0 的 Clock Source 设定为 SYSPLL0 AUX0_PLL_Clock,SYSPLL0 AUX0_PLL_Clock 源自于 SysPLL0 ( 528 MHz ),经过除频后为 396 MHz
c ) 4 : 将 SYSPLL0 AUX0_PLL_Clock ( 396 MHz ) / 4 等于 99 MHz
3.5.2 查看 flexspi_nor_flash_init 中的 Code ( 如图 9 )
3.5.3 查看 flexspi_nor_flash_init 将在分页中开启 flexspi_octal_flash_ops.c ( 如图 10 )
3.5.4 customLUT参数:该参数为 RT595 与外部 Flash 通讯时的相关资讯,需对应 Flash 来查看,查询 customLUT 后 main code 中可以找到对应的 Table,LUT 为 RT595 与 Device 通讯时,RT595 以查表的形式来定义要使用哪种格式与 Device 进行通讯,此处仅是将 customLUT 的参数映射到 localLUT 的结构变数中,当后续使用到该参数在细说该部分
3.5.5 进入 FLEXSPI_GetDefaultConfig function查看 ( 如图 11 ),function 中所设定的默认值如下:
- config->rxSampleClock = kFLEXSPI_ReadSampleClkLoopbackInternally默认由 MCU FlexSPI 产生 DQS 讯号来作为 Read Sample Clock
- config->enableSckFreeRunning = false 选择不启用外部CLK,CLK源自 MCU 本身
- config->enableDoze = true 当系统有打瞌睡模式请求时,AHB 时钟和串行时钟将被关闭。
- config->enableHalfSpeedAccess = false 不启用降速功能 ( 降速除频值为 2 ),给外部 A_SCLK/B_SCLK
- config->enableSckBDiffOpt = false 不启用B_SCLK为A_SCLK的差分时钟,通常做为 CLK_N、CLK_P 使用
- config->enableSameConfigForAll = false 每个 FLEXSPI 都是独立 Size 计算
- config->seqTimeoutCycle = 0xFFFFU 超时时间参数设定 ( 65535 * 1024 Serial Root Clock cycles ),超时产生中断并忽略AHB命令
- config->ipGrantTimeoutCycle = 0xFFU IP Command 延迟时间设定 ( 255 * 1024 AHB Clock cycles ),在指定时间后 IP Command 未被下达则触发中断
- config->txWatermark = 8 TX Watermark level is 8 * 64 Bits,当 Watermark 的空馀的程度大于或等于该水印值时,可触发中断
- config->rxWatermark = 8 RX Watermark level is 8*64 Bits,当 Watermark 的空馀的程度大于或等于该水印值时,可触发中断
- config->ahbConfig.ahbGrantTimeoutCycle = 0xFFU AHB 延迟时间设定 ( 255 * 1024 AHB Clock cycles ),超时即产生中断
- config->ahbConfig.ahbBusTimeoutCycle = 0xFFFFU AHB 在设定时间内未收到或传输资料时的 time out 设定 ( 65535 * 1024 ahb clock cycles ),超时即产生中断
- config->ahbConfig.resumeWaitCycle = 0x20U 配置在暂停命令序列恢复之前的等待空闲状态周期,等待超过设定值 AHB 个时钟后超时
- config->ahbConfig.buffer[i].enablePrefetch = true AHB 读取预取使能
- config->ahbConfig.buffer[i].masterIndex = 0xFU AHB RX Buffer 根据 AHB Master 的 ID (MSTR_ID) 进行分配,内容非0即使用
- config->ahbConfig.buffer[i].bufferSize = 0 AHB RX 缓冲区大小
- config->ahbConfig.buffer[i].enablePrefetch = true AHB 读取预取使能。
- config->ahbConfig.buffer[i].bufferSize = 256U AHB RX 缓冲区大小
* 上述 i 值为缓冲区规划
- config->ahbConfig.enableClearAHBBufferOpt = false 当 FlexSPI 返回停止模式 ACK 时,AHB RX/TX 缓冲区不会被自动清除
- config->ahbConfig.enableReadAddressOpt = false 当 flash 以并行模式访问或 flash 是字寻址时,存在 AHB 读取突发起始地址对齐限制
- config->ahbConfig.enableAHBPrefetch = false AHB 读取预取使能
- config->ahbConfig.enableAHBBufferable = false FlexSPI 将在所有数据传输到外部设备并且 AHB 命令完成后返回 AHB 总线就绪
- config->ahbConfig.enableAHBCachable = false 当存在 AHB 总线缓存读取访问时,FlexSPI 将不会检查它是否命中 AHB TX 缓冲区
3.5.6 进入 FLEXSPI Config 独立设定 ( 如图 12 ) 中所设定的值如下:
- ahbConfig.enableAHBPrefetch = true AHB 读取预取使能
- rxSampleClock = EXAMPLE_FLEXSPI_RX_SAMPLE_CLOCK Flash Read Sample Clk 来自 DQS Pin,即讯号来自于外部 Flash 的 DQS Pin
- ahbConfig.enableAHBBufferable = true FlexSPI 将在所有数据传输到外部设备并且 AHB 命令完成后返回 AHB 总线就绪
- ahbConfig.enableAHBCachable = true 当存在 AHB 总线缓存读取访问时,FlexSPI 将不会检查它是否命中 AHB TX 缓冲区
3.5.7 将 FLEXSPI Config 的参数设定到 FlexSPI register 中
3.5.8 将 deviceconfig 的参数设定到 FlexSPI register 的 Port A1 中 ( 如图 13 ),deviceconfig 的参数如下:
- .flexspiRootClk = 99000000 FLEXSPI 的时钟频率
- .flashSize = FLASH_SIZE Flash 的大小 ( KByte )
- .CSIntervalUnit = kFLEXSPI_CsIntervalUnit1SckCycle CS 的间隔单位可配置为 1 或 256 个周期
- .CSInterval = 2 CS 讯号断言间隔,通过多个 CS 间隔单位来获取 CS 讯号断言间隔周期
- .CSHoldTime = 3 CS 讯号保持时间
- .CSSetupTime = 3 CS 讯号建立时间
- .dataValidTime = 2 对外部设备的数据有效时间
- .columnspace = 0 列空间大小
- .enableWordAddress = 0 是否使能字(4字节)地址
- .AWRSeqIndex = NOR_CMD_LUT_SEQ_IDX_WRITE AHB写命令的AHB序列ID
- .AWRSeqNumber = 1 AHB 写命令的序列数目
- .ARDSeqIndex = NOR_CMD_LUT_SEQ_IDX_READ AHB 读命令序列ID
- .ARDSeqNumber = 1 AHB读命令的序列数目
- .AHBWriteWaitUnit = kFLEXSPI_AhbWriteWaitUnit2AhbCycle AHB 写等待单位
- .AHBWriteWaitInterval = 0 AHB 写等待间隔,通过多个 AHB 写间隔单位来完成AHB写等待周期
3.5.8 将 customLUT的参数设定到 FlexSPI register 中 ( 如图 14 )
3.5.9 设定软件复位完成后由硬件自动清零
3.5.10 Release CACHE,本范例不会使用到 CACHE 因此这边不做解说
3.5.11 后续的 flexspi_nor_ 类别的 Function,将会透过输入 Function 来对应 LUT 的 index 执行出不同的通讯方式,下列范例将透过 get vendor ID ( 如图 15 ) 参数来解析如何对应到 EVK 上使用的 Flash ( MX25UW51345GXDI00 )
3.5.12 这边先解析 LUT 的参数
a ) 该 LUT 是透过其他 function 的 CUSTOM_LUT_LENGTH 参数来决定取出 Table 中的哪个参数来使用 ( 如图 16 )
b ) 以 get vendor ID ( Read ID ) 参数为例 ( 如图 17 )
b1 . kFLEXSPI_Command_DDR:参数传输将以 DDR Mode 传输 ( 如图 16 ),对应到 Transmit Data 后续资料传输会以 8 bit 传输
b2 . kFLEXSPI_8PAD:参数传输将透过 8 Pin Mode 传输
b3 . 0x9F:对应 MX25UW51345GXDI00 参数 ( 如图 18 )
b4 . 0x60:对应 MX25UW51345GXDI00 参数 ( 如图 18 )
c ) 下一步骤的资料解析如下
c1 . kFLEXSPI_Command_RADDR_DDR:参数传输将以 DDR Mode 传输 ( 如图 19 ),对应到 Transmit Data 后续资料传输会以 32 bit 传输
c2 . kFLEXSPI_8PAD:参数传输将透过 8 Pin Mode 传输
c3. 0x20:设定传输 32 bit 的 0 值 ( 如图 18 ),若要变更该参数则可修改 flexspi_nor_get_vendor_id Function 中的 flashXfer.deviceAddress 参数
c4. kFLEXSPI_Command_DUMMY_DDR:参数传输将以 DDR Mode 传输 ( 如图 20 )
c5. 0x08:对应 MX25UW51345GXDI00 中所需的 Dummy 时间参数 ( 如图 21 ),需要等到 8 个 DDR mode 下的 SCLK 时间
d ) 下一步骤的资料解析如下
d1 . kFLEXSPI_Command_READ_DDR:参数传输将以 DDR Mode 接收 ( 如图 22 )
d2 . kFLEXSPI_8PAD:参数传输将透过 8 Pin Mode 传输
d3 . 0x04 : 该参数在这边不被 function 参考,可以填任意值
d4 . kFLEXSPI_Command_STOP : 该参数是在告知 MCU 传输已结束
d5 . kFLEXSPI_1PAD : 参数传输将透过 1 Pin Mode 传输
d6 . 0x0 : 因 STOP Command 不传输实际 Data 因此填 0
图 22 ( 注 4 )
3.5.13 回到 flexspi_nor_get_vendor_id Function 中查看 ( 如图 23 ) 先关参数如下:
a ) flashXfer.deviceAddress = 0,MCU 对 Flash 传输时的 Address
b ) flashXfer.port = FLASH_PORT,这边设定为 PortA1,因 NXP EVK 配置使用 PortA1 对 Flash 进行通讯
c ) flashXfer.cmdType = kFLEXSPI_Read,get vendor id 行为为 read,因此这边设定为 Read
d ) flashXfer.SeqNumber = 1,设定要执行的序列数目,范例中设定 1 即可
e ) flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_READID_OPI,LUT 循址 inedx
f ) flashXfer.data = &temp,Read 后的参数摆设位址
g ) flashXfer.dataSize = 1,读取的 ID Size 大小
h ) FLEXSPI_TransferBlocking : 透过该 Function 将 flashXfer 的参数设定到 FlexSPI register 中,并启动传输功能
i ) *vendorId = temp : 将从 Flash 中取得的 ID 参数回传到 vendorId 变数中
3.5.14 后续的其他 Function 的解析方式与上述解析雷同,本范例解析完毕
注 1:作者:NXP Semiconductors;出处:NXP 文件 IMXRT500RM Rev. 0.1 的 Fig.122
注 2:作者:NXP Semiconductors;出处:NXP 文件 spf-45800_d1
注 3 :作者:NXP Semiconductors;出处:NXP 文件 MIMXRT595EVKHUG User's Guide Rev. 0, 的 Fig.1
注 4 :作者:NXP Semiconductors;出处:NXP 文件 MIMXRT595EVKHUG User's Guide Rev. 0, 的 Table 404
注 5 :作者:MACRONIX INTERNATIONAL CO., LTD.;出处: MACRONIX 文件 MX25UM51345G Datasheet 的 Table 6
注 6 :作者:MACRONIX INTERNATIONAL CO., LTD.;出处: MACRONIX 文件 MX25UM51345G Datasheet 的 Figure 13.