STM32F767 QUADSPI 的基本用法

#转载:本文介绍 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]);

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

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Version: 2.12.0 (2019-07-17) Keil.STM32F7xx_DFP.2.12.0.pack Download Updated Pack to include subset of STM32Cube_FW_F7 Firmware Package version V1.15.0 using HAL Drivers V1.2.7 Added support for Low Level (LL) drivers. Corrected RTE_Device.h file (I2C3_SDA) Corrected condition for selecting HAL RCC MX_Device_h.ftl: Updated parsing of USART virtual mode Updated generation of macros: Added handling for '(' and ')' symbols Corrected launching STM32CubeMX via &quot;play&quot; button for existing projects overwrites with a new STM32CubeMX project file instead of loading existing. Updated Board Examples: graphics examples use Segger emWin version 5.50.0. examples enable Event Recorder in debug targets Updated LCDConf.c (ready for GUI_USE_ARGD = 1) CMSIS-Driver: CAN: Corrected SetBitrate function to leave Silent and Loopback mode as they were. Corrected SetMode function to clear Silent and Loopback mode when NORMAL mode is activated. Corrected MessageSend function to only access required data for sending. EMAC: Corrected __MEMORY_AT(x) define to be compliant with Arm Compiler 6. Corrected: ETH DMA initialization moved to enable of MAC transmitter or receiver solving netInitialize/netUnnitialize/netInitialize sequence. I2C: Corrected transfers for data sizes greater than 255 (Complete Reload handling). Corrected I2C_SlaveReceive functionality. Corrected code alignment. MCI: Added data cache handling. USART: Added check for valid pointer to USART_PIN prior to use. Corrected POWER_OFF sequence. DMA is DeInitialized after it is aborted. USB Device: Updated USBD_EndpointConfigure function to check that maximum packet size requested fits into configured FIFO (compile time configured). I/O output speed is configurable SPI: Updated SPI_TRANSFER_INFO structure - tx_buf type changed from uint8_t * to const uint8_t *. Added check for valid pointer to SPI_PIN prior to use.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值