为后面的QSPI内存映射铺垫。
为芯片内执行 (XIP) 执行代码。
参考例程:
C:\Users\admin\STM32Cube\Repository\STM32Cube_FW_F7_V1.16.0\Projects\STM32F723E-Discovery\Examples\QSPI\QSPI_ReadWrite
源码链接:
https://download.csdn.net/download/sudaroot/13092140
下面内容摘自《STM32H7xx参考手册中文版.PDF》
QSPI控制Flash W25Q64芯片用间接模式。
指令阶段
这一阶段,将在 QUADSPI_CCR[7:0] 寄存器的 INSTRUCTION 字段中配置的一条 8 位指令 发送到 FLASH,指定待执行操作的类型。
尽管大多数 FLASH 从 IO0/SO 信号(单线 SPI 模式)只能以一次 1 位的方式接收指令,但指 令阶段可选择一次发送 2 位(在双线 SPI 模式中通过 IO0/IO1)或一次发送 4 位(在四线 SPI 模式中通过 IO0/IO1/IO2/IO3)。这可通过 QUADSPI_CCR[9:8] 寄存器中的 IMODE[1:0] 字 段进行配置。
若 IMODE = 00,则跳过指令阶段,命令序列从地址阶段(如果存在)开始。
地址阶段
在地址阶段,将 1-4 字节发送到 FLASH,指示操作地址。待发送的地址字节数在
QUADSPI_CCR[13:12] 寄存器的 ADSIZE[1:0] 字段中进行配置。在间接模式和自动轮询模 式下,待发送的地址字节在 QUADSPI_AR 寄存器的 ADDRESS[31:0] 中指定。在内存映射 模式下,则通过 AXI(来自于 Cortex® 或 DMA)直接给出地址。
地址阶段可一次发送 1 位(在单线 SPI 模式中通过 SO)、2 位(在双线 SPI 模式中通过 IO0/IO1)或 4 位(在四线 SPI 模式中通过 IO0/IO1/IO2/IO3)。这可通过 QUADSPI_CCR[11:10] 寄存器中的 ADMODE[1:0] 字段进行配置。
若 ADMODE = 00,则跳过地址阶段,命令序列直接进入下一阶段(如果存在)。
交替字节阶段
不常用,直接设置跳过。
ABMODE = 00,则跳过交替字节阶段,命令序列直接进入下一阶段(如果存在)。
空指令周期阶段
在空指令周期阶段,给定的 1-31 个周期内不发送或接收任何数据,目的是当采用更高的时钟频 率时,给 FLASH 留出准备数据阶段的时间。这一阶段中给定的周期数在 QUADSPI_CCR[22:18] 寄存器的 DCYC[4:0] 字段中指定。在 SDR 和 DDR 模式下,持续时间被指定为一定个数的全 时钟周期。
若 DCYC 为零,则跳过空指令周期阶段,命令序列直接进入数据阶段(如果存在)。
空指令周期阶段的操作模式由 DMODE 确定。
为确保数据信号从输出模式转变为输入模式有足够的“周转”时间,使用双线和四线模式从FLASH 接收数据时,至少需要指定一个空指令周期。
数据阶段
在数据阶段,可从 FLASH 接收或向其发送任意数量的字节。
在间接模式和自动轮询模式下,待发送/接收的字节数在 QUADSPI_DLR 寄存器中指定。
在间接写入模式下,发送到 FLASH 的数据必须写入 QUADSPI_DR 寄存器。在间接读取模 式下,通过读取 QUADSPI_DR 寄存器获得从 FLASH 接收的数据。
在内存映射模式下,读取的数据通过 AXI 直接发送回 Cortex 或 DMA。
数据阶段可一次发送/接收 1 位(在单线 SPI 模式中通过 SO)、2 位(在双线 SPI 模式中通过 IO0/IO1)或 4 位(在四线 SPI 模式中通过 IO0/IO1/IO2/IO3)。这可通过 QUADSPI_CCR[15:14] 寄存器中的 ABMODE[1:0] 字段进行配置。
若 DMODE = 00,则跳过数据阶段,命令序列在拉高 nCS 时立即完成。这一配置仅可用于 仅间接写入模式。
硬件:STM32H743IIT6 + W25Q64
1、配置时钟树,QSPI时钟是240MHz:
2、设置QSPI参数。
Clock Prescaler:时钟分频系数。内部计算默认+1,FCLK(qspi) = Fquadspi_ker_ck / (Clock Prescaler + 1)。
上面时钟树设置的QSPI时钟是240MHz,而W25Q64芯片最大支持133MHz,我想2分频成120MHz。由于内部计算默认加1了,故分频240MHz / (2 - 1) = 120MHz.
Fifo Threshold:FIFO 阈值级别。设置FIFO阈值为4,即如果 FIFO 中存在 5 个以上空闲字节可供读或写,则 FTF 置 1。
Sample Shifting:采样移位。默认情况下,QUADSPI 在 FLASH 驱动数据后过半个 CLK 周期开始采集数据。使用该
位,可考虑外部信号延迟,推迟数据采集。0:不发生移位 ;1:移位半个周期。
Flash Size:Flash芯片大小。FSIZE+1 是对 FLASH 寻址所需的地址位数。
注:在间接模式下,FLASH 容量最高可达4GB(使用 32 位进行寻址),但在内存映射模式下的可寻址空间限制为 256MB。
W25Q64大小是64Mbit = 8MByte = 8388608Byte 即 2的23次方。但是内部默认+1,故填写23 - 1.
Chip select high time:片选高电平时间,根据W25Q64芯片手册的tSHSL参数设置,内部默认+1个周期。
由于W25Q64的tSHSL最小是50ns,故1s / 120MHz = 8.3ns。那么Chip select high time >= (50 / 8.3) - 1 , 考虑走线等因素结果向上加1.
Clock Mode:nCS 为高电平(片选释放)时,CLK 必须保持电平。对应的就是SPI协议的模式 0/模式 3 (Mode 0 / mode 3)。
根据自己芯片选择,W25Q64兼容两种时序,故选择高低电平都可行。
Flash ID:FLASH 选择。挂载在Bank1就叫 Flash ID1,只有在双QSPI芯片时操作单闪存时才有用。
Dual-flash mode:双闪存模式。该位激活双闪存模式,同时使用两个外部 FLASH 以将吞吐量和容量扩大一倍。
最后生成代码。
编程前,先讲一下编程思路。
1、W25Q64芯片在上电默认通讯方式是SPI,模式1或者模式3.
2、即如果我们要使用QSPI方式通讯,那么一定要先用SPI方式设置芯片下一次通讯方式为QSPI.
对此ST的HAL库代码需要用户自己先配置QSPI通讯的方式,支持单线,双线和四线通讯。
首先编程
第一步先把HAL_QSPI_Command()配置指令、地址、交替字节、空指令周期数和数据,以几线方式通讯。对应的就是下图。
第二步才是收发数据HAL_QSPI_Transmit()或HAL_QSPI_Receive();
看看发送指令函数HAL_StatusTypeDef HAL_QSPI_Command()参数中的命令结构体QSPI_CommandTypeDef。
把结构体成员整理了一下顺序,和上图的四线模式时序一样的顺序。
其实就是上图时序几个步骤设置几个数据IO通讯或者跳过。
typedef struct
{
uint32_t InstructionMode; // 指令模式,可设置使用 单线 双线 四线通讯,设0则跳过发送指令。
uint32_t Instruction; // 指令值
uint32_t AddressMode; // 地址模式,可设置使用 单线 双线 四线通讯,设0则跳过发送地址。
uint32_t AddressSize; // 地址位数,可8bit 16bit 24bit 32bit
uint32_t Address; // 地址值
uint32_t AlternateByteMode; // 交替模式,常见设0则跳过即可。
uint32_t AlternateBytesSize; // 交替字节位数,可8bit 16bit 24bit 32bit
uint32_t AlternateBytes; // 交替字节
uint32_t DummyCycles; // 空指令周期
uint32_t DataMode; // 数据模式,可设置使用 单线 双线 四线通讯,设0则跳过发送数据。
uint32_t NbData; // 发送或接收数据长度
uint32_t DdrMode; // DDR(双倍数据速率)模式,设0禁止。
uint32_t DdrHoldHalfCycle; // DDR模式下数据输出延时
uint32_t SIOOMode; // 在每个事务中发送指令或者只发一次指令
}QSPI_CommandTypeDef;
下一步就是调用QSPI收发数据函数
HAL_StatusTypeDef HAL_QSPI_Transmit (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout);
HAL_StatusTypeDef HAL_QSPI_Receive (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout);
可以看到没有写接收的字节长度,因为长度是在HAL_QSPI_Command()参数中QSPI_CommandTypeDef的NbData已指定。
QSPI单线即标准SPI协议,且W25Q64上电默认是标准的SPI协议,故无需额外设置。
// 以标准的SPI协议测试读ID指令
uint16_t W25Qx_SPI_ReadID(QSPI_HandleTypeDef *hqspi)
{
uint8_t temp[2] = {0};
QSPI_CommandTypeDef s_command;
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; // 指令单线传输
s_command.Instruction = MANUFACTURER_DEVICE_ID; // 读ID指令0x90
s_command.AddressMode = QSPI_ADDRESS_1_LINE; // 地址单线传输
s_command.Address = 0; // 地址 0
s_command.AddressSize = QSPI_ADDRESS_24_BITS; // 地址 24bit
s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; // 跳过交替字节模式
s_command.DummyCycles = 0; // 空指令周期数
s_command.DataMode = QSPI_DATA_1_LINE; // 数据单线传输
s_command.NbData = 2; // 数据接收长度
s_command.DdrMode = QSPI_DDR_MODE_DISABLE; // 禁止DDR模式
s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; // 禁止DDR延时
s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; // 在每个事务中发送指令
/* Send the command */
if (HAL_QSPI_Command(hqspi, &s_command, 1000) != HAL_OK)
{
return HAL_ERROR;
}
HAL_QSPI_Receive(hqspi, temp, 1000);
printf("ID = %X %X\r\n", temp[0], temp[1]);
return HAL_OK;
}
打印出“ID = EF 16”, W25Q64读取ID正确。
1、先用QSPI单线(标准SPI)协议把W25Q64通讯设成四线QSPI。
先检查一下寄存器2的QE标志位是否为1,然后发送QPI 模式(0x38)指令启动QSPI。
我的芯片默认寄存器2的QE标志位是为1,故直接设置QPI.再读ID.
uint16_t W25Qx_QSPI_ReadID( QSPI_HandleTypeDef *hqspi )
{
uint8_t temp[2] = {0};
QSPI_CommandTypeDef s_command = {0};
// SPI 启动QPI模式
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; // 指令QSPI单线传输
s_command.Instruction = ENTER_QPI_MODE; // 指令,0x38使能QPI模式
s_command.AddressMode = QSPI_ADDRESS_NONE; // 地址跳过
s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; // 交替字节模式跳过
s_command.DummyCycles = 0; // 空指令周期数
s_command.DataMode = QSPI_DATA_NONE; // 数据模式跳过
s_command.DdrMode = QSPI_DDR_MODE_DISABLE; // 禁止DDR模式
s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; // 禁止DDR模式数据延时
s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; // 在每个事务中发送指令
/* Send the command */
if (HAL_QSPI_Command(hqspi, &s_command, 1000) != HAL_OK)
{
return HAL_ERROR;
}
// QSPI 读ID
s_command.InstructionMode = QSPI_INSTRUCTION_4_LINES; // 指令QSPI四线传输
s_command.Instruction = MANUFACTURER_DEVICE_ID; // 指令,0x90读ID
s_command.AddressMode = QSPI_ADDRESS_4_LINES; // 地址QSPI四线传输
s_command.Address = 0; // 地址 0
s_command.AddressSize = QSPI_ADDRESS_24_BITS; // 地址 24bit
s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; // 交替字节模式跳过
s_command.DummyCycles = 0; // 空指令周期数
s_command.DataMode = QSPI_DATA_4_LINES; // 数据QSPI四线传输
s_command.NbData = 2; // 数据长度 1
s_command.DdrMode = QSPI_DDR_MODE_DISABLE; // 禁止DDR模式
s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; // 禁止DDR模式数据延时
s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; // 在每个事务中发送指令
/* Send the command */
if (HAL_QSPI_Command(hqspi, &s_command, 1000) != HAL_OK)
{
return HAL_ERROR;
}
HAL_QSPI_Receive(hqspi, temp, 1000);
printf("QSPI, ID = %X %X\r\n", temp[0], temp[1]);
return HAL_OK;
}
打印出“ QSPI, = EF 16 ”, W25Q64读取ID正确。
至于后面的后面的读写和标准SPI编程一样,微微改动即可,就不写了,直接上代码了。
状态轮询模式实际上就是读取某一W25Q64寄存器的值,进行and 或者 or 运算,与匹配值对比。
若一致运算结果与结果一致就退出,HAL_QSPI_AutoPolling()返回0表正确。
下面是官方例程代码。
typedef struct
{
uint32_t Match; // 匹配值
uint32_t Mask; // 掩码
uint32_t Interval; // 两次读操作间CLK周期数。
uint32_t StatusBytesSize; // 指定接收到的状态字节的大小
uint32_t MatchMode; // 用于确定匹配项的方法,AND或者OR运算
uint32_t AutomaticStop; // 指定匹配后是否停止自动轮询
}QSPI_AutoPollingTypeDef;
/* Configure automatic polling mode to wait the QUADEN bit=1 and WIP bit=0 */
s_config.Match = QSPI_SR_QUADEN;
s_config.Mask = QSPI_SR_QUADEN|QSPI_SR_WIP;
s_config.MatchMode = QSPI_MATCH_MODE_AND;
s_config.StatusBytesSize = 1;
s_config.Interval = 0x10;
s_config.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE;
s_command.InstructionMode = QSPI_INSTRUCTION_4_LINES;
s_command.Instruction = READ_STATUS_REG_CMD;
s_command.DataMode = QSPI_DATA_4_LINES;
if (HAL_QSPI_AutoPolling(hqspi, &s_command, &s_config, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return HAL_ERROR;
}
return HAL_OK;
过程大概是:
s_command写好读取的命令和协议,发送。
读取到的W25Q64某一寄存器的值,与Mask进行运算,运算方式由MatchMode指定,
若运算结果和Match一致则匹配成功。
看手册会发现有很多读指令。每个读指令用法都不一样,看红框圈出来的内容,
如(1-4-4)代表的是传输的三个步骤使用几线传输数据,指令用单线传输,地址和数据都是四线传输。
现在看读指令Fast Read Quad I/O (EBh),在地址传输完成后,要等3个字节,每个字节传输需要2个时钟周期,即6个时钟周期才能接收到数据。
下面是完整的驱动代码,我移植野火的QSPI驱动。
/*
* w25qxx_qspi.c
*
* Created on: 2020年10月19日
* Author: sudaroot
*/
#include
#include
#include "stm32h7xx_hal_qspi.h"
extern QSPI_HandleTypeDef hqspi;
static void W25Qx_QSPI_Delay(uint32_t ms);
static uint8_t W25Qx_QSPI_Addr_Mode_Init(void);
static uint8_t W25Qx_QSPI_ResetMemory (void);
static uint8_t W25Qx_QSPI_WriteEnable (void);
static uint8_t W25Qx_QSPI_AutoPollingMemReady (uint32_t Timeout);
/**
* @brief 初始化QSPI存储器
* @retval QSPI存储器状态
*/
uint8_t W25Qx_QSPI_Init(void)
{
QSPI_CommandTypeDef s_command;
uint8_t value = W25QxJV_FSR_QE;
/* QSPI存储器复位 */
if (W25Qx_QSPI_ResetMemory() != QSPI_OK)
{
return QSPI_NOT_SUPPORTED;
}
/* 使能写操作 */
if (W25Qx_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(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
!= HAL_OK)
{
return QSPI_ERROR;
}
/* 传输数据 */
if (HAL_QSPI_Transmit(&hqspi, &value, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
!= HAL_OK)
{
return QSPI_ERROR;
}
/* 自动轮询模式等待存储器就绪 */
if (W25Qx_QSPI_AutoPollingMemReady(W25QxJV_SUBSECTOR_ERASE_MAX_TIME) != QSPI_OK)
{
return QSPI_ERROR;
}
/* 配置地址模式为 4 字节 , 非W25Q256直接跳过*/
if (sFLASH_ID != 0XEF4019)
return QSPI_OK;
if (W25Qx_QSPI_Addr_Mode_Init() != QSPI_OK)
{
return QSPI_ERROR;
}
return QSPI_OK;
}
/**
* @brief 检查地址模式不是4字节地址,配置为4字节
* @retval QSPI存储器状态
*/
static uint8_t W25Qx_QSPI_Addr_Mode_Init(void)
{
QSPI_CommandTypeDef s_command;
uint8_t reg;
/* 初始化读取状态寄存器命令 */
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
s_command.Instruction = READ_STATUS_REG3_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(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
!= HAL_OK)
{
return QSPI_ERROR;
}
/* 接收数据 */
if (HAL_QSPI_Receive(&hqspi, ®, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
!= HAL_OK)
{
return QSPI_ERROR;
}
/* 检查寄存器的值 */
if ((reg & W25Q256FV_FSR_4ByteAddrMode) == 1) // 4字节模式
{
return QSPI_OK;
}
else // 3字节模式
{
/* 配置进入 4 字节地址模式命令 */
s_command.Instruction = Enter_4Byte_Addr_Mode_CMD;
s_command.DataMode = QSPI_DATA_NONE;
/* 配置并发送命令 */
if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
!= HAL_OK)
{
return QSPI_ERROR;
}
/* 自动轮询模式等待存储器就绪 */
if (W25Qx_QSPI_AutoPollingMemReady(
W25QxJV_SUBSECTOR_ERASE_MAX_TIME) != QSPI_OK)
{
return QSPI_ERROR;
}
return QSPI_OK;
}
}
/**
* @brief 从QSPI存储器中读取大量数据.
* @param pData: 指向要读取的数据的指针
* @param ReadAddr: 读取起始地址
* @param Size: 要读取的数据大小
* @retval QSPI存储器状态
*/
uint8_t W25Qx_QSPI_FastRead(uint8_t *pData, uint32_t ReadAddr, uint32_t Size)
{
QSPI_CommandTypeDef s_command;
if(Size == 0) return QSPI_OK;
/* 初始化读命令 */
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
s_command.Instruction = QUAD_INOUT_FAST_READ_CMD;
s_command.AddressMode = QSPI_ADDRESS_4_LINES;
s_command.AddressSize = QSPI_ADDRESS_24_BITS;
s_command.Address = ReadAddr;
s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
s_command.DataMode = QSPI_DATA_4_LINES;
s_command.DummyCycles = 0;
s_command.NbData = Size;
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(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
!= HAL_OK)
{
return QSPI_ERROR;
}
/* 接收数据 */
if (HAL_QSPI_Receive(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
!= HAL_OK)
{
return QSPI_ERROR;
}
return QSPI_OK;
}
/**
* @brief 从QSPI存储器中读取大量数据.
* @note 改指令只能使用在50MHz一下,本配置下不好用
* @param pData: 指向要读取的数据的指针
* @param ReadAddr: 读取起始地址
* @param Size: 要读取的数据大小
* @retval QSPI存储器状态
*/
uint8_t W25Qx_QSPI_Read(uint8_t *pData, uint32_t ReadAddr, uint32_t Size)
{
QSPI_CommandTypeDef s_command;
/* 初始化读命令 */
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
s_command.Instruction = READ_CMD; //READ_CMD;
s_command.AddressMode = QSPI_ADDRESS_1_LINE;
s_command.AddressSize = QSPI_ADDRESS_24_BITS;
s_command.Address = ReadAddr;
s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
s_command.DataMode = QSPI_DATA_1_LINE;
s_command.DummyCycles = 0;
s_command.NbData = Size;
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(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
!= HAL_OK)
{
return QSPI_ERROR;
}
/* 接收数据 */
if (HAL_QSPI_Receive(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
!= HAL_OK)
{
return QSPI_ERROR;
}
return QSPI_OK;
}
/**
* @brief 将大量数据写入QSPI存储器
* @param pData: 指向要写入数据的指针
* @param WriteAddr: 写起始地址
* @param Size: 要写入的数据大小
* @retval QSPI存储器状态
*/
uint8_t W25Qx_QSPI_Write(uint8_t *pData, uint32_t WriteAddr, uint32_t Size)
{
QSPI_CommandTypeDef s_command;
uint32_t end_addr, current_size, current_addr;
/* 计算写入地址和页面末尾之间的大小 */
current_addr = 0;
while (current_addr <= WriteAddr)
{
current_addr += W25QxJV_PAGE_SIZE;
}
current_size = current_addr - WriteAddr;
/* 检查数据的大小是否小于页面中的剩余位置 */
if (current_size > Size)
{
current_size = Size;
}
/* 初始化地址变量 */
current_addr = WriteAddr;
end_addr = WriteAddr + Size;
/* 初始化程序命令 */
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
s_command.Instruction = QUAD_INPUT_PAGE_PROG_CMD;
s_command.AddressMode = QSPI_ADDRESS_1_LINE;
s_command.AddressSize = QSPI_ADDRESS_24_BITS;
s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
s_command.DataMode = QSPI_DATA_4_LINES;
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;
/* 逐页执行写入 */
do
{
s_command.Address = current_addr;
s_command.NbData = current_size;
/* 启用写操作 */
if (W25Qx_QSPI_WriteEnable() != QSPI_OK)
{
return QSPI_ERROR;
}
/* 配置命令 */
if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
!= HAL_OK)
{
return QSPI_ERROR;
}
/* 传输数据 */
if (HAL_QSPI_Transmit(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
!= HAL_OK)
{
return QSPI_ERROR;
}
/* 配置自动轮询模式等待程序结束 */
if (W25Qx_QSPI_AutoPollingMemReady(HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != QSPI_OK)
{
return QSPI_ERROR;
}
/* 更新下一页编程的地址和大小变量 */
current_addr += current_size;
pData += current_size;
current_size =
((current_addr + W25QxJV_PAGE_SIZE) > end_addr) ?
(end_addr - current_addr) : W25QxJV_PAGE_SIZE;
} while (current_addr < end_addr);
return QSPI_OK;
}
/**
* @brief 擦除QSPI存储器的指定块
* @param BlockAddress: 需要擦除的块地址
* @retval QSPI存储器状态
*/
uint8_t W25Qx_QSPI_Erase_Block(uint32_t BlockAddress)
{
QSPI_CommandTypeDef s_command;
/* 初始化擦除命令 */
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
s_command.Instruction = SECTOR_ERASE_CMD;
s_command.AddressMode = QSPI_ADDRESS_1_LINE;
s_command.AddressSize = QSPI_ADDRESS_24_BITS;
s_command.Address = BlockAddress;
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 (W25Qx_QSPI_WriteEnable() != QSPI_OK)
{
return QSPI_ERROR;
}
/* 发送命令 */
if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
!= HAL_OK)
{
return QSPI_ERROR;
}
/* 配置自动轮询模式等待擦除结束 */
if (W25Qx_QSPI_AutoPollingMemReady(W25QxJV_SUBSECTOR_ERASE_MAX_TIME) != QSPI_OK)
{
return QSPI_ERROR;
}
return QSPI_OK;
}
/**
* @brief 擦除整个QSPI存储器
* @retval QSPI存储器状态
*/
uint8_t W25Qx_QSPI_Erase_Chip(void)
{
QSPI_CommandTypeDef s_command;
/* 初始化擦除命令 */
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
s_command.Instruction = CHIP_ERASE_CMD;
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 (W25Qx_QSPI_WriteEnable() != QSPI_OK)
{
return QSPI_ERROR;
}
/* 发送命令 */
if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
!= HAL_OK)
{
return QSPI_ERROR;
}
/* 配置自动轮询模式等待擦除结束 */
if (W25Qx_QSPI_AutoPollingMemReady(W25QxJV_BULK_ERASE_MAX_TIME) != QSPI_OK)
{
return QSPI_ERROR;
}
return QSPI_OK;
}
/**
* @brief 读取QSPI存储器的当前状态
* @retval QSPI存储器状态
*/
uint8_t W25Qx_QSPI_GetStatus(void)
{
QSPI_CommandTypeDef s_command;
uint8_t reg;
/* 初始化读取状态寄存器命令 */
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
s_command.Instruction = READ_STATUS_REG1_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(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
!= HAL_OK)
{
return QSPI_ERROR;
}
/* 接收数据 */
if (HAL_QSPI_Receive(&hqspi, ®, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
!= HAL_OK)
{
return QSPI_ERROR;
}
/* 检查寄存器的值 */
if ((reg & W25QxJV_FSR_BUSY) != 0)
{
return QSPI_BUSY;
}
else
{
return QSPI_OK;
}
}
/**
* @brief 返回QSPI存储器的配置
* @param pInfo: 在配置结构上的指针
* @retval QSPI存储器状态
*/
uint8_t W25Qx_QSPI_GetInfo(QSPI_Info *pInfo)
{
/* 配置存储器配置结构 */
pInfo->FlashSize = W25QxJV_FLASH_SIZE;
pInfo->EraseSectorSize = W25QxJV_SUBSECTOR_SIZE;
pInfo->EraseSectorsNumber = (W25QxJV_FLASH_SIZE / W25QxJV_SUBSECTOR_SIZE);
pInfo->ProgPageSize = W25QxJV_PAGE_SIZE;
pInfo->ProgPagesNumber = (W25QxJV_FLASH_SIZE / W25QxJV_PAGE_SIZE);
return QSPI_OK;
}
/**
* @brief 复位QSPI存储器。
* @param hqspi: QSPI句柄
* @retval 无
*/
static uint8_t W25Qx_QSPI_ResetMemory()
{
QSPI_CommandTypeDef s_command;
/* 初始化复位使能命令 */
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
s_command.Instruction = RESET_ENABLE_CMD;
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(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
/* 发送复位存储器命令 */
s_command.Instruction = RESET_MEMORY_CMD;
if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
s_command.InstructionMode = QSPI_INSTRUCTION_4_LINES;
s_command.Instruction = RESET_ENABLE_CMD;
/* 发送命令 */
if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
/* 发送复位存储器命令 */
s_command.Instruction = RESET_MEMORY_CMD;
if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
W25Qx_QSPI_Delay(1);
/* 配置自动轮询模式等待存储器就绪 */
if (W25Qx_QSPI_AutoPollingMemReady(HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != QSPI_OK)
{
return QSPI_ERROR;
}
return QSPI_OK;
}
/**
* @brief 发送写入使能,等待它有效.
* @param hqspi: QSPI句柄
* @retval 无
*/
static uint8_t W25Qx_QSPI_WriteEnable()
{
QSPI_CommandTypeDef s_command;
QSPI_AutoPollingTypeDef s_config;
/* 启用写操作 */
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
s_command.Instruction = WRITE_ENABLE_CMD;
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(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
!= HAL_OK)
{
return QSPI_ERROR;
}
/* 配置自动轮询模式等待写启用 */
s_config.Match = W25QxJV_FSR_WREN;
s_config.Mask = W25QxJV_FSR_WREN;
s_config.MatchMode = QSPI_MATCH_MODE_AND;
s_config.StatusBytesSize = 1;
s_config.Interval = 0x10;
s_config.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE;
s_command.Instruction = READ_STATUS_REG1_CMD;
s_command.DataMode = QSPI_DATA_1_LINE;
s_command.NbData = 1;
if (HAL_QSPI_AutoPolling(&hqspi, &s_command, &s_config,
HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
return QSPI_OK;
}
/**
* @brief 读取存储器的SR并等待EOP
* @param hqspi: QSPI句柄
* @param Timeout 超时
* @retval 无
*/
static uint8_t W25Qx_QSPI_AutoPollingMemReady(uint32_t Timeout)
{
QSPI_CommandTypeDef s_command;
QSPI_AutoPollingTypeDef s_config;
/* 配置自动轮询模式等待存储器准备就绪 */
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
s_command.Instruction = READ_STATUS_REG1_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.DdrMode = QSPI_DDR_MODE_DISABLE;
s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
s_config.Match = 0x00;
s_config.Mask = W25QxJV_FSR_BUSY;
s_config.MatchMode = QSPI_MATCH_MODE_AND;
s_config.StatusBytesSize = 1;
s_config.Interval = 0x10;
s_config.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE;
if (HAL_QSPI_AutoPolling(&hqspi, &s_command, &s_config, Timeout) != HAL_OK)
{
return QSPI_ERROR;
}
return QSPI_OK;
}
/**
* @brief 读取FLASH ID
* @param 无
* @retval FLASH ID
*/
uint32_t W25Qx_QSPI_FLASH_ReadID(void)
{
QSPI_CommandTypeDef s_command;
uint32_t Temp = 0;
uint8_t pData[3];
/* 读取JEDEC ID */
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
s_command.Instruction = READ_JEDEC_ID_CMD;
s_command.AddressMode = QSPI_ADDRESS_NONE;
s_command.DataMode = QSPI_DATA_1_LINE;
s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
s_command.DummyCycles = 0;
s_command.NbData = 3;
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(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
!= HAL_OK)
{
printf("QSPI_FLASH_ReadID ERROR!!!....\r\n");
/* 用户可以在这里添加一些代码来处理这个错误 */
while (1)
{
}
}
if (HAL_QSPI_Receive(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
!= HAL_OK)
{
printf("QSPI_FLASH_ReadID ERROR!!!....\r\n");
/* 用户可以在这里添加一些代码来处理这个错误 */
while (1)
{
}
}
Temp = (pData[2] | pData[1] << 8) | (pData[0] << 16);
return Temp;
}
/**
* @brief 读取FLASH Device ID
* @param 无
* @retval FLASH Device ID
*/
uint32_t W25Qx_QSPI_FLASH_ReadDeviceID(void)
{
QSPI_CommandTypeDef s_command;
uint32_t Temp = 0;
uint8_t pData[3];
/*##-2-读取设备ID测试 ###########################################*/
/* 读取制造/设备 ID */
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
s_command.Instruction = READ_ID_CMD;
s_command.AddressMode = QSPI_ADDRESS_1_LINE;
s_command.AddressSize = QSPI_ADDRESS_24_BITS;
s_command.Address = 0x000000;
s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
s_command.DataMode = QSPI_DATA_1_LINE;
s_command.DummyCycles = 0;
s_command.NbData = 2;
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(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
printf("QSPI_FLASH_ReadDeviceID ERROR!!!....\r\n");
/* 用户可以在这里添加一些代码来处理这个错误 */
while (1)
{
}
}
if (HAL_QSPI_Receive(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
printf("QSPI_FLASH_ReadDeviceID ERROR!!! ....\r\n");
/* 用户可以在这里添加一些代码来处理这个错误 */
while (1)
{
}
}
Temp = pData[1] | (pData[0] << 8);
return Temp;
}
static void W25Qx_QSPI_Delay(uint32_t ms)
{
HAL_Delay(ms);
}
/*
* w25qxx_qspi.h
*
* Created on: 2020年10月19日
* Author: sudaroot
*/
#ifndef INC_W25QX_QSPI_H_
#define INC_W25QX_QSPI_H_
#include "main.h"
/* Private typedef -----------------------------------------------------------*/
//#define sFLASH_ID 0xEF3015 //W25X16
//#define sFLASH_ID 0xEF4015 //W25Q16
#define sFLASH_ID 0XEF4017 //W25Q64
//#define sFLASH_ID 0XEF4018 //W25Q128
//#define sFLASH_ID 0XEF4019 //W25Q256
/* QSPI Error codes */
#define QSPI_OK ((uint8_t)0x00)
#define QSPI_ERROR ((uint8_t)0x01)
#define QSPI_BUSY ((uint8_t)0x02)
#define QSPI_NOT_SUPPORTED ((uint8_t)0x04)
#define QSPI_SUSPENDED ((uint8_t)0x08)
/* W25QxJV Micron memory */
/* Size of the flash */
#define QSPI_FLASH_SIZE 24 /* 地址总线宽度访问整个内存空间 */
#define QSPI_PAGE_SIZE 256
/* QSPI Info */
typedef struct {
uint32_t FlashSize; /*!< 闪存大小 */
uint32_t EraseSectorSize; /*!< 擦除操作的扇区大小 */
uint32_t EraseSectorsNumber; /*!< 擦除操作的扇区数 */
uint32_t ProgPageSize; /*!< 编程操作的页面大小 */
uint32_t ProgPagesNumber; /*!< 编程操作的页面数 */
} QSPI_Info;
/* Private define ------------------------------------------------------------*/
/*命令定义-开头*******************************/
/**
* @brief W25QxJV配置
*/
#define W25QxJV_FLASH_SIZE 0x800000 /* 64 MBits => 8MBytes */
#define W25QxJV_SECTOR_SIZE 0x10000 /* 128 sectors of 64KBytes */
#define W25QxJV_SUBSECTOR_SIZE 0x1000 /* 2048 subsectors of 4kBytes */
#define W25QxJV_PAGE_SIZE 0x100 /* 65536 pages of 256 bytes */
#define W25QxJV_DUMMY_CYCLES_READ 4
#define W25QxJV_DUMMY_CYCLES_READ_QUAD 10
#define W25QxJV_BULK_ERASE_MAX_TIME 250000
#define W25QxJV_SECTOR_ERASE_MAX_TIME 3000
#define W25QxJV_SUBSECTOR_ERASE_MAX_TIME 800
/**
* @brief W25QxJV 指令
*/
/* 复位操作 */
#define RESET_ENABLE_CMD 0x66
#define RESET_MEMORY_CMD 0x99
#define ENTER_QPI_MODE_CMD 0x38
#define EXIT_QPI_MODE_CMD 0xFF
/* 识别操作 */
#define READ_ID_CMD 0x90
#define DUAL_READ_ID_CMD 0x92
#define QUAD_READ_ID_CMD 0x94
#define READ_JEDEC_ID_CMD 0x9F
/* 读操作 */
#define READ_CMD 0x03
#define FAST_READ_CMD 0x0B
#define DUAL_OUT_FAST_READ_CMD 0x3B
#define DUAL_INOUT_FAST_READ_CMD 0xBB
#define QUAD_OUT_FAST_READ_CMD 0x6B
#define QUAD_INOUT_FAST_READ_CMD 0xEB
/* 写操作 */
#define WRITE_ENABLE_CMD 0x06
#define WRITE_DISABLE_CMD 0x04
/* 寄存器操作 */
#define READ_STATUS_REG1_CMD 0x05
#define READ_STATUS_REG2_CMD 0x35
#define READ_STATUS_REG3_CMD 0x15
#define WRITE_STATUS_REG1_CMD 0x01
#define WRITE_STATUS_REG2_CMD 0x31
#define WRITE_STATUS_REG3_CMD 0x11
/* 编程操作 */
#define PAGE_PROG_CMD 0x02
#define QUAD_INPUT_PAGE_PROG_CMD 0x32
#define EXT_QUAD_IN_FAST_PROG_CMD 0x12
#define Enter_4Byte_Addr_Mode_CMD 0xB7
/* 擦除操作 */
#define SECTOR_ERASE_CMD 0x20 //0xD8擦:64K 0x52擦:32K 0x20擦:4K
#define CHIP_ERASE_CMD 0xC7
#define PROG_ERASE_RESUME_CMD 0x7A
#define PROG_ERASE_SUSPEND_CMD 0x75
/* 状态寄存器标志 */
#define W25QxJV_FSR_BUSY ((uint8_t)0x01) /*!< busy */
#define W25QxJV_FSR_WREN ((uint8_t)0x02) /*!< write enable */
#define W25QxJV_FSR_QE ((uint8_t)0x02) /*!< quad enable */
#define W25Q256FV_FSR_4ByteAddrMode ((uint8_t)0x01) /*!< 4字节地址模式 */
/*命令定义-结尾*******************************/
uint8_t W25Qx_QSPI_Init(void);
uint8_t W25Qx_QSPI_Erase_Block(uint32_t BlockAddress);
uint8_t W25Qx_QSPI_FastRead(uint8_t* pData, uint32_t ReadAddr, uint32_t Size);
uint8_t W25Qx_QSPI_Read(uint8_t* pData, uint32_t ReadAddr, uint32_t Size);
uint8_t W25Qx_QSPI_Write(uint8_t* pData, uint32_t WriteAddr, uint32_t Size);
uint32_t W25Qx_QSPI_FLASH_ReadDeviceID(void);
uint32_t W25Qx_QSPI_FLASH_ReadID(void);
#endif /* INC_W25QX_QSPI_H_ */
手册中提醒了,QSPI FLASH寻址空间不能大于256MB,但是QSPI FLASH芯片可以大于256MB。
QSPI Flash映射到内存地址是0x9000 0000,芯片内部flash地址是0x0800 0000,别搞混了。HAL库有定义:
内存映射需要调用HAL的HAL_QSPI_MemoryMapped()函数,配置函数,调用。
只要你访问的地址是0x9000 0000,那么芯片自动去QSPI flash读取0地址的数据。
下面函数参数s_command配置的是使用W25Q64 qspi 的读指令,这个指令一般会使用读取速度最快的,所以是四线读取指令。
而s_mem_mapped_cfg参数,设置是用CS超时值和超时是否释放CS片选。
/**
* @brief Configure the QSPI in memory-mapped mode
* @retval QSPI memory status
*/
static uint32_t QSPI_EnableMemoryMappedMode(QSPI_HandleTypeDef *QSPIHandle)
{
QSPI_CommandTypeDef s_command;
QSPI_MemoryMappedTypeDef s_mem_mapped_cfg;
/* Configure the command for the read instruction */
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
s_command.Instruction = QUAD_INOUT_FAST_READ_CMD;
s_command.AddressMode = QSPI_ADDRESS_4_LINES;
s_command.AddressSize = QSPI_ADDRESS_24_BITS;
s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
s_command.DataMode = QSPI_DATA_4_LINES;
s_command.DummyCycles = 6;
s_command.DdrMode = QSPI_DDR_MODE_DISABLE;
s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_HALF_CLK_DELAY;
s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
/* Configure the memory mapped mode */
s_mem_mapped_cfg.TimeOutActivation = QSPI_TIMEOUT_COUNTER_DISABLE;
s_mem_mapped_cfg.TimeOutPeriod = 0;
return HAL_QSPI_MemoryMapped(QSPIHandle, &s_command, &s_mem_mapped_cfg);
}
测试程序。main函数
int main(void)
{
uint8_t temp1[50] = "hello sudaroot\r\n";
uint8_t temp2[50] = {0};
uint8_t *temp3 = (uint8_t*)QSPI_BASE;
SCB_EnableICache();
SCB_EnableDCache();
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_QUADSPI_Init();
MX_USART1_UART_Init();
W25Qx_QSPI_Init();
W25Qx_QSPI_Erase_Block(0);
W25Qx_QSPI_Write(temp1, 0, 15);
W25Qx_QSPI_FastRead(temp2, 0, 15);
printf("1: %s\r\n", temp2);
QSPI_EnableMemoryMappedMode(&hqspi);
memset(temp2, 0, 50);
memcpy(temp2, temp3, 15);
printf("2: %s\r\n", temp2);
while (1)
{
}
}
现象:
全篇完。
本人博客仅仅代表我个人见解方便记录成长笔记。
若有与 看官老爷见解有冲突,我坚信看官老爷见解是对的,我的是错的。
感谢~!