打印
[应用相关]

STM32 QSPI Flash 退出内存映射模式的方法

[复制链接]
310|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2024-10-8 17:03 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

最近在折腾自己做的板子上的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

841606704f45ead536.png (359.57 KB )

841606704f45ead536.png

使用特权

评论回复
沙发
星辰大海不退缩| | 2024-10-8 21:27 | 只看该作者
学习一下映射关系

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1923

主题

15596

帖子

11

粉丝