STM32CubeIDE QSPI间接模式和内存映射模式 读写W25Q64

2024-02-10 14:38

本文主要是介绍STM32CubeIDE QSPI间接模式和内存映射模式 读写W25Q64,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

随言:

为后面的QSPI内存映射铺垫。

为芯片内执行 (XIP) 执行代码。

参考例程:

C:\Users\admin\STM32Cube\Repository\STM32Cube_FW_F7_V1.16.0\Projects\STM32F723E-Discovery\Examples\QSPI\QSPI_ReadWrite

源码链接:

H743_QSPI_W25Q64.rar-嵌入式文档类资源-CSDN下载

QSPI介绍:

下面内容摘自《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 时立即完成。这一配置仅可用于 仅间接写入模式。

STM32CubeIDE:

硬件: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已指定。

1、QSPI单线读取ID

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指令0x90s_command.AddressMode       = QSPI_ADDRESS_1_LINE;			// 地址单线传输s_command.Address			  = 0;								// 地址 0s_command.AddressSize		  = QSPI_ADDRESS_24_BITS;			// 地址 24bits_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正确。

2、QSPI四线读取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 读IDs_command.InstructionMode   = QSPI_INSTRUCTION_4_LINES;		// 指令QSPI四线传输s_command.Instruction       = MANUFACTURER_DEVICE_ID;			// 指令,0x90读IDs_command.AddressMode       = QSPI_ADDRESS_4_LINES;			// 地址QSPI四线传输s_command.Address			  = 0;								// 地址 0s_command.AddressSize		  = QSPI_ADDRESS_24_BITS;			// 地址 24bits_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;		// 交替字节模式跳过s_command.DummyCycles       = 0;								// 空指令周期数s_command.DataMode          = QSPI_DATA_4_LINES;				// 数据QSPI四线传输s_command.NbData			  = 2;								// 数据长度 1s_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编程一样,微微改动即可,就不写了,直接上代码了。

3、状态轮询模式

状态轮询模式实际上就是读取某一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一致则匹配成功。

4、看手册写驱动

看手册会发现有很多读指令。每个读指令用法都不一样,看红框圈出来的内容,

如(1-4-4)代表的是传输的三个步骤使用几线传输数据,指令用单线传输,地址和数据都是四线传输。

现在看读指令Fast Read Quad I/O (EBh),在地址传输完成后,要等3个字节,每个字节传输需要2个时钟周期,即6个时钟周期才能接收到数据。

下面是完整的驱动代码,我移植野火的QSPI驱动。

/** w25qxx_qspi.c**  Created on: 2020年10月19日*      Author: sudaroot*/
#include <stdio.h>
#include <w25qx_qspi.h>
#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, &reg, 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, &reg, 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){}
}

现象:

退出内存映射模式:

ST官网有篇帖子说了怎么退出,不过我没测试。

https://community.st.com/s/question/0D50X00009XkaJuSAJ/stm32f7-qspi-exit-memory-mapped-mode

  全篇完。

本人是一个嵌入式未入门小白,博客仅仅代表我个人主观见解,记录成长笔记。
笔记是以最简单的方式,只展示最核心的原理。
若有与 大神大大 见解有歧义,我绝对坚信 大神大大 见解是对的,我的是错的。
若无积分等无法下载源码,可加入QQ群657407920下载交流经验。感谢~!

 

这篇关于STM32CubeIDE QSPI间接模式和内存映射模式 读写W25Q64的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/697376

相关文章

NameNode内存生产配置

Hadoop2.x 系列,配置 NameNode 内存 NameNode 内存默认 2000m ,如果服务器内存 4G , NameNode 内存可以配置 3g 。在 hadoop-env.sh 文件中配置如下。 HADOOP_NAMENODE_OPTS=-Xmx3072m Hadoop3.x 系列,配置 Nam

10. 文件的读写

10.1 文本文件 操作文件三大类: ofstream:写操作ifstream:读操作fstream:读写操作 打开方式解释ios::in为了读文件而打开文件ios::out为了写文件而打开文件,如果当前文件存在则清空当前文件在写入ios::app追加方式写文件ios::trunc如果文件存在先删除,在创建ios::ate打开文件之后令读写位置移至文件尾端ios::binary二进制方式

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍1、W25Q64简介2、硬件电路3、W25Q64框图4、Flash操作注意事项软件SPI读写W2

模版方法模式template method

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/template-method 超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。 上层接口有默认实现的方法和子类需要自己实现的方法

【iOS】MVC模式

MVC模式 MVC模式MVC模式demo MVC模式 MVC模式全称为model(模型)view(视图)controller(控制器),他分为三个不同的层分别负责不同的职责。 View:该层用于存放视图,该层中我们可以对页面及控件进行布局。Model:模型一般都拥有很好的可复用性,在该层中,我们可以统一管理一些数据。Controlller:该层充当一个CPU的功能,即该应用程序

迭代器模式iterator

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/iterator 不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素

《x86汇编语言:从实模式到保护模式》视频来了

《x86汇编语言:从实模式到保护模式》视频来了 很多朋友留言,说我的专栏《x86汇编语言:从实模式到保护模式》写得很详细,还有的朋友希望我能写得更细,最好是覆盖全书的所有章节。 毕竟我不是作者,只有作者的解读才是最权威的。 当初我学习这本书的时候,只能靠自己摸索,网上搜不到什么好资源。 如果你正在学这本书或者汇编语言,那你有福气了。 本书作者李忠老师,以此书为蓝本,录制了全套视频。 试

利用命令模式构建高效的手游后端架构

在现代手游开发中,后端架构的设计对于支持高并发、快速迭代和复杂游戏逻辑至关重要。命令模式作为一种行为设计模式,可以有效地解耦请求的发起者与接收者,提升系统的可维护性和扩展性。本文将深入探讨如何利用命令模式构建一个强大且灵活的手游后端架构。 1. 命令模式的概念与优势 命令模式通过将请求封装为对象,使得请求的发起者和接收者之间的耦合度降低。这种模式的主要优势包括: 解耦请求发起者与处理者

JVM内存调优原则及几种JVM内存调优方法

JVM内存调优原则及几种JVM内存调优方法 1、堆大小设置。 2、回收器选择。   1、在对JVM内存调优的时候不能只看操作系统级别Java进程所占用的内存,这个数值不能准确的反应堆内存的真实占用情况,因为GC过后这个值是不会变化的,因此内存调优的时候要更多地使用JDK提供的内存查看工具,比如JConsole和Java VisualVM。   2、对JVM内存的系统级的调优主要的目的是减少