STM32F4 QuadSPI读取W25Q32JV厂商/设备ID值异常问题
QSPI读取Flash ID异常:如何发送3个0x00哑字节?
我需要向外部Flash发送指令0x90,随后发送3个0x00哑字节,其余配置无问题。厂商ID为1字节,设备ID为2字节,因此读取长度设为3,地址为0x00,但目前读取的值不正确。以下是相关代码:
QSPI初始化函数
static void MX_QUADSPI_Init(void) { /* QUADSPI parameter configuration*/ hqspi.Instance = QUADSPI; hqspi.Init.ClockPrescaler = 0; hqspi.Init.FifoThreshold = 4; hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; hqspi.Init.FlashSize = 23; hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_2_CYCLE; hqspi.Init.ClockMode = QSPI_CLOCK_MODE_0; hqspi.Init.FlashID = QSPI_FLASH_ID_2; hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE; if (HAL_QSPI_Init(&hqspi) != HAL_OK) { Error_Handler(); } }
读取Flash ID的函数
void QSPI_Flash_ID_Read(uint8_t *data, int length, uint32_t address){ QSPI_AutoPollingMemReady(&hqspi); QSPI_CommandTypeDef sCommand; sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; sCommand.AddressSize = QSPI_ADDRESS_24_BITS; sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* Reading Sequence ------------------------------------------------ */ sCommand.Instruction = 0x90; sCommand.AddressMode = QSPI_ADDRESS_1_LINE; sCommand.Address = address; sCommand.NbData = length; sCommand.DataMode = QSPI_DATA_4_LINES; sCommand.DummyCycles = 0; HAL_QSPI_Command(&hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE); HAL_QSPI_Receive(&hqspi, data, 1000); }
问题分析与解决方案
0x90指令的典型时序是指令(1线) → 24位地址(1线) → 3个0x00哑字节 → 4线读取ID数据。当前代码的核心问题是跳过了"发送3个0x00哑字节"的步骤,直接在地址后开始读取数据,导致时序不匹配。
方案:分阶段发送指令、哑字节并读取ID
通过两次QSPI命令完成完整时序:先发送指令、地址和3个哑字节,再保持CS拉低的状态下读取ID数据。修改后的函数如下:
void QSPI_Flash_ID_Read(uint8_t *data, int length, uint32_t address){ QSPI_AutoPollingMemReady(&hqspi); QSPI_CommandTypeDef sCommand; uint8_t dummy_bytes[3] = {0x00, 0x00, 0x00}; // 第一阶段:发送指令、24位地址、3个0x00哑字节 sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; sCommand.AddressMode = QSPI_ADDRESS_1_LINE; sCommand.AddressSize = QSPI_ADDRESS_24_BITS; sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; sCommand.Instruction = 0x90; sCommand.Address = address; sCommand.NbData = 3; // 指定发送3个哑字节 sCommand.DataMode = QSPI_DATA_1_LINE; // 用1线模式发送哑字节 sCommand.DummyCycles = 0; HAL_QSPI_Command(&hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE); HAL_QSPI_Transmit(&hqspi, dummy_bytes, 1000); // 第二阶段:读取ID数据(无需重复发送指令和地址) sCommand.InstructionMode = QSPI_INSTRUCTION_NONE; sCommand.AddressMode = QSPI_ADDRESS_NONE; sCommand.NbData = length; sCommand.DataMode = QSPI_DATA_4_LINES; HAL_QSPI_Command(&hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE); HAL_QSPI_Receive(&hqspi, data, 1000); }
关键说明
- 哑周期≠哑字节:
DummyCycles参数对应的是时钟周期数,不是字节数。你需要的是发送3个0x00字节,因此必须通过数据发送阶段完成,无法通过修改DummyCycles实现。 - 时钟速度验证:
ClockPrescaler = 0会让QSPI时钟等于AHB时钟,需确保该速度在你的Flash芯片允许范围内,否则可能导致数据读取错误。 - Flash容量匹配:
FlashSize = 23对应2^24字节(16MB),如果你的Flash容量不符,需调整该参数。
内容的提问来源于stack exchange,提问作者Ahmed Jamal
相关产品推荐
相关产品推荐

