STM32标准库(Standard Peripheral Library,SPL)作为早期固件库,为QSPI外设提供了寄存器级抽象,其驱动架构与HAL库存在显著差异。标准库通过stm32fxxx_qspi.c/h文件提供QSPI控制函数,开发者需直接配置QUADSPI_TypeDef结构体中的寄存器,而非使用HAL库的句柄机制。
一、标准库QSPI驱动核心架构
标准库采用分层驱动模型,其核心组件包括:
| 驱动层 | 功能模块 | 关键函数/宏 |
|---|---|---|
| 硬件抽象层 | 寄存器映射 | QUADSPI->CR,QUADSPI->CCR |
| 配置层 | 时钟/引脚初始化 | RCC_AHB3PeriphClockCmd(),GPIO_Init() |
| 命令层 | 指令发送控制 | QSPI_SendCommand(),QSPI_ReceiveData() |
| 状态层 | 标志位管理 | QSPI_GetFlagStatus(),QSPI_ClearFlag() |
标准库的QSPI初始化需手动配置所有时序参数,以下为典型配置流程:
// 1. 时钟使能 RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_QSPI, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); // 2. GPIO配置(以PB2/6/10/11/12为例) GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_6 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOB, &GPIO_InitStruct); // 3. 复用功能映射 GPIO_PinAFConfig(GPIOB, GPIO_PinSource2, GPIO_AF_QSPI); GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_QSPI); GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_QSPI); GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_QSPI); GPIO_PinAFConfig(GPIOB, GPIO_PinSource12, GPIO_AF_QSPI); // 4. QSPI控制器初始化 QSPI_InitTypeDef QSPI_InitStruct; QSPI_StructInit(&QSPI_InitStruct); QSPI_InitStruct.QSPI_SShift = QSPI_SShift_HalfCycle; // 采样移位 QSPI_InitStruct.QSPI_Prescaler = 2; // 时钟分频 QSPI_InitStruct.QSPI_CKMode = QSPI_CKMode_Mode0; // 时钟模式 QSPI_InitStruct.QSPI_CSHTime = QSPI_CSHTime_1Cycle; // CS保持时间 QSPI_InitStruct.QSPI_FSize = 23; // 闪存容量(2^24 = 16MB) QSPI_InitStruct.QSPI_FSelect = QSPI_FSelect_1; // 闪存选择 QSPI_Init(&QSPI_InitStruct);二、标准库命令发送机制
标准库通过直接配置QUADSPI_CCR寄存器实现命令发送,与HAL库的QSPI_CommandTypeDef结构体形成对比:
// 标准库命令发送函数示例 uint8_t QSPI_SendCommand_STD(uint32_t instruction, uint32_t address, uint32_t dummyCycles, uint32_t dataSize) { // 等待总线空闲 while(QSPI_GetFlagStatus(QUADSPI_FLAG_BUSY) == SET); // 清除所有标志位 QSPI_ClearFlag(QUADSPI_FLAG_TC | QUADSPI_FLAG_TE | QUADSPI_FLAG_SM); // 配置CCR寄存器(核心差异点) QUADSPI->CCR = (instruction << QUADSPI_CCR_INSTRUCTION_Pos) | (QSPI_CCR_IMODE_1_LINE << QUADSPI_CCR_IMODE_Pos) | (QSPI_CCR_ADMODE_1_LINE << QUADSPI_CCR_ADMODE_Pos) | (address << QUADSPI_CCR_ADDRESS_Pos) | (dummyCycles << QUADSPI_CCR_DCYC_Pos) | (QSPI_CCR_DMODE_1_LINE << QUADSPI_CCR_DMODE_Pos) | (QSPI_CCR_ADSIZE_24_BITS << QUADSPI_CCR_ADSIZE_Pos) | (QSPI_CCR_FMODE_INDIRECT_WRITE << QUADSPI_CCR_FMODE_Pos); // 等待传输完成 while(QSPI_GetFlagStatus(QUADSPI_FLAG_TC) == RESET); return QSPI_GetFlagStatus(QUADSPI_FLAG_TE) == RESET ? SUCCESS : ERROR; }三、标准库与HAL库驱动对比分析
| 特性维度 | 标准库(SPL) | HAL库 |
|---|---|---|
| 抽象层级 | 寄存器级封装,直接操作QUADSPI->CCR | 高级句柄抽象,使用hqspi结构体 |
| 初始化复杂度 | 需手动配置时钟、GPIO、复用功能 | 通过MX_QSPI_Init()自动生成 |
| 命令配置 | 直接位操作寄存器 | 使用QSPI_CommandTypeDef结构体 |
| 中断处理 | 手动配置NVIC,查询标志位 | 回调函数机制(HAL_QSPI_TxCpltCallback()) |
| DMA支持 | 需手动配置DMA通道 | 集成DMA传输函数(HAL_QSPI_Transmit_DMA()) |
| 内存映射模式 | 直接配置QUADSPI_CR的MM位 | 专用函数HAL_QSPI_MemoryMapped() |
| 代码可移植性 | 较低,依赖具体型号 | 较高,跨系列兼容 |
四、标准库驱动NOR Flash的典型场景
以GD25Q64的Quad I/O Fast Read(0xEB指令)为例,标准库实现需关注以下技术细节:
// 标准库实现Quad I/O Fast Read uint32_t QSPI_QuadRead_STD(uint32_t addr, uint8_t *buffer, uint32_t size) { // 1. 配置CCR寄存器(指令阶段) QUADSPI->CCR &= ~(QUADSPI_CCR_INSTRUCTION_Msk | QUADSPI_CCR_IMODE_Msk | QUADSPI_CCR_ADMODE_Msk | QUADSPI_CCR_DMODE_Msk); // 2. 设置Quad I/O Fast Read指令(0xEB) QUADSPI->CCR |= (0xEB << QUADSPI_CCR_INSTRUCTION_Pos) | (QSPI_CCR_IMODE_1_LINE << QUADSPI_CCR_IMODE_Pos) | (QSPI_CCR_ADMODE_4_LINES << QUADSPI_CCR_ADMODE_Pos) | (QSPI_CCR_DMODE_4_LINES << QUADSPI_CCR_DMODE_Pos) | (6 << QUADSPI_CCR_DCYC_Pos); // 6个dummy周期 // 3. 设置地址(24位) QUADSPI->AR = addr; // 4. 配置数据长度 QUADSPI->DLR = size - 1; // 5. 启动传输并读取数据 for(uint32_t i = 0; i < size; i++) { while(!(QUADSPI->SR & QUADSPI_SR_FTF)); // 等待FIFO非空 buffer[i] = *(volatile uint8_t*)&QUADSPI->DR; } return size; }五、状态寄存器操作的差异实现
博客中提到的状态寄存器配置,在标准库中需通过间接模式实现:
// 标准库写状态寄存器(以GD25Q64的QE位配置为例) void QSPI_WriteStatusReg_STD(uint8_t reg1, uint8_t reg2) { // 1. 发送写使能指令(0x06) QSPI_SendCommand_STD(0x06, 0, 0, 0); // 2. 配置WRSR指令(0x01) QUADSPI->CCR = (0x01 << QUADSPI_CCR_INSTRUCTION_Pos) | (QSPI_CCR_IMODE_1_LINE << QUADSPI_CCR_IMODE_Pos) | (QSPI_CCR_DMODE_1_LINE << QUADSPI_CCR_DMODE_Pos); // 3. 写入两个状态寄存器字节(博客提到的关键点) QUADSPI->DLR = 1; // 2字节数据 QUADSPI->DR = reg1; while(!(QUADSPI->SR & QUADSPI_SR_FTF)); QUADSPI->DR = reg2; // 4. 等待写入完成 while(QSPI_GetFlagStatus(QUADSPI_FLAG_BUSY) == SET); }六、标准库驱动的最佳实践建议
时钟配置优化:标准库需手动计算时钟分频,确保QSPI时钟不超过闪存最大频率(GD25Q64为104MHz)。
时序参数校准:通过
QUADSPI_DCR寄存器配置CSHT(片选保持时间)和CKMODE(时钟模式),匹配闪存时序要求。错误处理机制:标准库缺乏自动错误恢复,需实现超时检测和重试逻辑:
#define QSPI_TIMEOUT_MAX 100000 uint32_t timeout = 0; while((QUADSPI->SR & QUADSPI_SR_TCF) == 0) { if(timeout++ > QSPI_TIMEOUT_MAX) { // 超时处理:复位QSPI外设 RCC_AHB3PeriphResetCmd(RCC_AHB3Periph_QSPI, ENABLE); RCC_AHB3PeriphResetCmd(RCC_AHB3Periph_QSPI, DISABLE); return ERROR_TIMEOUT; } }内存映射模式配置:标准库需直接操作
QUADSPI_CR的MM位和QUADSPI_LPTR寄存器:// 进入内存映射模式 QUADSPI->CR |= QUADSPI_CR_MM; // 使能内存映射 QUADSPI->LPTR = 0x10; // 设置延迟周期 // 此时可通过0x90000000地址直接访问闪存
标准库驱动虽然代码量较大,但提供了更精细的时序控制和寄存器级访问能力,适用于对性能要求苛刻或需要深度定制的应用场景。在迁移至HAL库时,需特别注意状态机管理和中断处理机制的差异。
参考来源
- STM32 QSPI接口驱动GD/W25Qxx配置简要