XSPI 默认会在数据传输前(片选高电平时)输出自动校准时钟,确保通信稳定性,但使用 GPIO 替代硬件片选驱动多 QSPI 设备时,该时钟会导致数据同步异常(如 LCD 点不亮)。核心解决方案:在最后一次写 XSPI_DCR2/CCR 寄存器与下一次传输之间,读写一次 XSPI_CALSOR 或 XSPI_CALSIR 寄存器,即可禁用自动校准时钟输出。
1. 问题背景:校准时钟引发的多设备驱动异常
1.1 应用场景
客户使用 STM32 的 XSPI 外设驱动两个 QSPI LCD,通过普通 GPIO 软件控制片选(分时切换两个设备),而非 XSPI 硬件片选引脚。
1.2 异常现象
LCD 初始化时无法点亮,逻辑分析仪抓取到关键问题:
- XSPI 在 GPIO 片选拉低(有效)前,会输出一串额外时钟(自动校准时钟);
- 硬件片选场景下,片选高电平时的校准时钟不影响通信,但 GPIO 软件片选无法同步该时钟,导致 LCD 接收无效数据,启动失败。
2. 核心原理:XSPI 自动校准的触发与禁用规则
2.1 自动校准的作用
XSPI 的自动校准是默认行为,传输前通过输出校准时钟更新校准参数,补偿时序偏差,确保高速通信的可靠性。
2.2 禁用自动校准的关键条件
根据 STM32 参考手册,满足以下条件即可跳过自动校准(不输出校准时钟):
- 在最后一次写 XSPI_DCR2(设备配置寄存器 2)或 XSPI_CCR(命令配置寄存器) 之后;
- 在下一次 XSPI 传输启动之前;
- 执行一次XSPI_CALSOR(校准输出寄存器)或 XSPI_CALSIR(校准输入寄存器)的读写操作。
2.3 核心逻辑
手动读写 CALSOR/CCSIR 寄存器后,XSPI 会认为已通过软件配置校准参数,无需再执行自动校准,从而避免额外时钟输出。
3. 实操方案:代码实现与验证
以 STM32H7S3 Nucleo 板为例,外扩 Quad PSRAM 验证方案有效性,核心是在 XSPI 传输前添加 CALSOR 寄存器操作,以下是完整实现代码。
3.1 关键代码修改(禁用校准时钟)
XSPI 写操作(禁用校准时钟)
uint8_t XSPI_RAM_Write(uint32_t addr, uint8_t *data, uint32_t size) {
XSPI_RegularCmdTypeDef sCommand = {0};
__IO uint32_t xspi_calsor; // 存储校准寄存器值
// 1. 配置XSPI命令(根据设备手册调整指令、地址模式等参数)
sCommand.OperationType = HAL_XSPI_OPTYPE_COMMON_CFG;
sCommand.IOSelect = HAL_XSPI_SELECT_IO_3_0;
sCommand.Instruction = 0x38; // QSPI写指令(需匹配设备)
sCommand.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
sCommand.InstructionWidth = HAL_XSPI_INSTRUCTION_8_BITS;
sCommand.AddressMode = HAL_XSPI_ADDRESS_4_LINES;
sCommand.AddressWidth = HAL_XSPI_ADDRESS_24_BITS;
sCommand.Address = addr;
sCommand.DataLength = size;
sCommand.AlternateBytesMode = HAL_XSPI_ALT_BYTES_NONE;
sCommand.DataMode = HAL_XSPI_DATA_4_LINES;
sCommand.DummyCycles = 0;
sCommand.DTRMode相关 = HAL_XSPI_DTR_DISABLE; // 禁用DTR模式
// 2. 发送XSPI命令(配置DCR2/CCR寄存器)
if (HAL_XSPI_Command(&hxspi1, &sCommand, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
return false;
}
// 3. 关键操作:读写CALSOR寄存器,禁用自动校准
xspi_calsor = hxspi1.Instance->CALSOR; // 读CALSOR
hxspi1.Instance->CALSOR = xspi_calsor; // 写回原数值(无修改,仅触发禁用逻辑)
// 4. 传输数据(此时无校准时钟输出)
if (HAL_XSPI_Transmit(&hxspi1, data, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
return false;
}
return true;
}
XSPI 读操作(保留校准时钟,对比验证)
uint8_t XSPI_RAM_Read(uint32_t addr, uint8_t *data, uint32_t size) {
XSPI_RegularCmdTypeDef sCommand = {0};
// 配置XSPI读命令(指令0xEB为快速读指令,需匹配设备)
sCommand.OperationType = HAL_XSPI_OPTYPE_COMMON_CFG;
sCommand.IOSelect = HAL_XSPI_SELECT_IO_3_0;
sCommand.Instruction = 0xEB;
sCommand.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
sCommand.InstructionWidth = HAL_XSPI_INSTRUCTION_8_BITS;
sCommand.AddressMode = HAL_XSPI_ADDRESS_4_LINES;
sCommand.AddressWidth = HAL_XSPI_ADDRESS_24_BITS;
sCommand.Address = addr;
sCommand.DataLength = size;
sCommand.AlternateBytesMode = HAL_XSPI_ALT_BYTES_NONE;
sCommand.DataMode = HAL_XSPI_DATA_4_LINES;
sCommand.DummyCycles = 6; // 读操作需配置虚拟周期
// 发送命令+接收数据(未操作CALSOR,保留校准时钟)
if (HAL_XSPI_Command(&hxspi1, &sCommand, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
return false;
}
if (HAL_XSPI_Receive(&hxspi1, data, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
return false;
}
return true;
}
3.2 验证结果
通过逻辑分析仪抓取波形,关键结论:
- 写操作(已禁用校准):GPIO 片选拉低前,无额外校准时钟输出,数据传输正常;
- 读操作(未禁用校准):片选拉低前,仍有校准时钟输出(符合预期);
- 实际应用:将该写操作逻辑移植到双 QSPI LCD 驱动中,成功避免无效时钟,LCD 正常点亮。
4. 关键注意事项
- 校准参数保障:禁用自动校准前,需确保 XSPI 已完成有效校准(如初始化时执行一次读 ID、复位命令,触发自动校准获取初始参数);
- 寄存器选择:CALSOR 和 CALSIR 均可,推荐使用 CALSOR(操作更简洁,无需额外配置);
- 命令顺序:必须在 “最后一次写 DCR2/CCR”(即 HAL_XSPI_Command 之后)和 “传输启动”(HAL_XSPI_Transmit/Receive 之前)之间操作 CALSOR;
- 设备兼容性:该方案适用于所有支持 XSPI 的 STM32 系列(如 H7、U5、H5),需根据设备手册调整指令、地址宽度等参数。
XSPI 的自动校准时钟是默认的可靠性保障,但在 GPIO 软件片选、多设备分时驱动等场景下会成为障碍。通过 “传输前读写 CALSOR/CCSIR 寄存器” 的简单操作,即可禁用该时钟,且不影响通信稳定性。核心是利用 XSPI 的软件校准优先级高于自动校准的设计逻辑,兼顾灵活性与可靠性。