FlexSPI 传输结构体解析
本文整理自22. FlexSPI—读写外部SPI NorFlash — [野火]i.MX RT库开发实战指南——基于i.MXRT1052 文档
用作个人学习和分享
一、FlexSPI 传输结构体(flexspi_transfer_t
)
传输结构体用于描述单次 FlexSPI 数据传输的具体参数,本质是将硬件寄存器配置(如 IPCR0/IPCR1、TX/RX FIFO)封装为软件友好的结构体,配合FLEXSPI_TransferBlocking
(阻塞)或FLEXSPI_TransferNonBlocking
(非阻塞)函数即可触发传输。其核心作用是 “告诉 FlexSPI:用哪个端口、执行哪个 LUT 指令、传输多少数据、数据存在哪里”。
1. 结构体定义与成员总览
typedef struct _flexspi_transfer {uint32_t deviceAddress; /*!< 目标设备内部地址 */flexspi_port_t port; /*!< 操作的FlexSPI端口 */flexspi_command_type_t cmdType; /*!< 传输命令类型 */uint8_t seqIndex; /*!< LUT中指令序列的ID */uint8_t SeqNumber; /*!< 要执行的LUT序列数量 */uint32_t *data; /*!< 数据缓冲区指针(发送/接收) */size_t dataSize; /*!< 传输数据的字节数 */
} flexspi_transfer_t;
2. 关键成员
成员名 | 硬件寄存器映射 | 核心功能与使用说明 |
---|---|---|
deviceAddress | IPCR0 寄存器 [SFAR] 位 | 目标设备(如 FLASH)的内部地址,仅用于需要地址的操作(如读 FLASH 的 0x1000 地址)。 示例:读 FLASH 地址 0x1000 时,赋值为 0x1000 ;无地址的命令(如复位)赋值为0 。 |
port | -(端口选择控制逻辑) | 指定操作的 FlexSPI 物理端口,枚举类型flexspi_port_t 取值如下:- kFLEXSPI_PortA1 /A2 :A 组端口 1/2- kFLEXSPI_PortB1 /B2 :B 组端口 1/2注意:需与外部设备(如 FLASH)实际连接的端口一致。 |
cmdType | -(命令类型识别逻辑) | 定义传输的命令类型,决定data 缓冲区是否有效,枚举类型flexspi_command_type_t 取值:- kFLEXSPI_Command :纯命令(无数据),忽略data (如 FLASH 复位);- kFLEXSPI_Config :配置设备模式(如设置 FLASH 时序),data 长度由 LUT 定义;- kFLEXSPI_Read :读操作,仅data 作为接收缓冲区有效;- kFLEXSPI_Write :写操作,仅data 作为发送缓冲区有效。 |
seqIndex | IPCR1 寄存器 [ISEQID] 位 | LUT(查找表)中指令序列的 ID。FlexSPI 通过该值在 LUT 中找到对应的指令(如 “读 FLASH” 序列)。 示例:若 LUT 中索引 0 定义 “读 ID” 序列, seqIndex 赋值为0 。 |
SeqNumber | IPCR1 寄存器 [ISEQNUM] 位 | 要执行的 LUT 指令序列数量。多数场景下为1 (执行 1 个序列);若需连续执行多个序列(如 “写使能 + 写数据”),需赋值为对应数量(需提前在 LUT 中定义连续序列)。 |
data | TX FIFO(发送)/RX FIFO(接收) | 数据缓冲区指针: - 写操作:指向要发送的数据数组(数据最终写入 TX FIFO 寄存器); - 读操作:指向接收数据的数组(数据从 RX FIFO 读取后存入); - 纯命令 / 配置操作:可设为 NULL 。注意:缓冲区需为 32 位对齐(FlexSPI FIFO 为 32 位宽度)。 |
dataSize | IPCR1 寄存器 [IDATSZ] 位 | 传输数据的字节数,16 位寄存器限制(最大 65535 字节)。 示例:读 256 字节数据时,赋值为 256 ;无数据传输时赋值为0 。 |
3. 典型使用示例(读 FLASH 数据)
// 1. 定义数据缓冲区(32位对齐)
uint32_t rxBuffer[64] __attribute__((aligned(4))); // 接收256字节(64*4)// 2. 初始化传输结构体
flexspi_transfer_t transfer = {.deviceAddress = 0x1000, // 读FLASH的0x1000地址.port = kFLEXSPI_PortA1, // 设备连接A1端口.cmdType = kFLEXSPI_Read, // 读操作.seqIndex = 1, // LUT中索引1为“读数据”序列.SeqNumber = 1, // 执行1个序列.data = rxBuffer, // 接收缓冲区.dataSize = sizeof(rxBuffer) // 传输256字节
};// 3. 触发阻塞传输(FlexSPI_BASE为外设基地址)
FLEXSPI_TransferBlocking(FLEXSPI_BASE, &transfer);
二、FlexSPI 外部设备配置结构体(flexspi_device_config_t
)
外部设备配置结构体用于定义FlexSPI 与外部设备(如 FLASH)的通讯时序与硬件参数,本质是将设备相关的配置寄存器(如 FLSHxxCR0/CR1/CR2、DLLACR)封装,配合FLEXSPI_SetFlashConfig
函数写入硬件,确保 FlexSPI 与外部设备的时序匹配(如时钟、CS 建立时间)。
1. 结构体定义与成员总览
typedef struct _flexspi_device_config {uint32_t flexspiRootClk; /*!< FlexSPI根时钟频率 */bool isSck2Enabled; /*!< 是否使能SCK2时钟 */uint32_t flashSize; /*!< FLASH容量(单位:KByte) */flexspi_cs_interval_cycle_unit_t CSIntervalUnit; /*!< CS间隔单位 */uint16_t CSInterval; /*!< CS间隔周期数 */uint8_t CSHoldTime; /*!< CS保持时间(根时钟周期) */uint8_t CSSetupTime; /*!< CS建立时间(根时钟周期) */uint8_t dataValidTime; /*!< 数据有效时间(单位:ns) */uint8_t columnspace; /*!< 列地址宽度(单位:bit) */bool enableWordAddress; /*!< 是否使能4字节地址 */uint8_t AWRSeqIndex; /*!< AHB写命令的LUT序列ID */uint8_t AWRSeqNumber; /*!< AHB写命令的序列数量 */uint8_t ARDSeqIndex; /*!< AHB读命令的LUT序列ID */uint8_t ARDSeqNumber; /*!< AHB读命令的序列数量 */flexspi_ahb_write_wait_unit_t AHBWriteWaitUnit; /*!< AHB写等待单位 */uint16_t AHBWriteWaitInterval; /*!< AHB写等待周期数 */bool enableWriteMask; /*!< 是否使能DQS作为写掩码 */
} flexspi_device_config_t;
2. 关键成员详解(按功能分类)
(1)基础时钟与设备容量配置
成员名 | 硬件寄存器映射 | 核心功能与使用说明 |
---|---|---|
flexspiRootClk | -(库函数计算用) | FlexSPI 根时钟频率(如 120MHz),不用于设置时钟,仅供库函数计算时序参数(如 CS 建立时间)。 需与实际配置的 FlexSPI 根时钟一致(从 PLL 或外设时钟树获取)。 |
isSck2Enabled | -(SCK2 控制逻辑) | 是否使能 SCK2 时钟。RT1052 无 SCK2 引脚,固定赋值为false ;其他支持 SCK2 的 MCU 按需配置。 |
flashSize | FLSHxxCR0 [FLSHSZ] 位 | 外部 FLASH 的容量(单位:KByte),单个设备最大 4GB,总容量不超过 4GB。 示例:16MB FLASH 赋值为 16*1024 = 16384 。 |
(2)CS 信号线时序配置
CS(片选)信号线的时序直接影响设备通讯稳定性,需严格遵循外部设备(如 FLASH)的数据手册。
成员名 | 硬件寄存器映射 | 核心功能与使用说明 |
---|---|---|
CSIntervalUnit | FLSHxxCR1[CSINTERVALUNIT] | CS 间隔的时间单位,枚举类型flexspi_cs_interval_cycle_unit_t 取值:- kFLEXSPI_CsIntervalUnit1SckCycle :1 个 SCK 周期- kFLEXSPI_CsIntervalUnit256SckCycle :256 个 SCK 周期 |
CSInterval | FLSHxxCR1[CSINTERVAL] | CS 线 “无效→有效” 或 “有效→无效” 的最小间隔周期数,单位由CSIntervalUnit 定义。示例:FLASH 要求 CS 间隔≥100ns,若 SCK=100MHz(10ns / 周期),则 CSIntervalUnit=1SckCycle ,CSInterval=10 。 |
CSHoldTime | FLSHxxCR1 [TCSH] 位 | CS 线 “有效→无效” 的保持时间,单位为 FlexSPI 根时钟周期。 示例:根时钟 = 120MHz(~8.3ns / 周期),FLASH 要求 CS 保持≥10ns,赋值为 2 (2*8.3=16.6ns)。 |
CSSetupTime | FLSHxxCR1 [TCSS] 位 | CS 线 “无效→有效” 的建立时间,单位为 FlexSPI 根时钟周期。 配置逻辑同 CSHoldTime ,需满足设备手册的最小建立时间要求。 |
(3)数据时序与地址配置
成员名 | 硬件寄存器映射 | 核心功能与使用说明 |
---|---|---|
dataValidTime | DLLACR/DLLBCR 寄存器 | 数据有效时间(单位:ns),用于校准 FlexSPI 的数据采样时序,确保数据稳定采样。 需根据外部设备的 “数据输出延迟” 配置(如 FLASH 数据延迟 5ns,赋值为 5 )。 |
columnspace | FLSHxxCR1 [CAS] 位 | 列地址宽度(单位:bit),仅用于 “行 + 列” 地址架构的 FLASH(如某些 NAND FLASH)。 无列地址的 FLASH(如多数 NOR FLASH)赋值为 0 。 |
enableWordAddress | FLSHxxCR1 [WA] 位 | 是否使能4 字节地址(32 位寻址)。若 FLASH 容量超过 16MB(需 32 位地址),赋值为true ;否则为false 。 |
(4)AHB 命令配置(直接访问 FLASH 映射地址)
AHB 命令允许通过 “访问 MCU 内部映射地址” 直接读写 FLASH(无需传输结构体),需配置 AHB 命令对应的 LUT 序列与等待时间。
成员名 | 硬件寄存器映射 | 核心功能与使用说明 |
---|---|---|
AWRSeqIndex | FLSHxxCR2 [AWRSEQID] 位 | AHB 写命令(如*(uint32_t*)0x60000000 = 0x1234 )对应的 LUT 序列 ID。 |
AWRSeqNumber | FLSHxxCR2 [AWRSEQNUM] 位 | AHB 写命令执行的 LUT 序列数量(通常为1 )。 |
ARDSeqIndex | FLSHxxCR2 [ARDSEQID] 位 | AHB 读命令(如uint32_t val = *(uint32_t*)0x60000000 )对应的 LUT 序列 ID。 |
ARDSeqNumber | FLSHxxCR2 [ARDSEQNUM] 位 | AHB 读命令执行的 LUT 序列数量(通常为1 )。 |
AHBWriteWaitUnit | FLSHxxCR2 [AWRWAITUNIT] 位 | AHB 写等待的时间单位,枚举类型flexspi_ahb_write_wait_unit_t 取值(如kFLEXSPI_AhbWriteWaitUnit8AhbCycle 表示 8 个 AHB 周期)。 |
AHBWriteWaitInterval | FLSHxxCR2 [AWRWAIT] 位 | AHB 写等待的周期数,总等待时间 = AHBWriteWaitInterval * AHBWriteWaitUnit 。用于 FLASH 写操作后的等待(FLASH 需时间完成内部编程),需根据设备手册配置(如等待 1ms,AHB=240MHz 时,计算对应周期数)。 |
(5)其他配置
成员名 | 硬件寄存器映射 | 核心功能与使用说明 |
---|---|---|
enableWriteMask | FLSHCR4 寄存器 | 是否使能 DQS 引脚作为写掩码,仅用于 16 位数据宽度的设备(如某些 16 位 FLASH),用于地址对齐。 8 位设备赋值为 false 。 |
3. 典型使用示例(配置 16MB NOR FLASH)
// 1. 初始化外部设备配置结构体
flexspi_device_config_t deviceConfig = {.flexspiRootClk = 120000000U, // FlexSPI根时钟120MHz.isSck2Enabled = false, // RT1052不使能SCK2.flashSize = 16 * 1024U, // 16MB FLASH(16*1024 KByte).CSIntervalUnit = kFLEXSPI_CsIntervalUnit1SckCycle, // CS间隔单位:1个SCK周期.CSInterval = 10U, // CS间隔10个SCK周期(SCK=100MHz时100ns).CSHoldTime = 2U, // CS保持时间2个根时钟周期(~16.6ns).CSSetupTime = 2U, // CS建立时间2个根时钟周期(~16.6ns).dataValidTime = 5U, // 数据有效时间5ns.columnspace = 0U, // NOR FLASH无列地址.enableWordAddress = true, // 16MB需4字节地址.AWRSeqIndex = 2U, // AHB写对应LUT序列2.AWRSeqNumber = 1U, // 执行1个序列.ARDSeqIndex = 3U, // AHB读对应LUT序列3.ARDSeqNumber = 1U, // 执行1个序列.AHBWriteWaitUnit = kFLEXSPI_AhbWriteWaitUnit8AhbCycle, // AHB写等待单位:8个AHB周期.AHBWriteWaitInterval = 30000U, // 总等待时间:30000*8 = 240000 AHB周期(AHB=240MHz时1ms).enableWriteMask = false // 8位FLASH不使能写掩码
};// 2. 应用配置到A1端口(FLASH连接A1端口)
FLEXSPI_SetFlashConfig(FLEXSPI_BASE, &deviceConfig, kFLEXSPI_PortA1);