最近在折腾自己做的板子上的QSPI FLASH,型号为MT25QL256ABA1EW9,容量为32MB,使用四线SPI进行读写。 使用野火的代码野火-QSPI—读写串行FLASH,稍作修改就完成了Flash的读写测试。 但测试读取速度时发现,才只有9223KB/s,速度不理想。遂上网搜集资料,由硬汉大佬给出的代码,以及该帖子,得知可以开启内存映射模式,但其只能在读取状态下进行,开启了就写入不了了。 后来在ST论坛上看到这个帖子https://community.st.com/t5/stm3 ... ped-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; }
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区域测试,也一切正常
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.csdn.net/m0_55217834/article/details/142680776 |