STM32使用QUADSPI读写外部Nor Flash(以W25Q64为例)

news2024/11/22 22:43:16

使用QUADSPI读写W25Q64

    • QUADSPI介绍
    • 硬件连接
        • 双闪存模式禁止
        • 双闪存模式使能
    • QUADSPI命令序列
        • 指令阶段
        • 地址阶段
        • 交替字节阶段
        • 空指令周期阶段
        • 数据阶段
    • QUADSPI主要信号接口协议模式
        • 单线SPI模式
        • 双线SPI模式
        • 四线SPI模式
    • 使用QUADSPI操作W25Q64
        • 发送命令函数
        • 状态轮询函数
        • 读ID函数
        • QUADSPI模式使能函数
        • 写使能函数
        • 全片擦除函数
        • 扇区擦除函数
        • 读数据函数
        • 页写函数
        • 扇区写函数
        • 内存映射函数
        • 测试

QUADSPI介绍

QUADSPI 是一种专用的通信接口,连接单、双或四(条数据线) SPI Flash 存储介质。该接
口可以在以下三种模式下工作:
①间接模式:使用 QUADSPI 寄存器执行全部操作。
②状态轮询模式:周期性读取外部 Flash 状态寄存器,而且标志位置 1 时会产生中断(如擦除或烧写完成,会产生中断)。
③内存映射模式:外部 Flash 映射到微控制器地址空间,从而系统将其视作内部存储器。
采用双闪存模式时,将并行访问两个 Quad-SPI Flash,可同时发送/接收 8 位数据,吞吐量和容量均可提高二倍。

硬件连接

Nor Flash以W25Q64为例:
在这里插入图片描述

双闪存模式禁止

在这里插入图片描述

双闪存模式使能

在这里插入图片描述
双闪存模式不是所有含有QUADSPI的MCU都支持的,因为使用双闪存模式需要更多的IO口,对于一些IO口较少的MCU型号就没有那么多引脚可以复用为QUADSPI,具体可以看一下MCU的数据手册,确认一下是不是能连接第二个Flash。另外使用QUADSPI的话片选引脚就不能像标准SPI一样可以随意指定引脚,必须要使用指定的引脚,这个时候片选是由硬件完成的。
在这里插入图片描述

QUADSPI命令序列

QUADSPI 通过命令与 Flash 通信 每条命令包括指令、地址、交替字节、空指令和数据这五个阶段。任一阶段均可跳过,但至少要包含指令、地址、交替字节或数据阶段之一。
在这里插入图片描述

指令阶段

这一阶段,将在 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] 中指定。在内存映射模式下,则通过 AHB(来自于 Cortex® 或 DMA)直接给出地址。

地址阶段可一次发送 1 位(在单线 SPI 模式中通过 SO)、 2 位(在双线 SPI 模式中通过 IO0/IO1)或 4 位(在四线 SPI 模式中通过 IO0/IO1/IO2/IO3)。这可通过QUADSPI_CCR[11:10] 寄存器中的 ADMODE[1:0] 字段进行配置。

若 ADMODE = 00,则跳过地址阶段,命令序列直接进入下一阶段(如果存在)。

交替字节阶段

在交替字节阶段,将 1-4 字节发送到 Flash,一般用于控制操作模式。待发送的交替字节数在 QUADSPI_CCR[17:16] 寄存器的 ABSIZE[1:0] 字段中进行配置。待发送的字节在QUADSPI_ABR 寄存器中指定。

交替字节阶段可一次发送1位(在单线 SPI 模式中通过 SO)、2位(在双线 SPI 模式中 通 过 IO0/IO1)或4位(在四线 SPI 模 式 中 通 过 IO0/IO1/IO2/IO3)。这可通过QUADSPI_CCR[15:14] 寄存器中的 ABMODE[1:0] 字段进行配置。

若 ABMODE = 00,则跳过交替字节阶段,命令序列直接进入下一阶段(如果存在)。

交替字节阶段存在仅需发送单个半字节而不是一个全字节的情况,比如采用双线模式并且仅使用两个周期发送交替字节时。在这种情况下,固件可采用四线模式 (ABMODE = 11) 并发送一个字节,方法是 ALTERNATE 的位 7 和 3 置“1”( IO3 保持高电平)且位 6 和 2 置“0”( IO2 线保持低电平)。此时,半字节的高 2 位存放在 ALTERNATE 的位 4:3,低 2位存放在位 1 和 0 中。例如,如果半字节 2 (0010) 通过IO0/IO1 发送,则 ALTERNATE 应设置为 0x8A (1000_1010)。

空指令周期阶段

在空指令周期阶段,给定的 1-31 个周期内不发送或接收任何数据,目的是当采用更高的时钟频率时,给Flash留出准备数据阶段的时间。这一阶段中给定的周期数在QUADSPI_CCR[22:18] 寄存器的 DCYC[4:0] 字段中指定。在 SDR 和 DDR 模式下,持续时间被指定为一定个数的全时钟周期。

若 DCYC 为零,则跳过空指令周期阶段,命令序列直接进入数据阶段(如果存在)。

空指令周期阶段的操作模式由 DMODE 确定。

为确保数据信号从输出模式转变为输入模式有足够的“周转”时间,使用双线和四线模式从Flash 接收数据时,至少需要指定一个空指令周期。

数据阶段

在数据阶段,可从 Flash 接收或向其发送任意数量的字节。

在间接模式和自动轮询模式下,待发送/接收的字节数在 QUADSPI_DLR 寄存器中指定。

在间接写入模式下,发送到 Flash 的数据必须写入 QUADSPI_DR 寄存器。在间接读取模式下,通过读取 QUADSPI_DR 寄存器获得从 Flash 接收的数据。

在内存映射模式下,读取的数据通过 AHB 直接发送回 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 时立即完成。这一配置仅可用于仅间接写入模式。

QUADSPI主要信号接口协议模式

主要的信号模式为3种,单线SPI,就是标准SPI;双线SPI,比如数据阶段使用两条信号线;还有一种四线SPI,比如数据阶段使用四条信号线。双线SPI和四线SPI时信号线为双向IO,也就是说要么类似于标准SPI的MISO,要么类似于标准SPI的MOSI,工作方式为半双工。

单线SPI模式

传统 SPI 模式允许串行发送/接收单独的 1 位。在此模式下,数据通过 SO 信号(其 I/O 与IO0 共享)发送到 Flash。从 Flash 接收到的数据通过 SI(其 I/O 与 IO1 共享)送达。

通过将( QUADSPI_CCR 中的) IMODE/ADMODE/ABMODE/DMODE 字段设置为 01,可对不同的命令阶段分别进行配置,以使用此单个位模式。
在每个已配置为单线模式的阶段中:
①IO0 (SO) 处于输出模式
②IO1 (SI) 处于输入模式(高阻抗)
③IO2 处于输出模式并强制置“0”(以禁止“写保护”功能)
④IO3 处于输出模式并强制置“1”(以禁止“保持”功能)

若 DMODE = 01,这对于空指令阶段也同样如此。

双线SPI模式

在双线模式下,通过 IO0/IO1 信号同时发送/接收两位。
通过将 QUADSPI_CCR 寄存器的 IMODE/ADMODE/ABMODE/DMODE 字段设置为 10,可对不同的命令阶段分别进行配置,以使用双线 SPI 模式。
在每个已配置为双线模式的阶段中:
①IO0/IO1 在数据阶段进行读取操作时处于高阻态(输入),在其他情况下为输出
②IO2 处于输出模式并强制置“0”
③IO3 处于输出模式并强制置“1”

在空指令阶段,若 DMODE = 01,则 IO0/IO1 始终保持高阻态。

四线SPI模式

在四线模式下,通过 IO0/IO1/IO2/IO3 信号同时发送/接收四位。

通过将 QUADSPI_CCR 寄存器的 IMODE/ADMODE/ABMODE/DMODE 字段设置为 11,可对不同的命令阶段分别进行配置,以使用四线 SPI 模式。
在每个已配置为四线模式的阶段中, IO0/IO1/IO2/IO3 在数据阶段进行读取操作时均处于高阻态(输入),在其他情况下为输出。

在空指令阶段中,若 DMODE = 11,则 IO0/IO1/IO2/IO3 均为高阻态。

IO2 和 IO3 仅用于 Quad SPI 模式,如果未配置任何阶段使用四线 SPI 模式,即使 QUADSPI激活,对应 IO2 和 IO3 的引脚也可用于其他功能。

使用QUADSPI操作W25Q64

使用STM32CubeMX配置生成QSPI代码,以四线SPI为例。
模式选择:
在这里插入图片描述
引脚选择:
在这里插入图片描述
QSPI参数设置:
在这里插入图片描述
ClockPrescaler:该参数定义基于 AHB 时钟生成 CLK 所用的分频系数(值 + 1),QSPI是挂载在AHB总线上的,AHB时钟频率为80MHz,设置为1时,QPSI的速率为40MHz(80÷2)。W25Q64除读操作外支持的最大速率是133Mhz,跟电压有关系;读操作最大为50Mhz。这里指的是使用标准SPI的速率,使用双线SPI和四线SPI时的速率是相对于单线SPI来说的。
在这里插入图片描述
FifoThreshold:该参数定义在间接模式下 FIFO 中将导致 FIFO 阈值标志(FTF, QUADSPI_SR[2])置 1 的字节数阈值。
在间接模式下,若达到 FIFO 阈值,或从 Flash 读取完成后, FIFO 中留有数据时,FTF置 1。只要阈值条件不再为“真”,FTF就自动清零。
在自动轮询模式下,每次读取状态寄存器时,FTF即置 1 ;读取数据寄存器时,FTF清零。

SampleShifting:默认情况下, QUADSPI 在 Flash 驱动信号后过半个 CLK 周期才对 Flash 驱动的数据采样。在外部信号延迟时,这有利于推迟数据采样。

FlashSize:该参数用于指定外部 SPI Flash 的特性,FSIZE+1 是对 Flash 寻址所需的地址位数。在间接模式下, Flash 容量最高可达 4GB(使用32 位进行寻址),但在内存映射模式下的可寻址空间限制为 256MB。W25Q64一共8M,8388608字节,也就是2的23次方,所以FSIZE应该设置为22。
在这里插入图片描述

ChipSelectHighTime:片选拉高的时间,也就是在操作完W25Q64之后,片选线拉高取消选中SPI Flash的时间。在W25Q64手册中可以看到读操作释放最短时间10ns,擦除,写入等操作最小是50ns。QPSI时钟为40MHz时,一个clock为25ns,所以最少需要2个clock,因为手册中写的都是最小值,所以设置为3个clock,也就是75ns。
在这里插入图片描述
在这里插入图片描述
ClockMode:也就是模式0和模式3的选择(“模式 0”,在未进行任何操作时 CLK 保持低电平;“模式 3”,在未进行任何操作时 CLK 升至高电平)。

FlashID:只使用一个FLASH,所以就是默认的就可以,因为我用的这个MCU不支持挂两个SPI Flash,所以其实是不可选的。

DualFlash:双闪存模式禁止。

生成代码之后QSPI初始化函数如下:
在这里插入图片描述

发送命令函数

因为操作W25Q64是通过命令来实现的,所以就用一个专用的函数QSPI_SendCommand来发送命令。用到的命令有:

#define W25Q64_WRITE_ENABLE               0x06 

#define W25Q64_READ_STATUS_REG1           0x05 
#define W25Q64_READ_STATUS_REG2           0x35 
#define W25Q64_WRITE_STATUS_REG1          0x01 
#define W25Q64_WRITE_STATUS_REG2          0x31 

#define W25Q64_SECTOR_ERASE               0x20 
#define W25Q64_CHIP_ERASE                 0xC7 

#define W25Q64_MANUFACTURER_DEVICEID      0x90 

#define W25Q64_QUAD_INPUT_PAGE_PROGRAM    0x32 
#define W25Q64_FAST_READ_QUAD_OUTPUT      0x6B
static HAL_StatusTypeDef QSPI_SendCommand(uint32_t instruction,
                                          uint32_t address,
                                          uint32_t addressMode,
                                          uint32_t addressSize, 
                                          uint32_t dummyCycles, 
                                          uint32_t nbData, 
                                          uint32_t dataMode,
                                          uint32_t siooMode)
{
	QSPI_CommandTypeDef     sCommand = {0};
	HAL_StatusTypeDef Status = HAL_OK;
	
    sCommand.Instruction       = instruction;
    sCommand.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
	sCommand.Address           = address;
    sCommand.AddressMode       = addressMode;
	sCommand.AddressSize       = addressSize;
    sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    sCommand.DummyCycles       = dummyCycles;
	sCommand.NbData            = nbData;
    sCommand.DataMode          = dataMode;
    sCommand.DdrMode           = QSPI_DDR_MODE_DISABLE;
    sCommand.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;
    sCommand.SIOOMode          = siooMode;
	
	Status = HAL_QSPI_Command(&hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE);
	
	return Status;
}

参数说明:
instruction:指令
address:地址
addressMode:地址模式,也就是使用几根IO发送地址段。
addressSize:地址大小,也就是地址位数。
dummyCycles:空指令周期数,根据每个命令序列填写。如下图,Number of Clock右下角括号中表示分别使用的IO数。比如(1-4-4)表示指令阶段使用1个 IO,地址阶段使用4个 IO,数据阶段也是使用4个 IO。
在这里插入图片描述

nbData:数据字节数,在发送和读取的时候有用,读取/ 写入的字节数在数据长度寄存器 (QUADSPI_DLR) 中指定。如果 QUADSPI_DLR =
0xFFFFFFFF(全为“1”),则数据长度视为未定义, QUADSPI 将继续传输数据,直到到达(由 FSIZE 定义的) Flash 的结尾。如果不传输任何字节, DMODE (QUADSPI_CCR[25:24])应设置为 00。

dataMode:数据模式,也就是使用几根IO发送/接收数据段。
siooMode:发送指令模式,每次传输都需要指令段还是只有第一次发送需要(读写长数据时只用发送一次读写指令)。

状态轮询函数

状态轮询主要是判断当前操作是否完成,主要判断QUADSPI模式是否使能(QE位),写使能(WEL位),忙(BUSY)。使用状态轮询模式配上中断可以不用一直查询和等待状态改变,而是在产生需要的状态后产生中断。不过我这里用的是等待超时,没有使用中断。

在状态轮询模式下, QUADSPI 周期性启动命令以读取一定数量的状态字节(最多 4 个)。可屏蔽接收的字节以隔离一些状态位,从而在所选的位具有定义的值时可产生中断。

在状态轮询模式下, 主要设置的是状态屏蔽寄存器 (QUADSPI _PSMKR)和状态匹配寄存器 (QUADSPI _PSMAR)。MASK[31:0] (QUADSPI _PSMKR) 的内容用于屏蔽来自 Flash 的数据。如果 MASK[n] = 0,则屏蔽结果的位 n,从而不考虑该位。如果 MASK[n] = 1 并且位 [n] 的内容与 MATCH[n] (QUADSPI_PSMAR) 相同,说明存在位 n 匹配。

static void QSPI_AutoPollingMemReady(CHECK_STATE check)
{
	QSPI_CommandTypeDef     sCommand = {0};
	QSPI_AutoPollingTypeDef sConfig = {0};
	uint32_t timeout = 0;
	
	if(check == NO_CHECK)
	{
		return;
	}	

  	sCommand.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
	if(check == CHECK_QE)
		sCommand.Instruction       = W25Q64_READ_STATUS_REG2;
	else
		sCommand.Instruction       = W25Q64_READ_STATUS_REG1;
	sCommand.AddressMode       = QSPI_ADDRESS_NONE;
	sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
	sCommand.DataMode          = QSPI_DATA_1_LINE;
	sCommand.DummyCycles       = 0;
	sCommand.NbData            = 1;
	sCommand.DdrMode           = QSPI_DDR_MODE_DISABLE;
	sCommand.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;
	sCommand.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;
	
	sConfig.MatchMode       = QSPI_MATCH_MODE_AND;
	sConfig.StatusBytesSize = 1;
	sConfig.Interval        = 0x10;
	sConfig.AutomaticStop   = QSPI_AUTOMATIC_STOP_ENABLE;
	
	switch(check)
	{
		case CHECK_BUSY:
		  {
				sConfig.Match           = 0x00;
				sConfig.Mask            = 0x01;
				timeout                 = HAL_QSPI_TIMEOUT_ERASE_VALUE;
			}
			break;
		case CHECK_WEL:
		  {
				sConfig.Match           = 0x02;
				sConfig.Mask            = 0x03;
				timeout                 = HAL_QSPI_TIMEOUT_DEFAULT_VALUE;
			}
			break;
		case CHECK_QE:
		  {
				sConfig.Match           = 0x02;
				sConfig.Mask            = 0x02;
				timeout                 = HAL_QSPI_TIMEOUT_DEFAULT_VALUE;
			}
			break;
		default:
			break;
	}
	
	HAL_QSPI_AutoPolling(&hqspi, &sCommand, &sConfig, timeout);
}

读ID函数

static uint16_t W25QXX_ReadID(void)
{
	uint16_t id = 0;	  
	QSPI_SendCommand(W25Q64_MANUFACTURER_DEVICEID,
					0x000000,
					QSPI_ADDRESS_1_LINE,
					QSPI_ADDRESS_24_BITS,
					0,
					sizeof(uint16_t),
					QSPI_DATA_1_LINE,
					QSPI_SIOO_INST_EVERY_CMD);
	QSPI_Receive((uint8_t *)&id);
	return id;
}

QUADSPI模式使能函数

因为用的是四线SPI模式,需要需要确保QUADSPI模式使能,根据手册中的描述,出厂是默认使能的。
在这里插入图片描述
判断出厂有没有默认使能也可以通过W25Q64型号来判断,根据命名规则,最后一个字母如果是Q就是出厂默认使能。
在这里插入图片描述

static void W25QXX_EnterQspiMode(void) 
{
	uint8_t reg2 = 0;	
	QSPI_SendCommand(W25Q64_READ_STATUS_REG2,
					0x000000,
					QSPI_ADDRESS_NONE,
					QSPI_ADDRESS_8_BITS,
					0,
					sizeof(uint8_t),
					QSPI_DATA_1_LINE,
					QSPI_SIOO_INST_EVERY_CMD);
	QSPI_Receive((uint8_t *)&reg2);
	if(!(reg2 & REG2_QE_BIT))
	{
		reg2 |= REG2_QE_BIT;
		W25QXX_Write_Enable();
		QSPI_SendCommand(W25Q64_WRITE_STATUS_REG2,
						 0x000000,
						 QSPI_ADDRESS_NONE,
						 QSPI_ADDRESS_8_BITS,
						 0,
						 sizeof(uint8_t),
						 QSPI_DATA_1_LINE,
						 QSPI_SIOO_INST_EVERY_CMD);
		HAL_QSPI_Transmit(&hqspi, &reg2,HAL_QSPI_TIMEOUT_DEFAULT_VALUE);
		QSPI_AutoPollingMemReady(CHECK_QE);
	}
}

写使能函数

static void W25QXX_Write_Enable(void)   
{
	QSPI_SendCommand(W25Q64_WRITE_ENABLE,
					0x000000,
					QSPI_ADDRESS_NONE,
					QSPI_ADDRESS_8_BITS,
					0,
					0,
					QSPI_DATA_NONE,
					QSPI_SIOO_INST_EVERY_CMD);
	QSPI_AutoPollingMemReady(CHECK_WEL);
} 

全片擦除函数

void W25QXX_Erase_Chip(void)   
{
	W25QXX_Write_Enable();
	QSPI_SendCommand(W25Q64_CHIP_ERASE,
					0x000000,
					QSPI_ADDRESS_NONE,
					QSPI_ADDRESS_8_BITS,
					0,
					0,
					QSPI_DATA_NONE,
					QSPI_SIOO_INST_EVERY_CMD);
  QSPI_AutoPollingMemReady(CHECK_BUSY);
} 

扇区擦除函数

void W25QXX_Erase_Sector(uint32_t EraseAddr) 
{
	W25QXX_Write_Enable();
	QSPI_SendCommand(W25Q64_SECTOR_ERASE,
					EraseAddr,
					QSPI_ADDRESS_1_LINE,
					QSPI_ADDRESS_24_BITS,
					0,
					0,
					QSPI_DATA_NONE,
					QSPI_SIOO_INST_EVERY_CMD);
  QSPI_AutoPollingMemReady(CHECK_BUSY);
}

读数据函数

void W25QXX_ReadData(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t ReadLen)   
{
	QSPI_SendCommand(W25Q64_FAST_READ_QUAD_OUTPUT,
					ReadAddr,
					QSPI_ADDRESS_1_LINE,
					QSPI_ADDRESS_24_BITS,
					8,
					ReadLen,
					QSPI_DATA_4_LINES,
					QSPI_SIOO_INST_ONLY_FIRST_CMD);
  QSPI_Receive(pBuffer);
}

页写函数

static void W25QXX_Write_Page(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t WriteLen)
{
	W25QXX_Write_Enable();
	QSPI_SendCommand(W25Q64_QUAD_INPUT_PAGE_PROGRAM,
					WriteAddr,
					QSPI_ADDRESS_1_LINE,
					QSPI_ADDRESS_24_BITS,
					0,
					WriteLen,
					QSPI_DATA_4_LINES,
					QSPI_SIOO_INST_ONLY_FIRST_CMD);
  HAL_QSPI_Transmit(&hqspi, pBuffer,HAL_QSPI_TIMEOUT_DEFAULT_VALUE);
  QSPI_AutoPollingMemReady(CHECK_BUSY);
}

扇区写函数

void W25QXX_Write_Sector(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t WriteLen)
{
	uint32_t WritePageNum = ((WriteLen%W25Q64_PAGE_NUM) == 0) ? (WriteLen/W25Q64_PAGE_NUM) : ((WriteLen/W25Q64_PAGE_NUM)+1);
	for(uint8_t i = 0;i < WritePageNum;i++)
	{
		if(WriteLen > W25Q64_PAGE_NUM)
			W25QXX_Write_Page(&pBuffer[i*W25Q64_PAGE_NUM],WriteAddr,W25Q64_PAGE_NUM);
		else
			W25QXX_Write_Page(&pBuffer[i*W25Q64_PAGE_NUM],WriteAddr,WriteLen);
		WriteAddr += W25Q64_PAGE_NUM;
		WriteLen -= W25Q64_PAGE_NUM;
	}
}

内存映射函数

void W25QXX_EnterMemoryMappedMode(void)
{
	QSPI_CommandTypeDef     sCommand = {0};
	QSPI_MemoryMappedTypeDef sMemMappedCfg = {0};
	
	QSPI_AutoPollingMemReady(CHECK_BUSY);
	
	sCommand.Instruction       = W25Q64_FAST_READ_QUAD_OUTPUT;
	sCommand.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
	sCommand.Address           = 0;
	sCommand.AddressMode       = QSPI_ADDRESS_1_LINE;
	sCommand.AddressSize       = QSPI_ADDRESS_24_BITS;
	sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
	sCommand.DummyCycles       = 8;
	sCommand.NbData            = 0;
	sCommand.DataMode          = QSPI_DATA_4_LINES;
	sCommand.DdrMode           = QSPI_DDR_MODE_DISABLE;
	sCommand.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;
	sCommand.SIOOMode          = QSPI_SIOO_INST_ONLY_FIRST_CMD;
	
	sMemMappedCfg.TimeOutActivation = QSPI_TIMEOUT_COUNTER_DISABLE;
	
	if (HAL_QSPI_MemoryMapped(&hqspi, &sCommand, &sMemMappedCfg) != HAL_OK)
	{
	  printf("MemMapped error\r\n");
	}
}

测试

uint8_t aTxBuffer[] = "  ****Memory-mapped QSPI5 communication****  ****Memory-mapped QSPI4 communication****  ****Memory-mapped QSPI3 communication****  ****Memory-mapped QSPI2 communication****  ****Memory-mapped QSPI1 communication**** ";
uint8_t aRxBuffer[256];
__IO uint8_t *qspi_addr = (__IO uint8_t *)(0x90000000);

/*以下main函数中调用*/
	W25QXX_WriteData(aTxBuffer,0,sizeof(aTxBuffer));
	
	W25QXX_ReadData(aRxBuffer,0,sizeof(aTxBuffer));
	
	printf("%s\r\n",aRxBuffer);
	
	W25QXX_EnterMemoryMappedMode();//进入内存映射模式
	
	HAL_UART_Transmit(&huart2, (uint8_t *)qspi_addr, sizeof(aTxBuffer), HAL_MAX_DELAY);

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/643286.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

应用案例 | FG-200:通过Modbus将FF H1设备集成到DCS系统

一 背景 FOUNDATION Fieldbus&#xff08;FF&#xff09;协议是一种现代化的数字通信协议&#xff0c;其中FF H1协议在大型的化工、电力、石油等流程工业领域得到了广泛应用。由于FF H1协议具有诸多优势&#xff0c;例如高度可靠性、高速数据传输、强大的诊断能力和灵活的设备…

第4章 总体设计

文章目录 第5章 总体设计5.1 设计过程例题 5.2 设计原理5.2.1 模块化模块化的优势 例题5.2.2 抽象5.2.3 逐步求精求精实际上是细化的过程与抽象的关系 5.2.4 信息隐藏和局部化5.2.5 模块独立模块独立的重要性模块独立的定性标准度量耦合① 无直接耦合② 数据耦合③ 标记耦合④ …

MySQL数据库基础 11

第十一章 数据处理之增删改 1. 插入数据1.1 实际问题1.2 方式1&#xff1a;VALUES的方式添加1.3 方式2&#xff1a;将查询结果插入到表中 2. 更新数据3. 删除数据4. MySQL8新特性&#xff1a;计算列 1. 插入数据 1.1 实际问题 解决方式&#xff1a;使用 INSERT 语句向表中插入…

8.1 正弦波振荡电路(2)

四、石英晶体正弦波振荡电路 石英晶体谐振器&#xff0c;简称石英晶体&#xff0c;具有非常稳定的固有频率。对于振荡频率稳定性要求高的电路&#xff0c;应选用石英晶体作选频网络。 1、石英晶体的特点 将二氧化硅&#xff08; SiO 2 \,\textrm {SiO}_2\, SiO2​&#xff0…

华为OD机试真题 JavaScript 实现【找终点】【2023 B卷 100分】,附详细解题思路

一、题目描述 给定一个正整数数组&#xff0c;设为nums&#xff0c;最大为100个成员&#xff0c;求从第一个成员开始&#xff0c;正好走到数组最后一个成员&#xff0c;所使用的最少步骤数。 要求&#xff1a; 第一步必须从第一元素开始&#xff0c;且1 < 第一步的步长 &…

测试 4 年,从外包 15K 跳槽去字节 38K+12,啃完这份笔记你也可以

粉丝小王转行做测试已经是第4个年头&#xff0c;一直是一个不温不火的小职员&#xff0c;本本分分做着自己的事情&#xff0c;觉得自己的工作已经遇到了瓶颈&#xff0c;一个偶然的机会&#xff0c;获得了一份软件测试全栈知识点学习笔记&#xff0c;通过几个月的学习&#xff…

虚拟机(VMware )部署

一、VMware 概述&#xff1a; VMware是一家提供虚拟化解决方案的领先公司&#xff0c;其产品被广泛应用于企业和个人用户的计算环境中。VMware的虚拟化技术可以将物理计算资源&#xff08;如服务器、存储和网络&#xff09;抽象成虚拟化的资源&#xff0c;从而提供更高的灵活性…

路径规划算法:基于黑猩猩优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于黑猩猩优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于黑猩猩优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化算法…

MySQL之流程控制,索引

一、条件语句 if 条件语句 delimiter // CREATE PROCEDURE proc_if () BEGINdeclare i int default 0;if i 1 THENSELECT 1;ELSEIF i 2 THENSELECT 2;ELSESELECT 7;END IF;END // delimiter ; 二、循环语句 while循环 delimiter // CREATE PROCEDURE proc_while () BEGI…

测试用例设计背后的底层逻辑你一定不知道

目录 前言 1、万物皆可测 2、用例的本质 3、业务建模 4、其他一些测试法 总结&#xff1a; 前言 测试用例是每位测试人员都绕不开的话题&#xff0c;也是大家习以为常的事情。几乎所有测试相关的公众号、博客、专栏&#xff0c;都会提及测试用例&#xff0c;由此可见它的…

华为OD机试真题 JavaScript 实现【矩阵稀疏扫描】【2023 B卷 100分】,附详细解题思路

一、题目描述 如果矩阵中的许多系数都为零&#xff0c;那么该矩阵就是稀疏的。对稀疏现象有兴趣是因为它的开发可以带来巨大的计算节省&#xff0c;并且在许多大的实践中都会出现矩阵稀疏的问题。 给定一个矩阵&#xff0c;现在需要逐行和逐列地扫描矩阵&#xff0c;如果某一…

备战金九银十,互联网大厂最全“Java 面试宝典 +Java 核心知识集”汇总

搬砖也有好几年了&#xff0c;本想在金三银四跳槽来着&#xff0c;结果我想你们应该猜到了&#xff0c;于是计划着下半年跳槽试试&#xff0c;算是提前为金九银十做准备吧&#xff01; 现在着手准备着以防万一自己措手不及&#xff0c;这不&#xff0c;这几天刚整理出炉的两份…

强化学习笔记-11 Off-policy Methods with Approximation

前几章我们讨论了off-policy方式&#xff0c;其同on-policy方式最大的不同之处在于其在训练所采取的动作&#xff0c;是根据behavior policy进行决策的&#xff0c;而不是根据target policy。这种方式的好处在于兼顾了exploitation and exploration。本节将讨论如何通过模型近似…

Spring MVC 的创建连接和使用

目录 前言&#xff1a; MVC 是什么&#xff1f; 1. Spring MVC 项目的创建和连接&#xff1a; 1.1 创建 1.2 连接 2. RequestMapping 注解使用详析&#xff1a; 2.1 指定请求类型&#xff1a; 2.1.1 指定 GET 请求 2.1.2 指定 POST 请求 3. 参数的获取与传递&#xff1a; 3.1 传…

CTR预估之WideDeep系列模型:DeepFM/DCN

前言 在CTR预估中&#xff0c;FM系列模型使用浅层网络&#xff08;线性模型&#xff09;&#xff0c;让模型自己学习特征组合交互&#xff0c;为显式建模的方式&#xff1b;而DNN系列模型使用深层网络&#xff0c;隐式挖掘模型的高阶特征交互。 本文继续介绍结合这两者优点的…

在我电脑中待了很久的5款使用办公软件

你电脑中用的最久的软件是哪些&#xff1f;以下是否有你曾经使用过的软件呢&#xff1f;工欲善其事&#xff0c;必先利其器&#xff0c;今天继续分享五款实用的办公软件。 矢量图绘制——Inkscape Inkscape是一款用于绘制和编辑矢量图形的工具。它可以让你用简单的操作来创建…

AI学术交流——“人工智能”和“神经网络学习”

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​ 目录 前言 一.人工智能 1.“人工智能之父” 2.达特茅斯会议&#xff08;人工智能起源&a…

2023四大服装管理软件,第1款最受欢迎!

对于服装行业来说&#xff0c;依靠传统的方式管理服装门店或工厂&#xff0c;很难实现精准高效的管理&#xff0c;往往是耗费了大量的时间和精力&#xff0c;也没有挣到什么钱。 这就需要借助服装管理软件&#xff0c;来降低管理和运营的成本&#xff0c;减少工作量的同时&…

自带海量设计模板的网站 精准尺寸一键套用出图

现在大家通过手机去购物时都是通过查看商品的详情页&#xff0c;因为详情页会介绍产品的功能和产品的设计优势等&#xff0c;还有产品的基本信息。那么对于刚接触电商的商家来讲&#xff0c;制作详情页和商品主图是一件困难的事情&#xff0c;但如果套用模板或者是直接选择一个…