STM32 QSPI接口GD/W25Qxx配置简要
- ????本篇会具体涉及介绍Winbond(华邦)和GD(兆易创新) NOR flash相关型号指令差异。由于网络上可以搜索到很多相关QSPI相关知识内容,不对QSPI通讯协议做深度解析。
- ????首先确保所使用的STM32型号,硬件上是否支持QSPI功能。
- 由于GD25QXX和对应的W25Qxx 芯片型号较多,芯片出厂,默认状态寄存器关键位存在差异,在没有进行状态寄存器关键位做配置的情况下。两者的驱动代码不能互为通用,代码上可以通过型号辨别,以及对状态寄存器关键位的使能配置,可以整合为一套通用的驱动代码。
- ????QSPI支持三种工作模式:间接模式、状态轮询模式和内存映射模式:
- 间接模式:使用QSPI寄存器执行全部操作。
- 状态轮询模式:周期性读取外部FLASH状态寄存器,当标志位置1时会产生中断(如擦除或烧写完成,产生中断)。
- 内存映射模式:外部FLASH映射到微控制器地址空间,从而系统将其视作内部存储器进行访问。
在间接模式下也可以切换为内存映射模式进行访问,切换过程需要重新对QSPI外设进行复位和重新初始化QSPI操作,代码操作比较麻烦,好处就是提升了读取访问速度。如果需要进行擦除或编程操作,还需要切换为间接模式下进行。
- QUADSPI 功能框图(见STM32H750参考手册)
图中BK2只有在双闪存模式下才需要。
-
4线SPI结构框图
-
????QSPI通讯序列
QUADSPI 通过命令与 FLASH 通信 每条命令包括指令、地址、交替字节、空指令和数据这 五个阶段 任一阶段均可跳过,但至少要包含指令、地址、交替字节或数据阶段之一。
nCS 在每条指令开始前下降,在每条指令完成后再次上升
对于通讯的整个过程,只需要留意3个状态:WIP(BUSY)用于查看擦除/编程操作是否正在进行、QE用于使能4线模式(Quad)和WREN写使能位其实和WIP位对应,该位决定接下来是否可以进行编程、擦写操作。
- ????GD25Q64读写测试:
- ????W25Q64读写测试:
- 基于QSPI片外flash下载算法实现的片外启动APP程序:
???? NOR FLASH 存储器介绍
- ????Winbond(华邦)W25Q64存储器功能控制结构图
- GD(兆易创新)GD25Q64
- 内存空间结构: 一页(Page)256字节,4K(4096 字节)为一个扇区(Sector),16个扇区为1块(Block),容量为8M字节,共有128个块,2048 个扇区。
- 支持页编程。每页大小由256字节组成,每页的256字节用一次页编程指令即可完成。
- 擦除指令通用: 16页(1个扇区4K)、32K、64K、全片擦除操作。
两者常规操作指令互为通用。在QE位都配置为使能状态情况下,除了在读取厂商ID有差异外,读取数据功能函数通用。测试了基于W25QXX制作的片外QSPI下载算法文件都可以兼容GD25XX使用。(前提是下载算法中没有对应闪存厂商ID校验,芯片相关状态寄存器位没有写保护)
????Nor Flash操作流程
- 写入操作时:
写入操作前,必须先进行写使能位.
每个数据位只能由1改写为0,不能由0改写为1
写入数据前必须先擦除,擦除后,所有数据位变为1。.(删除目录实际上是填充0xff)
擦除必须按最小擦除单元进行(一个扇区:4kb)
连续写入多字节时,最多写入一页(256字节)的数据,超过页尾位置的数据,会回到页首覆盖写入
写入操作结束后,芯片进入忙状态,不响应新的读写操作
- 读取操作时:
直接调用读取时序,快速读取(Fast Read Quad I/O (EBh)),需要QE=1,可以不需要使能
Write Enable (WREN) (06H)
,没有页的限制,读取操作结束后不会进入忙状态,但不能在忙状态时读取.
????Nor flash状态寄存器介绍
- ????winbond(华邦)W25QXX状态寄存器访问指令
- ????具体状态寄存器位说明
???? GD25QXX状态寄存器
- ????GD(兆易创新)GD25QXX状态寄存器总表:
????状态寄存器差异化说明
- ????QE位:该位属于非易失性(non-volatile),数据在断电后仍然能够保持的特性。一旦该位配置位1,将一直保持其状态,不会断电丢失。
- ????winbond(华邦)W25QXX,该位出厂默认状态是根据芯片具体型号决定该位默认状态是否为
1
- When the QE bit is set to a 0 state (factory default for part numbers with ordering options “
IM
”&“JM
”).- When the QE bit is set to a 1 (factory fixed default for part numbers with ordering options “
IQ
” & “JQ
”),
- ????GD(兆易创新)GD25Qxx,该位出厂默认状态是
0
,如需要使用QSPI 模式,必须先将该位配置为1才行。
- ✨QE位的使能,对于QSPI 4线Quad访问方式尤为重要。
????指令介绍
-
????winbond(华邦)W25Q64指令:
-
????GD(兆易创新)GD25Q64指令集
????状态寄存器配置
- ????状态寄存器1,QE位配置流程说明
????针对GD(兆易创新)GD25Q64,经测试,如需修改QE位,可以通过使用写状态寄存器1(0x01)命令,读取2个字节数据(S0-S7、S8-S16),再进行修改。
- ????个人测试时,如果单独写状态寄存器2的QE位,会写入不成功。
- QE位是否配置成功,可以通过读状态寄存器(0x35),进行查询。
-
Write Status Register (WRSR) (01H or 31H or 11H)
The Write Status Register (WRSR) command allows new values to be written to the Status Register. Before it can be accepted, a Write Enable (WREN) command must previously have been executed. After the Write Enable (WREN)
command has been decoded and executed, the device sets the Write Enable Latch (WEL).
The Write Status Register (WRSR) command has no effect on S15, S10, S1 and S0 of the Status Register. For command code of “01H” / “31H” / “11H”, the Status Register bits S7~S0 / S15~S8 / S23~S16 would be written. CS# must be driven high after the eighth bit of the data byte has been latched in. Otherwise, the Write Status Register (WRSR) command is not executed. As soon as CS# is driven high, the self-timed Write Status Register cycle (whose duration is tW) is initiated. While the Write Status Register cycle is in progress, the Status Register may still be read to check the value of the Write In Progress (WIP) bit. The Write In Progress (WIP) bit is 1 during the self-timed Write Status Register cycle, and is 0 when it is completed. When the cycle is completed, the Write Enable Latch (WEL) is reset.
The Write Status Register (WRSR) command allows the user to change the values of the Block Protect (BP4, BP3, BP2, BP1, and BP0) bits, to define the size of the area that is to be treated as read-only. The Write Status Register (WRSR) command also allows the user to set or reset the Status Register Protect (SRP1 and SRP0) bits in accordance with the Write Protect (WP#) signal. The Status Register Protect (SRP1 and SRP0) bits and Write Protect (WP#) signal allow the device to be put in the Hardware Protected Mode. The Write Status Register (WRSR) command is not executed once the Hardware Protected Mode is entered -
???? 时序图:
-
????读状态寄存器代码实现:
int qspi_flash_read_status1(uint8_t *status) {//读状态寄存器1
QSPI_CommandTypeDef command;
command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
command.AddressSize = QSPI_ADDRESS_24_BITS;
command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
command.DdrMode = QSPI_DDR_MODE_DISABLE;
command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
command.Instruction = READ_STATUS_REG_CMD; //0x05,读取:S7-S0
command.AddressMode = QSPI_ADDRESS_NONE;
command.DataMode = QSPI_DATA_1_LINE;
command.DummyCycles = 0;
command.NbData = 1;//1个字节
if (HAL_QSPI_Command(&hqspi, &command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) !=
HAL_OK) {
return HAL_ERROR;
}
if (HAL_QSPI_Receive(&hqspi, status, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) !=
HAL_OK) {
return HAL_ERROR;
}
return HAL_OK;
}
int qspi_flash_read_status2(uint8_t *status) {读状态寄存器2
QSPI_CommandTypeDef command= {0};
command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
command.AddressSize = QSPI_ADDRESS_24_BITS;
command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
command.DdrMode = QSPI_DDR_MODE_DISABLE;
command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
command.Instruction = READ_STATUS2_REG_CMD;//0x35读取:S15-S8
command.AddressMode = QSPI_ADDRESS_NONE;
command.DataMode = QSPI_DATA_1_LINE;
command.DummyCycles = 0;
command.NbData = 1;
if (HAL_QSPI_Command(&hqspi, &command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) !=
HAL_OK) {
return HAL_ERROR;
}
if (HAL_QSPI_Receive(&hqspi, status, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) !=
HAL_OK) {
return HAL_ERROR;
}
return HAL_OK;
}
- ????写状态寄存器代码实现:
int qspi_flash_write_status(uint8_t status1_val, uint8_t status2_val) {写状态寄存器1-2
QSPI_CommandTypeDef command = {0};
uint8_t buf[2];
TxCplt = 0;
qspi_flash_write_enable();
command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
command.AddressSize = QSPI_ADDRESS_24_BITS;
command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
command.DdrMode = QSPI_DDR_MODE_DISABLE;
command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
command.SIOOMode = QSPI_SIOO_INST_ONLY_FIRST_CMD;
command.Instruction = WRITE_STATUS_REG_CMD;
command.DummyCycles = 0;
command.AddressMode = QSPI_ADDRESS_NONE;
command.DataMode = QSPI_DATA_1_LINE;
command.NbData = 2;//2个字节(S0-S7,S7-S16)
buf[0] = status1_val;
buf[1] = status2_val;
if (HAL_QSPI_Command(&hqspi, &command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) !=
HAL_OK) {
return HAL_ERROR;
}
if (HAL_QSPI_Transmit(&hqspi, buf, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) !=
HAL_OK) {
return HAL_ERROR;
}
if (qspi_flash_atuo_polling_mem_ready() != HAL_OK) {
return HAL_ERROR;
}
return HAL_OK;
}
????读数据操作
- 主要介绍4线QUAD操作指令。
- ????Quad I/O Fast Read (EBH)
Quad I/O快速读取命令可以通过在输入3字节地址(A23-A0)之后设置“连续读取模式”位(M7-0),从而进一步降低命令开销。如果“连续读模式”位(M5-4)=(1,0),则下一个四I/O快速读命令(在CS#升降之后)不需要EBH命令代码。如果“连续读模式”位(M5-4)=(1,0),下一个命令需要该命令代码,从而恢复正常操作。重置命令之前也可以用来在发出正常命令前重置(M7-0)。
⏰Dummy Cycle空指令周期
- ????在STM32H7参考手册上,QUADSPI 通信配置寄存器 (QUADSPI_CCR),指定 CLK 周期数范围一般: 0 - 31。
对于stm32H7一般在快速读取数据时,默认设定的是6个Dummy Cycle,适用的最大工作频率是104MHz.验证这个参数是否设定合理,验证方法,读写数据页内容多次进行查看,验证数据是否正确即可。如果该参数设置不对,读取的数据和写入的数据对应不上,或者会出全部是0、或者是一个其他的uint8_t
类型任意的固定或不固定的数值,也可能出现读取出来的数据和写入的数据和对应的地址错位的情况。
- Winbond(华邦)W25Q64:没有找到相关描述
- GD(兆易创新)GD25Q64
- ????读数据实现
/**
* @brief 读NOR Flash
* @note 从指定地址开始读取指定长度的数据
* @param pbuf : 读取到数据保存的地址
* @param pbuf : 指定开始读取的地址
* @param datalen: 指定读取数据的字节数
* @retval 无
*/
void norflash_read(uint8_t *pbuf, uint32_t addr, uint16_t datalen)
{
QSPI_CommandTypeDef qspi_command_struct = {0};
qspi_flash_write_enable();//使能QE位
qspi_command_struct.InstructionMode = QSPI_INSTRUCTION_1_LINE;
qspi_command_struct.Instruction = QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD;//Quad I/O Fast Read EBH
qspi_command_struct.AddressMode = QSPI_ADDRESS_4_LINES;
qspi_command_struct.AddressSize = QSPI_ADDRESS_24_BITS;
qspi_command_struct.Address = addr;
qspi_command_struct.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
qspi_command_struct.DummyCycles = 6; //此处必须要有空闲等待时间,否则数据和地址会出现错位
qspi_command_struct.DataMode = QSPI_DATA_4_LINES;
qspi_command_struct.NbData = datalen;
qspi_command_struct.DdrMode = QSPI_DDR_MODE_DISABLE;
qspi_command_struct.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
qspi_command_struct.SIOOMode = QSPI_SIOO_INST_ONLY_FIRST_CMD;
HAL_QSPI_Command(&hqspi, &qspi_command_struct, HAL_QSPI_TIMEOUT_DEFAULT_VALUE);
HAL_QSPI_Receive(&hqspi, pbuf, HAL_QSPI_TIMEOUT_DEFAULT_VALUE);
}
????擦除和写入操作
之所以将擦除和写入放在一起说明,是因为nor flash写操作前,需要先对当前要写入数据的地址扇区进行擦除操作,目的是为了将要写入的区域,保证为空(0xff),才能保证写入的数据的准确性。需要注意的是,最小擦除大小为扇区单位(4K)。
- ????扇区擦除操作:
int qspi_flash_erase_sector(uint32_t address) {
uint32_t EraseStartAddress = address;
EraseStartAddress &=0x0FFFFFFF; //最大可寻址范围
EraseStartAddress = EraseStartAddress - EraseStartAddress % 0x1000;//起始地址-扇区地址
QSPI_CommandTypeDef command = {0};
qspi_flash_write_enable();//写使能QE=1
command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
command.AddressSize = QSPI_ADDRESS_24_BITS;//24位地址,可寻址范围小于或等于16MB
command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
command.DdrMode = QSPI_DDR_MODE_DISABLE;
command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
command.Instruction = SUBSECTOR_ERASE_4_BYTE_ADDR_CMD;//擦除指令0x20
command.AddressMode = QSPI_ADDRESS_1_LINE;
command.Address = EraseStartAddress;
command.DataMode = QSPI_DATA_NONE;
command.DummyCycles = 0;
if (HAL_QSPI_Command(&hqspi, &command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) !=
HAL_OK) {
return HAL_ERROR;
}
if (qspi_flash_atuo_polling_mem_ready() != HAL_OK) {
return HAL_ERROR;
}
return HAL_OK;
}
- ????写操作
/* 扇区缓存 */
static uint8_t g_norflash_buf[4096];
/**
* @brief 写NOR Flash
* @note 在指定地址开始写入指定长度的数据,该函数带擦除操作
* NOR Flash一般是:256个字节为一个Page,4096个字节为一个Sector,16个Sector为1个Block
* 擦除的最小单位为Sector
* @param pbuf : 待写入数据的起始地址
* @param addr : 指定开始写入数据的地址
* @param datalen: 指定写入数据的字节数
* @retval 无
*/
void norflash_write(uint8_t *pbuf, uint32_t addr, uint16_t datalen)
{
uint32_t secpos;
uint16_t secoff;
uint16_t secremain;
uint16_t i;
uint8_t *norflash_buf;
norflash_buf = g_norflash_buf; /* 扇区缓存 */
secpos = addr / 4096; /* 扇区索引 */
secoff = addr % 4096; /* 写入起始地址在当前扇区的偏移量 */
secremain = 4096 - secoff; /* 计算当扇区剩余的字节数 */
if (datalen <= secremain) /* 不大于4096个字节 */
{
secremain = datalen;
}
while (1)
{
norflash_read(norflash_buf, secpos * 4096, 4096); /* 读出当前整个扇区的内容,用于擦除扇区后恢复非写入区域的数据 */
for (i=0; i<secremain; i++) /* 校验数据 */
{
if (norflash_buf[secoff + i] != 0xFF) /* 写入区域中有非0xFF的数据,就需要擦除 */
{
break;
}
}
if (i < secremain) /* 需要擦除 */
{
// /* 擦除整个扇区 */
qspi_flash_erase_sector(secpos) ;
for (i=0; i<secremain; i++) /* 将待写入的数据,先写入扇区缓存 */
{
norflash_buf[i + secoff] = pbuf[i];
}
norflash_write_nocheck(norflash_buf, secpos * 4096, 4096); /* 写入整个扇区 */
}
else /* 不需要擦除,可以直接写入 */
{
norflash_write_nocheck(pbuf, addr, secremain); /* 直接写扇区剩余的空间 */
}
if (datalen == secremain) /* 写入结束 */
{
break;
}
else /* 写入未结束 */
{
secpos++; /* 扇区索引增1 */
secoff = 0; /* 扇区偏移位置为0 */
pbuf += secremain; /* 偏移pbuf指针地址secremain个已写入的字节数 */
addr += secremain; /* 偏移写入地址secremain个已写入的字节数 */
datalen -= secremain; /* 计算剩余写入的字节数 */
if (datalen > 4096) /* 剩余数据量超过4096,只能先写入一个扇区数据 */
{
secremain = 4096;
}
else /* 剩余数据量不超过4096,可一次性写入 */
{
secremain = datalen;
}
}
}
}
????复位nor flash操作
- GD25Q64复位使能和复位指令:Enable Reset (66H) and Reset (99H)
- 复位内容:Volatile settings:易失性设置,包括易失性状态寄存器位、写使能锁存状态(WEL)、编程/擦除暂停状态、读取参数设置(P7-P0)、连续读取模式位设置(M7-M0)和环绕位设置(W6-W4).
- 复位流程:执行复位命令序列包括:先发送使能复位命令(66H),然后将CS#拉高,再发送复位命令(99H),最后将CS#拉高。设备在接受复位命令后会花费大约tRST / tRST_E的时间来完成复位,在此期间不会接受任何其他命令。
- ????High-Z时间:
- STM32H750 ,时钟配置为400MHz,执行一个
__NOP( )
控制指令时间:1/400MHz=2.5ns,
-
????复位命令发出之后,到nor flash 执行完内部复位所需的时间:12ms
-
来自:
https://doc.embedfire.com/mcu/stm32/h743prov/hal/zh/latest/book/QSPI.html#qspi-7
**
* @brief 复位QSPI存储器。
* @param QSPIHandle: QSPI句柄
* @retval 无
*/
static uint8_t QSPI_ResetMemory()
{
QSPI_CommandTypeDef s_command;
/* 初始化复位使能命令 */
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
s_command.Instruction = RESET_ENABLE_CMD;//0x66
s_command.AddressMode = QSPI_ADDRESS_NONE;
s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
s_command.DataMode = QSPI_DATA_NONE;
s_command.DummyCycles = 0;
s_command.DdrMode = QSPI_DDR_MODE_DISABLE;
s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
/* 发送命令 */
if (HAL_QSPI_Command(&QSPIHandle, &s_command,
HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
return QSPI_ERROR;
}
/* 发送复位存储器命令 0x99*/
s_command.Instruction = RESET_MEMORY_CMD;
if (HAL_QSPI_Command(&QSPIHandle, &s_command,
HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
return QSPI_ERROR;
}
/* 配置自动轮询模式等待存储器就绪 */
if (QSPI_AutoPollingMemReady(HAL_QPSI_TIMEOUT_DEFAULT_VALUE) !=
QSPI_OK) {
return QSPI_ERROR;
}
return QSPI_OK;
}
????QSPI外设引脚配置
- ????QSPI外设引脚配置不是固定的,可以根据实际使用情况,可以映射到任意支持的引脚上。
- ????QSPI引脚配置:需要将外设引脚的速度设置为非常高(Very HIGH)。
- ????需要注意CS引脚配置,不能使能
Fast Mode
.
void MX_QUADSPI_Init(void)
{
/* USER CODE BEGIN QUADSPI_Init 0 */
/* USER CODE END QUADSPI_Init 0 */
/* USER CODE BEGIN QUADSPI_Init 1 */
/* USER CODE END QUADSPI_Init 1 */
hqspi.Instance = QUADSPI;
hqspi.Init.ClockPrescaler = 1; //200MHz/(1+1)=100MHz
hqspi.Init.FifoThreshold = 32; //最大支持32
hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE;
hqspi.Init.FlashSize = 22;//8MB= 2^[FSIZE+1] =1<<23
hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_6_CYCLE;//信号(CS)的高电平持续时间
hqspi.Init.ClockMode = QSPI_CLOCK_MODE_0;/*时钟模式选择模式0,nCS为高电平(片选释放),传输数据CLK保持低电平*/
hqspi.Init.FlashID = QSPI_FLASH_ID_1;
hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE;
if (HAL_QSPI_Init(&hqspi) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN QUADSPI_Init 2 */
/* USER CODE END QUADSPI_Init 2 */
}
-
有关信号时间参数信息:
-
????QSPI初始化配置:(参考野火内容介绍:
https://doc.embedfire.com/mcu/stm32/h743prov/hal/zh/latest/book/QSPI.html#qspi-7
)
/**
* @brief 初始化QSPI存储器
* @retval QSPI存储器状态
*/
uint8_t BSP_QSPI_Init(void)
{
QSPI_CommandTypeDef s_command;
uint8_t value = W25Q256JV_FSR_QE;//0X02
/* QSPI存储器复位 */
if (QSPI_ResetMemory() != QSPI_OK) {
return QSPI_NOT_SUPPORTED;
}
/* 使能写操作 */
if (QSPI_WriteEnable() != QSPI_OK) {
return QSPI_ERROR;
}
/* 设置四路使能的状态寄存器,使能四通道IO2和IO3引脚 */
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
s_command.Instruction = WRITE_STATUS_REG2_CMD;
s_command.AddressMode = QSPI_ADDRESS_NONE;
s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
s_command.DataMode = QSPI_DATA_1_LINE;
s_command.DummyCycles = 0;
s_command.NbData = 1;
s_command.DdrMode = QSPI_DDR_MODE_DISABLE;
s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
/* 配置命令 */
if (HAL_QSPI_Command(&QSPIHandle, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
return QSPI_ERROR;
}
/* 传输数据 */
if (HAL_QSPI_Transmit(&QSPIHandle, &value, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
return QSPI_ERROR;
}
/* 自动轮询模式等待存储器就绪 */
if (