STM32F767 QUADSPI 的基本用法

本文详述了如何在STM32F767开发板上配置和使用QUADSPI接口,与外部W25Q256FV Flash进行读写操作。内容涵盖了QUADSPI的命令序列、配置寄存器解释、初始化步骤、QSPI指令发送及读写实现,并提供了W25Q256FV的初始化和擦写读写操作的示例代码。
摘要由CSDN通过智能技术生成

#转载:本文介绍 STM32F767 QUADSPI 的基本用法。

开发环境
硬件环境

电脑:Windows 10 Home x64
Apollo STM32F767 开发板 (ST-LINK V2 仿真器)

软件环境

Keil Version 5.24.1 (Pack Installer:Keil.STM32F7xx_DFP.2.9.0.pack)
STM32CubeMX Version 4.25.0(Packages Manager:STM32CubeF7)

QUADSPI 功能简介
主要特性

三种功能模式:间接模式、状态轮询模式和内存映射模式
双闪存模式,通过并行访问两个 Flash,可同时发送/接收 8 位数据
支持 SDR 和 DDR 模式
针对间接模式和内存映射模式,完全可编程操作码
针对间接模式和内存映射模式,完全可编程帧格式
集成 FIFO,用于发送和接收
允许 8、16 和 32 位数据访问
具有适用于间接模式操作的 DMA 通道
在达到 FIFO 阈值、超时、操作完成以及发生访问错误时产生中断

QUADSPI 命令序列

QUADSPI 通过命令与 Flash 通信每条命令包括指令、地址、交替字节、空指令和数据这五个阶段 任一阶段均可跳过,但至少要包含指令、地址、交替字节或数据阶段之一。
nCS 在每条指令开始前下降,在每条指令完成后再次上升。

指令阶段

这一阶段,将在 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,则跳过交替字节阶段,命令序列直接进入下一阶段 (如果存在)。
空指令周期阶段

在空指令周期阶段,给定的 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 Flash 配置

设备配置寄存器 (QUADSPI_DCR) 可用于指定外部 SPI Flash 的特性。
 FSIZE[4:0] 字段使用下面的公式定义外部存储器的大小:
F l a s h 中 的 字 节 数 = 2 F S I Z E + 1 Flash 中的字节数 = 2^{FSIZE+1} Flash=2FSIZE+1
 FSIZE+1 是对 Flash 寻址所需的地址位数。在间接模式下,Flash 容量最高可达 4GB(使用 32 位进行寻址),但在内存映射模式下的可寻址空间限制为 256MB。
 如果 DFM = 1,FSIZE 表示两个 Flash 容量的总和。
 QUADSPI 连续执行两条命令时,它在两条命令之间将片选信号 (nCS) 置为高电平默认仅一个 CLK 周期时长。如果 Flash 需要命令之间的时间更长,可使用片选高电平时间 (CSHT) 字段指定 nCS 必须保持高电平的最少 CLK 周期数 (最大为 8)。
 时钟模式 (CKMODE) 位指示命令之间的 CLK 信号逻辑电平 (nCS = 1 时)。
QUADSPI 延迟数据采样

默认情况下,QUADSPI 在 Flash 驱动信号后过半个 CLK 周期才对 Flash 驱动的数据采样。
 在外部信号延迟时,这有利于推迟数据采样。使用 SSHIFT 位 (QUADSPI_CR[4]),可将数据采样移位半个 CLK 周期。
 DDR 模式下不支持时钟移位:若 DDRM 位置 1,SSHIFT 位必须清零。

使用 QSPI 读写 W25Q256FV
 W25Q256 为 32MB 串行 flash 存储,支持 dual/quad 与 QPI 模式。本例中将使用 QSPI 接口对 W25Q256 进行读写。
QSPI 的初始化

//QSPI 的初始化函数如下所示:

QSPI_HandleTypeDef hqspi;

void (void)
{
hqspi.Instance = QUADSPI;
hqspi.Init.ClockPrescaler = 2;
hqspi.Init.FifoThreshold = 4;
hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE;
hqspi.Init.FlashSize = QSPI_FLASH_SIZE - 1;
hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_4_CYCLE;
hqspi.Init.ClockMode = QSPI_CLOCK_MODE_0;
hqspi.Init.FlashID = QSPI_FLASH_ID_1;
hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE;
if (HAL_QSPI_Init(&hqspi) != HAL_OK)
{
_Error_Handler(FILE, LINE);
}

}

void HAL_QSPI_MspInit(QSPI_HandleTypeDef* qspiHandle)
{

GPIO_InitTypeDef GPIO_InitStruct;
if(qspiHandle->Instance==QUADSPI)
{
/* USER CODE BEGIN QUADSPI_MspInit 0 */

/* USER CODE END QUADSPI_MspInit 0 /
/
QUADSPI clock enable */
__HAL_RCC_QSPI_CLK_ENABLE();

/**QUADSPI GPIO Configuration    
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF10_QUADSPI;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);

GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF9_QUADSPI;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF10_QUADSPI;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

/* USER CODE BEGIN QUADSPI_MspInit 1 */

/* USER CODE END QUADSPI_MspInit 1 */
}
}

void HAL_QSPI_MspDeInit(QSPI_HandleTypeDef* qspiHandle)
{

if(qspiHandle->Instance==QUADSPI)
{
/* USER CODE BEGIN QUADSPI_MspDeInit 0 */

/* USER CODE END QUADSPI_MspDeInit 0 /
/
Peripheral clock disable */
__HAL_RCC_QSPI_CLK_DISABLE();

/**QUADSPI GPIO Configuration    
PF6     ------> QUADSPI_BK1_IO3
PF7     ------> QUADSPI_BK1_IO2
PF8     ------> QUADSPI_BK1_IO0
PF9     ------> QUADSPI_BK1_IO1
PB2     ------> QUADSPI_CLK
PB6     ------> QUADSPI_BK1_NCS 
*/
HAL_GPIO_DeInit(GPIOF, GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9);

HAL_GPIO_DeInit(GPIOB, GPIO_PIN_2|GPIO_PIN_6);

/* USER CODE BEGIN QUADSPI_MspDeInit 1 */

/* USER CODE END QUADSPI_MspDeInit 1 */
}
}

其主要功能为:

查询 W25Q256FV 手册可知,其最大可支持的读写时钟频率为 104MHz;QSPI 使用 AHB1 时钟,AHB 主时钟的频率为 216MHz,设置分频系数为 2,则 QSPI 时钟为 72MHz;
设置 QSPI 的 FIFO 深度为 4byte;
设置移位半个周期进行数据采样,有助于提高数据读取稳定性;
设置 flash 容量参数为 32MB,对应 QSPI_FLASH_SIZE 为 25;
查询 W25Q256FV 手册可知,CS 片选信号的禁止时间至少为 50ns;因此设置片选高电平时间为 4 个时钟周期;
时钟模式为模式 0,即片选信号 CS 释放后,CLK 保持低电平;W25Q256FV 对此无要求;
FlashID 选择 Flash ID 1;
非双 flash 模式;
初始化 QSPI。

初始化函数如下所示。

HAL_StatusTypeDef HAL_QSPI_Init(QSPI_HandleTypeDef *hqspi)
{
HAL_StatusTypeDef status = HAL_ERROR;
uint32_t tickstart = HAL_GetTick();

/* Check the QSPI handle allocation */
if(hqspi == NULL)
{
return HAL_ERROR;
}

/* Check the parameters */
assert_param(IS_QSPI_ALL_INSTANCE(hqspi->Instance));
assert_param(IS_QSPI_CLOCK_PRESCALER(hqspi->Init.ClockPrescaler));
assert_param(IS_QSPI_FIFO_THRESHOLD(hqspi->Init.FifoThreshold));
assert_param(IS_QSPI_SSHIFT(hqspi->Init.SampleShifting));
assert_param(IS_QSPI_FLASH_SIZE(hqspi->Init.FlashSize));
assert_param(IS_QSPI_CS_HIGH_TIME(hqspi->Init.ChipSelectHighTime));
assert_param(IS_QSPI_CLOCK_MODE(hqspi->Init.ClockMode));
assert_param(IS_QSPI_DUAL_FLASH_MODE(hqspi->Init.DualFlash));

if (hqspi->Init.DualFlash != QSPI_DUALFLASH_ENABLE )
{
assert_param(IS_QSPI_FLASH_ID(hqspi->Init.FlashID));
}

/* Process locked */
__HAL_LOCK(hqspi);

if(hqspi->State == HAL_QSPI_STATE_RESET)
{
/* Allocate lock resource and initialize it */
hqspi->Lock = HAL_UNLOCKED;

/* Init the low level hardware : GPIO, CLOCK */
HAL_QSPI_MspInit(hqspi);
         
/* Configure the default timeout for the QSPI memory access */
HAL_QSPI_SetTimeout(hqspi, HAL_QPSI_TIMEOUT_DEFAULT_VALUE);

}

/* Configure QSPI FIFO Threshold */
MODIFY_REG(hqspi->Instance->CR, QUADSPI_CR_FTHRES, ((hqspi->Init.FifoThreshold - 1) << 8));

/* Wait till BUSY flag reset */
status = QSPI_WaitFlagStateUntilTimeout(hqspi, QSPI_FLAG_BUSY, RESET, tickstart, hqspi->Timeout);

if(status == HAL_OK)
{

/* Configure QSPI Clock Prescaler and Sample Shift */
MODIFY_REG(hqspi->Instance->CR,(QUADSPI_CR_PRESCALER | QUADSPI_CR_SSHIFT | QUADSPI_CR_FSEL | QUADSPI_CR_DFM), ((hqspi->Init.ClockPrescaler << 24)| hqspi->Init.SampleShifting | hqspi->Init.FlashID| hqspi->Init.DualFlash ));
    
/* Configure QSPI Flash Size, CS High Time and Clock Mode */
MODIFY_REG(hqspi->Instance->DCR, (QUADSPI_DCR_FSIZE | QUADSPI_DCR_CSHT | QUADSPI_DCR_CKMODE), 
           ((hqspi->Init.FlashSize << 16) | hqspi->Init.ChipSelectHighTime | hqspi->Init.ClockMode));

/* Enable the QSPI peripheral */
__HAL_QSPI_ENABLE(hqspi);

/* Set QSPI error code to none */
hqspi->ErrorCode = HAL_QSPI_ERROR_NONE;  

/* Initialize the QSPI state */
hqspi->State = HAL_QSPI_STATE_READY;

}

/* Release Lock */
__HAL_UNLOCK(hqspi);

/* Return function status */
return status;
}

其具体配置通过 QUADSPI 控制寄存器 (QUADSPI_CR) 与 QUADSPI 器件配置寄存器 (QUADSPI_CCR) 实现,这里不再详细说明。

QSPI 的指令与读写实现

QSPI 的指令发送函数如下所示。该函数的输入参数可以完整实现对指令阶段的控制。

/**

  • 说明: 使用QSPI发送Cmd

  • 参数: insMode: 指令模式

  •    ins: 指令值
    
  •    addrMode:地址模式
    
  •    addr: 地址值
    
  •    addrSize: 地址值大小
    
  •    alterMode: 复用字节模式
    
  •    alter: 复用字节值
    
  •    altersize: 复用字节大小
    
  •    dummyCycles: 空周期时钟数
    
  •    dataMode: 数据模式
    
  • @返回值 无
    */
    QSPI_StatusTypeDef QSPI_SendCmd(uint32_t insMode, uint32 ins, uint32_t addrMode, uint32_t addr, uint32_t addrSize, uint32 alterMode, uint32 alter, uint32 altersize, uint32_t dummyCycles, uint32_t dataMode)
    {
    QSPI_CommandTypeDef qspiCmdhandler;

    qspiCmdhandler.Instruction = ins;
    qspiCmdhandler.Address = addr;
    qspiCmdhandler.AlternateBytes = alter;
    qspiCmdhandler.AddressSize=addrSize;
    qspiCmdhandler.AlternateBytesSize = altersize;
    qspiCmdhandler.DummyCycles = dummyCycles;
    qspiCmdhandler.InstructionMode = insMode;
    qspiCmdhandler.AddressMode = addrMode;
    qspiCmdhandler.AlternateByteMode = alterMode;
    qspiCmdhandler.DataMode=dataMode;

    qspiCmdhandler.SIOOMode=QSPI_SIOO_INST_EVERY_CMD;

    qspiCmdhandler.DdrMode=QSPI_DDR_MODE_DISABLE;
    qspiCmdhandler.DdrHoldHalfCycle=QSPI_DDR_HHC_ANALOG_DELAY;

    if (HAL_OK == HAL_QSPI_Command(&hqspi,&qspiCmdhandler,HAL_QPSI_TIMEOUT_DEFAULT_VALUE))
    return QSPI_OK;
    else
    return QSPI_ERROR;
    }

QSPI 的的读写函数实现如下,均利用了 HAL 库完成。

QSPI_StatusTypeDef QSPI_Receive(uint8 *buf,uint32 datalen)
{
hqspi.Instance->DLR = datalen - 1;
if(HAL_OK == HAL_QSPI_Receive(&hqspi,buf,HAL_QPSI_TIMEOUT_DEFAULT_VALUE))
return QSPI_OK;
else
return QSPI_ERROR;
}

QSPI_StatusTypeDef QSPI_Transmit(uint8 *buf,uint32 datalen)
{
hqspi.Instance->DLR = datalen - 1;
if(HAL_OK == HAL_QSPI_Transmit(&hqspi,buf,HAL_QPSI_TIMEOUT_DEFAULT_VALUE))
return QSPI_OK;
else
return QSPI_ERROR;
}

W25Q256FV 的初始化
操作框图

W25Q256FV 的操作框图如下所示。其上电后根据 ADP 的值决定进入 3byte 或者 4byte 地址线模式,单均处于 SPI 模式下。之后可以控制其进去 QPI 模式。

本例中,将其控制在 QPI 模式以达到最高性能。

运行模式判断

运行模式的判断通过以 SPI/QPI 读取 Device ID 实现,哪种方法可以得到正确的 ID 则说明具体为哪种模式;确认模式后,在读取 ADP 值确认是 3byte 或者 4byte 地址线模式。

函数实现如下:

W25QxxModeTypeDef GetW25QxxMode(void)
{
uint16 id = 0;
uint8 buf;

if (QSPI_OK != W25QxxReadDeciveID(&id, SPI_3_Byte))
return UnknownMode;

if (Q25Q256FV_ID == id)
{
if (QSPI_OK != QSPI_SendCmd(QSPI_INSTRUCTION_1_LINE, ReadStatusReg3, QSPI_ADDRESS_NONE, 0, 0, QSPI_ALTERNATE_BYTES_NONE, 0, 0, 0, QSPI_DATA_1_LINE))
return UnknownMode;
if (QSPI_OK != QSPI_Receive(&buf,1))
return UnknownMode;

  if ((buf & ReadStatusReg3_ADS_Msk) != ReadStatusReg3_ADS_Msk)
       return SPI_3_Byte;
  else
        return SPI_4_Byte;

}

if (QSPI_OK != W25QxxReadDeciveID(&id, QPI_3_Byte))
return UnknownMode;

if (Q25Q256FV_ID == id)
{
if (QSPI_OK != QSPI_SendCmd(QSPI_INSTRUCTION_4_LINES, ReadStatusReg3, QSPI_ADDRESS_NONE, 0, 0, QSPI_ALTERNATE_BYTES_NONE, 0, 0, 0, QSPI_DATA_4_LINES))
return UnknownMode;
if (QSPI_OK != QSPI_Receive(&buf,1))
return UnknownMode;

  if ((buf & ReadStatusReg3_ADS_Msk) != ReadStatusReg3_ADS_Msk)
       return QPI_3_Byte;
  else
        return QPI_4_Byte;

}

return UnknownMode;
}

初始化

W25Q256FV 初始化函数的功能包括:

设置为 QPI 模式;
设置为 4byte 地址线模式;
设置 SetReadParameters 参数,以便获得最高读写速度;

QSPI_StatusTypeDef W25QxxEnterQspiMode_FourByte(void)
{
uint8 buf = 0;
QSPI_StatusTypeDef res = QSPI_OK;

if (QSPI_OK != W25QxxReadReg(ReadStatusReg2, &buf)) return QSPI_ERROR;

if ((buf & ReadStatusReg2_QE_Msk) != ReadStatusReg2_QE_Msk)
{
if (QSPI_OK != W25QxxWriteVolatileEnable()) return QSPI_ERROR;

 buf |= ReadStatusReg2_QE_Msk;
    if (QSPI_OK != W25QxxWriteReg(WriteStatusReg2, &buf)) return QSPI_ERROR;
   
 if (QSPI_OK != W25QxxWriteDisable()) return QSPI_ERROR;

}

if ((W25QxxMode == SPI_3_Byte) || (W25QxxMode == SPI_4_Byte))
res = QSPI_SendCmd(QSPI_INSTRUCTION_1_LINE, EnterQPIMode, QSPI_ADDRESS_NONE, 0, 0, QSPI_ALTERNATE_BYTES_NONE, 0, 0, 0, QSPI_DATA_NONE);

if (res != QSPI_OK)
return QSPI_ERROR;

if (QSPI_OK != W25QxxReadReg(ReadStatusReg3, &buf)) return QSPI_ERROR;

if ((buf & ReadStatusReg3_ADP_Msk) != ReadStatusReg3_ADP_Msk)
{
if (QSPI_OK != W25QxxWriteVolatileEnable()) return QSPI_ERROR;

  buf |= ReadStatusReg3_ADP_Msk;
   if (QSPI_OK != W25QxxWriteReg(WriteStatusReg3, &buf)) return QSPI_ERROR;
   
 if (QSPI_OK != W25QxxWriteDisable()) return QSPI_ERROR;   
 
 if (QSPI_OK != QSPI_SendCmd(QSPI_INSTRUCTION_4_LINES, Enter4ByteAddrMode, QSPI_ADDRESS_NONE, 0, 0, QSPI_ALTERNATE_BYTES_NONE, 0, 0, 0, QSPI_DATA_NONE))
     return QSPI_ERROR;

}

W25QxxMode = QPI_4_Byte;

if (QSPI_OK != QSPI_SendCmd(QSPI_INSTRUCTION_4_LINES, SetReadParameters, QSPI_ADDRESS_NONE, 0, 0, QSPI_ALTERNATE_BYTES_NONE, 0, 0, 0, QSPI_DATA_4_LINES))
return QSPI_ERROR;

buf = SetReadParametersVal;
if (QSPI_OK != QSPI_Transmit(&buf,1)) return QSPI_ERROR;

return QSPI_OK;
}

擦除与读写操作

W25Q256FV 初始化完毕后,按照 QPI 4byte 模式对其进行擦除与读写操作。

QSPI_StatusTypeDef W25QxxEraseSector4KB(uint32 addr)
{
if (QSPI_OK != W25QxxWriteEnable()) return QSPI_ERROR;

while (isW25QxxBusy() == TRUE) {printf(“4KB erase done: %xn”, addr);};

if (QSPI_OK != QSPI_SendCmd(QSPI_INSTRUCTION_4_LINES, EraseSector4KB, QSPI_ADDRESS_4_LINES, addr, QSPI_ADDRESS_32_BITS, QSPI_ALTERNATE_BYTES_NONE, 0, 0, 0, QSPI_DATA_NONE))
return QSPI_ERROR;

while (isW25QxxBusy() == TRUE) {printf(“W25QxxEraseSector4KB: %xn”, addr);};

return QSPI_OK;
}

QSPI_StatusTypeDef W25QxxEraseSector64KB(uint32 addr)
{
if (QSPI_OK != W25QxxWriteEnable()) return QSPI_ERROR;

while (isW25QxxBusy() == TRUE) {printf(“4KB erase done: %xn”, addr);};

if (QSPI_OK != QSPI_SendCmd(QSPI_INSTRUCTION_4_LINES, EraseSector64KB, QSPI_ADDRESS_4_LINES, addr, QSPI_ADDRESS_32_BITS, QSPI_ALTERNATE_BYTES_NONE, 0, 0, 0, QSPI_DATA_NONE))
return QSPI_ERROR;

while (isW25QxxBusy() == TRUE) {printf(“W25QxxEraseSector64KB: %xn”, addr);};

return QSPI_OK;
}

QSPI_StatusTypeDef W25QxxEraseChip(void)
{
if (QSPI_OK != W25QxxWriteEnable()) return QSPI_ERROR;

while (isW25QxxBusy() == TRUE) {printf(“W25QxxEraseChipn”);};

if (QSPI_OK != QSPI_SendCmd(QSPI_INSTRUCTION_4_LINES, ChipErase, QSPI_ADDRESS_NONE, 0, 0, QSPI_ALTERNATE_BYTES_NONE, 0, 0, 0, QSPI_DATA_NONE))
return QSPI_ERROR;

while (isW25QxxBusy() == TRUE) {printf(“W25QxxEraseChipn”);};

return QSPI_OK;
}

QSPI_StatusTypeDef W25QxxWritePage32B(uint32 addr, uint8 *writeBuff, uint32 writeBuffLen)
{
if (QSPI_OK != W25QxxWriteEnable()) return QSPI_ERROR;

if (QSPI_OK != QSPI_SendCmd(QSPI_INSTRUCTION_4_LINES, PageProgram32B, QSPI_ADDRESS_4_LINES, addr, QSPI_ADDRESS_32_BITS, QSPI_ALTERNATE_BYTES_NONE, 0, 0, 0, QSPI_DATA_4_LINES))
return QSPI_ERROR;

if (QSPI_OK != QSPI_Transmit(writeBuff,writeBuffLen))
return QSPI_ERROR;

while (isW25QxxBusy() == TRUE) {printf(“W25QxxWritePage32B: %xn”, addr);};

return QSPI_OK;
}

QSPI_StatusTypeDef W25QxxRead(uint32 addr, uint8 *readBuff, uint32 readBuffLen)
{

if (QSPI_OK != QSPI_SendCmd(QSPI_INSTRUCTION_4_LINES, FastRead, QSPI_ADDRESS_4_LINES, addr, QSPI_ADDRESS_32_BITS, QSPI_ALTERNATE_BYTES_NONE, 0, 0, 8, QSPI_DATA_4_LINES))
return QSPI_ERROR;

if (QSPI_OK != QSPI_Receive(readBuff, readBuffLen))
return QSPI_ERROR;

while (isW25QxxBusy() == TRUE) {printf(“busy”);};

return QSPI_OK;
}

实验验证

测试函数设置如下:

程序首先擦除了地址 test_addr 所在的 4KB 区域;
之后读取擦除后的 test_addr 所在区域数值并打印;
对 test_addr 所在区域进行 32byte 的数值写入;
读取写入区域的数值并打印确定是否正确写入。

uint32 test_addr = 0x00000000;
uint32 test_index = 0;
uint8 test_writeBuff[256];
uint8 test_readBuff[256];

printf(“4KB erase addr is: %xn”, test_addr);
res = W25QxxEraseSector4KB(test_addr);
printf(“W25QxxEraseSector4KB(test_addr) res is: %xn”, res);
printf(“4KB erase done: %xnn”, test_addr);

res = W25QxxRead(test_addr, test_readBuff, 256);
printf(“W25QxxRead res is: %xn”, res);

for (test_index = 0; test_index < 256; test_index++)
printf(“test_readBuff[test_index] is: %xn”, test_readBuff[test_index]);

for (test_index = 0; test_index < 256; test_index++)
test_writeBuff[test_index] = test_index;

res = W25QxxWritePage32B(test_addr, test_writeBuff, 256);
printf(“W25QxxWritePage32B res is: %xn”, res);

res = W25QxxRead(test_addr, test_readBuff, 256);
printf(“W25QxxRead res is: %xn”, res);

for (test_index = 0; test_index < 256; test_index++)
printf(“test_readBuff[test_index] is: %xn”, test_readBuff[test_index]);

程序运行结果符合预期。务必注意读写操作所针对的区域必须首先完成擦除操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值