STM32 QSPI Flash 退出内存映射模式的方法
最近在折腾自己做的板子上的QSPI FLASH,型号为MT25QL256ABA1EW9,容量为32MB,使用四线SPI进行读写。
使用野火的代码野火-QSPI—读写串行FLASH,稍作修改就完成了Flash的读写测试。
但测试读取速度时发现,才只有9223KB/s,速度不理想。遂上网搜集资料,由硬汉大佬给出的代码,以及该帖子,得知可以开启内存映射模式,但其只能在读取状态下进行,开启了就写入不了了。
后来在ST论坛上看到这个帖子https://community.st.com/t5/stm32-mcus-products/stm32f7-qspi-exit-memory-mapped-mode/m-p/452589,当中提到清除繁忙位并重新初始化Flash即可,如下图所示。
因此编写代码,给读取操作增加了内存映射标志位判定。
完整的flash_qspi.c
如下
#include "flash_qspi.h"
#include "quadspi.h"
#include <stdio.h>
#include <string.h>
uint8_t memory_map_mode_flag = 0; // 内存映射标志位,用于加速读取
/*
*********************************************************************************************************
* 函 数 名: sfTestReadSpeed
* 功能说明: 测试串行Flash读速度。读取整个串行Flash的数据,最后打印结果
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
ALIGN_32BYTES(uint8_t SpeedTestbuf[16 * 1024]); /* 仅用于读速度测试目的 */
void sfTestReadSpeed(void)
{
uint32_t i;
int32_t iTime1, iTime2;
uint32_t uiAddr;
iTime1 = HAL_GetTick(); /* 记下开始时间 */
uiAddr = 0;
for (i = 0; i < FLASH_SIZE_32MB / (16 * 1024); i++, uiAddr += 16 * 1024)
{
BSP_QSPI_Read(SpeedTestbuf, uiAddr, 16 * 1024);
}
iTime2 = HAL_GetTick(); /* 记下结束时间 */
/* 打印读速度 */
printf("数据长度: %d字节, 读耗时: %dms, 读速度: %d KB/s\r\n", FLASH_SIZE_32MB, iTime2 - iTime1, FLASH_SIZE_32MB / (iTime2 - iTime1));
}
/*
*********************************************************************************************************
* 函 数 名: sfWriteTest
* 功能说明: 写串行Flash测试
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
#define TEST_SIZE 8 * 1024 // 8KB
#define TEST_ADDR 0x00
ALIGN_32BYTES(uint8_t buf[TEST_SIZE]);
void sfWriteTest(void)
{
uint32_t i;
int32_t iTime1, iTime2;
/* 填充测试缓冲区 */
for (i = 0; i < TEST_SIZE; i++)
{
buf[i] = i;
}
BSP_QSPI_Erase_Block(TEST_ADDR);
iTime1 = HAL_GetTick(); /* 记下开始时间 */
for (i = 0; i < TEST_SIZE; i += QSPI_PAGE_SIZE)
{
if (BSP_QSPI_Write(buf, TEST_ADDR + i, QSPI_PAGE_SIZE) != QSPI_OK)
{
printf("写串行Flash出错!\r\n");
return;
}
}
iTime2 = HAL_GetTick(); /* 记下结束时间 */
printf("写串行Flash成功!\r\n");
/* 打印读速度 */
printf("数据长度: %d字节, 写耗时: %dms, 写速度: %dB/s\r\n", TEST_SIZE, iTime2 - iTime1, (TEST_SIZE * 1000) / (iTime2 - iTime1));
}
/**
* @brief 擦除QSPI存储器的指定块
* @param BlockAddress: 需要擦除的块地址
* @retval QSPI存储器状态
*/
uint8_t BSP_QSPI_Erase_Block(uint32_t BlockAddress)
{
// 如果执行过内存映射读取操作,则在下次擦除前重新初始化QSPI FLASH
if (memory_map_mode_flag == 1)
{
HAL_QSPI_Abort(&hqspi);
BSP_QSPI_Init();
memory_map_mode_flag = 0;
}
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_32_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 (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 (QSPI_AutoPollingMemReady(SUBSECTOR_ERASE_MAX_TIME) != QSPI_OK)
{
return QSPI_ERROR;
}
return QSPI_OK;
}
static uint8_t QSPI_AutoPollingMemReady(uint32_t Timeout);
/**
* @brief 复位QSPI存储器。
* @param hqspi: QSPI句柄
* @retval 无
*/
static uint8_t 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;
}
/* 配置自动轮询模式等待存储器就绪 */
if (QSPI_AutoPollingMemReady(HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != QSPI_OK)
{
return QSPI_ERROR;
}
return QSPI_OK;
}
/**
* @brief 读取存储器的SR并等待EOP
* @param hqspi: QSPI句柄
* @param Timeout 超时
* @retval 无
*/
static uint8_t 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 = 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 发送写入使能,等待它有效.
* @param hqspi: QSPI句柄
* @retval 无
*/
static uint8_t 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 = FSR_WREN;
s_config.Mask = 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 设置QSPI存储器为4字节地址模式。
* @param 无
* @retval 无
*/
static uint8_t BSP_QSPI_4BYTE_ADDR_MOD(void)
{
QSPI_CommandTypeDef s_command;
/* 初始化复位使能命令 */
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
s_command.Instruction = ENTER_4_BYTE_ADDR_MODE_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;
}
/* 配置自动轮询模式等待存储器就绪 */
if (QSPI_AutoPollingMemReady(HAL_QPSI_TIMEOUT_DEFAULT_VALUE) !=
QSPI_OK)
{
return QSPI_ERROR;
}
return QSPI_OK;
}
/**
* @brief 从QSPI存储器中读取大量数据.
* @param pData: 指向要读取的数据的指针
* @param ReadAddr: 读取起始地址
* @param Size: 要读取的数据大小
* @retval QSPI存储器状态
*/
uint8_t BSP_QSPI_Read(uint8_t *pData, uint32_t ReadAddr, uint32_t Size)
{
uint8_t *temp = (uint8_t *)(QSPI_BASE + ReadAddr);
if (memory_map_mode_flag == 0) // 如果没有初始化内存映射 则先初始化
{
QSPI_CommandTypeDef s_command;
/* 初始化读命令 */
s_command.InstructionMode = QSPI_INSTRUCTION_4_LINES;
s_command.Instruction = QUAD_INOUT_FAST_READ_CMD;
s_command.AddressMode = QSPI_ADDRESS_4_LINES;
s_command.AddressSize = QSPI_ADDRESS_32_BITS;
s_command.Address = ReadAddr;
s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
s_command.DataMode = QSPI_DATA_4_LINES;
s_command.DummyCycles = 10;
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;
QSPI_MemoryMappedTypeDef s_mem_mapped_cfg;
s_mem_mapped_cfg.TimeOutActivation = QSPI_TIMEOUT_COUNTER_DISABLE;
s_mem_mapped_cfg.TimeOutPeriod = 0;
if (HAL_QSPI_MemoryMapped(&hqspi, &s_command, &s_mem_mapped_cfg) != HAL_OK)
{
printf("wrong!");
return QSPI_ERROR;
}
memory_map_mode_flag = 1; // 设置内存映射标志位为1 后面要是有写入操作需要置为0并重新初始化QSPI
}
memcpy(pData, temp, Size);
return QSPI_OK;
}
/**
* @brief 将大量数据写入QSPI存储器
* @param pData: 指向要写入数据的指针
* @param WriteAddr: 写起始地址
* @param Size: 要写入的数据大小
* @retval QSPI存储器状态
*/
uint8_t BSP_QSPI_Write(uint8_t *pData, uint32_t WriteAddr, uint32_t Size)
{
// 如果执行过内存映射读取操作,则在下次写入前重新初始化QSPI FLASH
if (memory_map_mode_flag == 1)
{
HAL_QSPI_Abort(&hqspi);
BSP_QSPI_Init();
memory_map_mode_flag = 0;
}
QSPI_CommandTypeDef s_command;
uint32_t end_addr, current_size, current_addr;
/* 计算写入地址和页面末尾之间的大小 */
current_addr = 0;
while (current_addr <= WriteAddr)
{
current_addr += QSPI_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_32_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 (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 (QSPI_AutoPollingMemReady(HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != QSPI_OK)
{
return QSPI_ERROR;
}
/* 更新下一页编程的地址和大小变量 */
current_addr += current_size;
pData += current_size;
current_size = ((current_addr + QSPI_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : QSPI_PAGE_SIZE;
} while (current_addr < end_addr);
return QSPI_OK;
}
/**
* @brief 初始化QSPI存储器
* @retval QSPI存储器状态
*/
uint8_t BSP_QSPI_Init(void)
{
QSPI_CommandTypeDef s_command;
uint8_t value = FSR_QE;
/* QSPI存储器复位 */
if (QSPI_ResetMemory() != QSPI_OK)
{
return QSPI_NOT_SUPPORTED;
}
/* 设置QSPI存储器为4字节地址模式 */
if (BSP_QSPI_4BYTE_ADDR_MOD() != QSPI_OK)
{
return QSPI_ERROR;
}
/* 使能写操作 */
if (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 (QSPI_AutoPollingMemReady(SUBSECTOR_ERASE_MAX_TIME) != QSPI_OK)
{
return QSPI_ERROR;
}
return QSPI_OK;
}
/**
* @brief 读取FLASH ID
* @param 无
* @retval FLASH ID
*/
uint32_t 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_1_LINE;
s_command.AddressSize = QSPI_ADDRESS_24_BITS;
s_command.DataMode = QSPI_DATA_1_LINE;
s_command.AddressMode = QSPI_ADDRESS_NONE;
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("something wrong ....\r\n");
/* 用户可以在这里添加一些代码来处理这个错误 */
while (1)
{
}
}
if (HAL_QSPI_Receive(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
printf("something wrong ....\r\n");
/* 用户可以在这里添加一些代码来处理这个错误 */
while (1)
{
}
}
Temp = (pData[2] | pData[1] << 8) | (pData[0] << 16);
return Temp;
}
完整的qspi_flash.h
代码如下
#ifndef _FLASH_QSPI_H_
#define _FLASH_QSPI_H_
#include "main.h"
/* 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)
/* W25Q256JV 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;
/*命令定义-开头*******************************/
/**
* @brief Flash 配置
*/
#define FLASH_SIZE_32MB 0x2000000 /* 256 MBits => 32MBytes */
#define SECTOR_SIZE 0x10000 /* 256 sectors of 64KBytes */
#define SUBSECTOR_SIZE 0x1000 /* 4096 subsectors of 4kBytes */
#define PAGE_SIZE 0x100 /* 65536 pages of 256 bytes */
#define DUMMY_CYCLES_READ 4
#define DUMMY_CYCLES_READ_QUAD 10
#define BULK_ERASE_MAX_TIME 250000
#define SECTOR_ERASE_MAX_TIME 3000
#define SUBSECTOR_ERASE_MAX_TIME 800
/**
* @brief QSPI指令
*/
/* 复位操作 */
#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 SECTOR_ERASE_CMD 0x20
#define CHIP_ERASE_CMD 0xC7
#define PROG_ERASE_RESUME_CMD 0x7A
#define PROG_ERASE_SUSPEND_CMD 0x75
/* 4字节地址模式操作 */
#define ENTER_4_BYTE_ADDR_MODE_CMD 0xB7
#define EXIT_4_BYTE_ADDR_MODE_CMD 0xE9
/* 状态寄存器标志 */
#define FSR_BUSY ((uint8_t)0x01) /*!< busy */
#define FSR_WREN ((uint8_t)0x02) /*!< write enable */
#define FSR_QE ((uint8_t)0x02) /*!< quad enable */
uint8_t BSP_QSPI_Init(void);
uint8_t BSP_QSPI_Erase_Block(uint32_t BlockAddress);
uint8_t BSP_QSPI_Read(uint8_t *pData, uint32_t ReadAddr, uint32_t Size);
uint8_t BSP_QSPI_Write(uint8_t *pData, uint32_t WriteAddr, uint32_t Size);
static uint8_t BSP_QSPI_4BYTE_ADDR_MOD(void);
static uint8_t QSPI_ResetMemory(void);
static uint8_t QSPI_WriteEnable(void);
static uint8_t QSPI_AutoPollingMemReady(uint32_t Timeout);
uint32_t QSPI_FLASH_ReadID(void);
// 速度测试
void sfWriteTest(void);
void sfTestReadSpeed(void);
#endif
在主函数中调用
sfTestReadSpeed();
sfWriteTest();
在串口可得到输出,读取速度是未开启内存映射时的4倍多,写入前512KB区域测试,也一切正常
作者:VR小杰