自学内容网 自学内容网

STM32H750 QSPI + MDMA Flash的读写测试

STM32H750 QSPI + MDMA Flash的读写测试


  • ✨首先声明本例程来源于:安富莱STM32-V7(https://www.armbbs.cn/forum.php?mod=viewthread&tid=91590&highlight=STM32-V7
  • 📍个人相关篇内容《STM32 QSPI接口驱动GD/W25Qxx配置简要
  • 🔖测试程序:V7-029_QSPI读写例程,四线DMA方式,读每秒48MB(V1.1)
  • 🌿测试对象:STM32H750VB+GD25Q64

测试的目的,熟悉和掌握对STM32H7 QSPI + MDMA的使用,同时可以测试Nor flash读写性能和指令。

📑测试准备

  • 🌿先从上方armbbs论坛将相对于的资料下载来。

  • 🌿原程序是基于STM32H743 + W25Q256,修改型号和调整QSPI引脚映射接口。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 🌿由于采用的是GD25Q64,调整地址模式(原来是32位地址模式改为24位地址模式)

  • 🌿指令调整,GD25Q64的QSPI 4线传输指令和32MB大容量的型号存在差异。

📗新增测试指令

  • 🌿Quad Output Fast Read (6BH)
    The Quad Output Fast Read command is followed by 3-byte address (A23-A0) and a dummy byte, and each bit is latched in on the rising edge of SCLK, then the memory contents are shifted out 4-bit per clock cycle from IO3, IO2, IO1 and IO0. The first byte addressed can be at any location. The address is automatically incremented to the next higher address after each byte of data is shifted out. The Quad Enable bit (QE) of Status Register (S9) must be set to enable for the Quad Output Fast Read command.
    在这里插入图片描述
  • 🔖该指令和Quad I/O Fast Read (EBH)的差异是dummy clock参数不同。读取时地址自增。
/**
 * @brief   快速读取NOR Flash
 * @note    从指定地址开始读取指定长度的数据
 * @param   pbuf   : 读取到数据保存的地址
 * @param   address   : 指定开始读取的地址
 * @param   len   : 指定读取数据的字节数
 * @retval  0: 成功; 1: 失败
 */
int QUAD_Single_FAST_READ(uint8_t *data, uint32_t address, uint32_t len)
{
  QSPI_CommandTypeDef command = {0};
  RxCplt = 0;

  command.InstructionMode = QSPI_INSTRUCTION_1_LINE; // 指令阶段的操作模式,单线传输
  command.AddressSize = QSPI_ADDRESS_24_BITS;
  command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;

  command.DdrMode = QSPI_DDR_MODE_DISABLE;
  command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
  command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; // QSPI_SIOO_INST_ONLY_FIRST_CMD  QSPI_SIOO_INST_EVERY_CMD

  command.Instruction = QUAD_OUT_FAST_READ_CMD; // 0x6b
  command.DummyCycles = 8;                      // 
  command.AddressMode = QSPI_ADDRESS_1_LINE;
  command.DataMode = QSPI_DATA_4_LINES;
  command.NbData = len;
  command.Address = address;

if (HAL_QSPI_Command(&QSPIHandle, &command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler(__FILE__, __LINE__);
}

/* MDMA方式读取 */
  if (HAL_QSPI_Receive_DMA(&QSPIHandle, data) != HAL_OK)
  {
    return HAL_ERROR;
  }
/* 等接受完毕 */
while(RxCplt == 0);
RxCplt = 0;

  return HAL_OK;
}
  • 🌿Chip Erase (60/C7H)芯片擦除指令。

✨在测试菜单中引入全擦写芯片指令,用来测试使用专门的擦除全片指令。
同时在测试过程中发现了安富莱提供的该案例中有全片擦除接口函数(QSPI_EraseChip)有bug,无法进行擦除,经过仔细检查,发现该函数里面地址模式参数配置错了。正确的配置如下:(该bug问题已向论坛反馈,当前资料信息软件资源版本V10.7(2024-08-30)

/*
*********************************************************************************************************
*函 数 名: QSPI_EraseChip
*功能说明: 整个芯片擦除
*形    参: 无
*返 回 值: 无
*********************************************************************************************************
*/
void QSPI_EraseChip(void)
{
QSPI_CommandTypeDef sCommand={0};

    /* 用于命令发送完成标志 */
CmdCplt = 0;

/* 写使能 */
QSPI_WriteEnable(&QSPIHandle);//WEL
    
/* 基本配置 */
sCommand.InstructionMode   = QSPI_INSTRUCTION_1_LINE;    /* 1线方式发送指令 */
sCommand.AddressSize       = QSPI_ADDRESS_24_BITS;       /* 32位地址 */
sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;  /* 无交替字节 */
sCommand.DdrMode           = QSPI_DDR_MODE_DISABLE;      /* W25Q256JV不支持DDR */
sCommand.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;  /* DDR模式,数据输出延迟 */
sCommand.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD; /* 每次传输都发指令 */

/* 擦除配置 */
sCommand.Instruction = BULK_ERASE_CMD;       /* 整个芯片擦除命令*/       
sCommand.AddressMode = QSPI_ADDRESS_NONE;  /* 地址模式 */       
sCommand.Address     = 0;                    /* 地址 */    
sCommand.DataMode    = QSPI_DATA_NONE;       /* 无需发送数据 */  
sCommand.DummyCycles = 0;                    /* 无需空周期 */  

if (HAL_QSPI_Command_IT(&QSPIHandle, &sCommand) != HAL_OK)
{
Error_Handler(__FILE__, __LINE__);
}

/* 等待命令发送完毕 */
while(CmdCplt == 0);
CmdCplt = 0;

/* 等待编程结束 */
StatusMatch = 0;
QSPI_AutoPollingMemReady(&QSPIHandle);
while(StatusMatch == 0);
StatusMatch = 0;
}
  • 🌿增加对状态寄存器的读取。

需要注意没有添加对QE寄存器使能相关的代码,只是读取操作,对于GD25QXX芯片,默认全新的芯片该位是0,该位不使能的情况下,很多指令是不能进行访问和操作的。GD25QXX芯片的QE使能操作可以参考上面的相关篇,有详细说明和介绍。

/**
 * 函数功能: 读取状态寄存器
 * 输入参数:Type: 1:状态寄存器1 2:状态寄存器2 3:状态寄存器3
 */
uint8_t qspi_flash_read_status(uint8_t CH)
{
uint8_t status = 0;
  QSPI_CommandTypeDef command;
 /* 用于等待发送完成标志 */
RxCplt = 0;
  command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
  command.AddressSize = QSPI_ADDRESS_24_BITS;
  command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
  command.DdrMode = QSPI_DDR_MODE_DISABLE;
  command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
  command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;

  switch (CH)
  {
  case 1:
    command.Instruction = READ_STATUS_REG_CMD; // 读状态寄存器1指令0x05,读取:S7-S0
    break;
  case 2:
    command.Instruction = READ_STATUS2_REG_CMD; // 读状态寄存器2指令//0x35读取:S15-S8
    break;
  case 3:
    command.Instruction = READ_STATUS3_REG_CMD; // 读状态寄存器3指令
    break;
  default:
    command.Instruction = READ_STATUS_REG_CMD;
    break;
  }
  command.AddressMode = QSPI_ADDRESS_NONE;
  command.DataMode = QSPI_DATA_1_LINE;
  command.DummyCycles = 0;
  command.NbData = 1;

  HAL_QSPI_Command(&QSPIHandle, &command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE);

  HAL_QSPI_Receive_DMA(&QSPIHandle, &status);
/* 等待数据发送完毕 */
while(RxCplt == 0);
RxCplt = 0;

return status;
}
  • 状态寄存器读取
typedef struct {
  uint8_t uniq_id[8];
  uint16_t page_size;
uint32_t  id;
  uint32_t page_count;
  uint32_t sector_size;
  uint32_t sector_count;
  uint32_t block_size;
  uint32_t block_count;
  uint32_t capacity_in_kilobyte;
  uint8_t status_register1;
  uint8_t status_register2;
  uint8_t status_register3;
  uint8_t lock;
  char *desc;
} spi_flash_info;

/**
 * @struct W25Q_STATUS_REG
 * @brief  W25Q Status Registers
 * @TODO: Mem protected recognition
 *
 * Structure to check chip's status registers
 * @{
 */
typedef struct{
bool BUSY;  ///< Erase/Write in progress
bool WEL;///< Write enable latch (1 - write allowed)
bool QE;///< Quad SPI mode
bool SUS; ///< Suspend Status
bool ADS; ///< Current addr mode (0-3 byte / 1-4 byte)
bool ADP; ///< Power-up addr mode
bool SLEEP; ///< Sleep Status
}W25Q_STATUS_REG;

static spi_flash_info sf_info;
static W25Q_STATUS_REG w25q_status;

static int qspi_flash_config(void)
{
    uint32_t id ;
    uint8_t status1, status2, status3;

    id =QSPI_ReadID() ;
    status1 = qspi_flash_read_status(1);
    status2 = qspi_flash_read_status(2); // 指令:0x35:S15-S8
    status3 = qspi_flash_read_status(3);
    sf_info.status_register1 = status1;
    sf_info.status_register2 = status2;
    sf_info.status_register3 = status3;
    w25q_status.BUSY = status1 & 0x01;
    w25q_status.WEL = (status1 >> 1) & 0x01;
    w25q_status.QE = (status2 >> 1) & 0x01;
    w25q_status.SUS = (status2 >> 7) & 0x01;
    w25q_status.ADS = status2 & 0x01;
    w25q_status.ADP = (status2 >> 1) & 0x01;
    sf_info.id = id;
    sf_info.uniq_id[0] = (id >> 24) & 0xFF;
    sf_info.uniq_id[1] = (id >> 16) & 0xFF;
    sf_info.uniq_id[2] = (id >> 8) & 0xFF;
    sf_info.uniq_id[3] = id & 0xFF;
    switch(id & 0xFF) {
        case 0x20:
            sf_info.block_count = 1024;
            sf_info.desc = "64M bytes";
            break;
        case 0x19:
            sf_info.block_count = 512;
            sf_info.desc = "32M bytes";
            break;
        case 0x18:
            sf_info.block_count = 256;
            sf_info.desc = "16M bytes";
            break;
        case 0x17:
            sf_info.block_count = 128;
            sf_info.desc = "8M bytes";
            break;
        case 0x16:
            sf_info.block_count = 64;
            sf_info.desc = "4M bytes";
            break;
        case 0x15:
            sf_info.block_count = 32;
            sf_info.desc = "2M bytes";
            break;
        case 0x14:
            sf_info.block_count = 16;
            sf_info.desc = "1M bytes";
            break;
        case 0x13:
            sf_info.block_count = 8;
            sf_info.desc = "512K bytes";
            break;
        case 0x12:
            sf_info.block_count = 4;
            sf_info.desc = "256K bytes";
            break;
        case 0x11:
            sf_info.block_count = 2;
            sf_info.desc = "128K bytes";
            break;
        default:
            sf_info.lock = 0;
            sf_info.desc = "unknown flash";
            return HAL_ERROR;
    }
    sf_info.page_size = 256;
    sf_info.sector_size = 0x1000;
    sf_info.sector_count = sf_info.block_count * 16;
    sf_info.page_count =
        (sf_info.sector_count * sf_info.sector_size) / sf_info.page_size;
    sf_info.block_size = sf_info.sector_size * 16;
    sf_info.capacity_in_kilobyte =
        (sf_info.sector_count * sf_info.sector_size) / 1024;
    return HAL_OK;

}

🛠测试阶段

  • 🌿程序烧录后,复位单片机,默认使用串口1(PA9,PA10)输出。

在这里插入图片描述

  • 指令:2 操作: [0x32]写QSPI Flash, 地址:0x0,长度:1024字节】
    在这里插入图片描述

  • 指令 3 操作 [0x32]写QSPI Flash前10KB空间, 全0x55】
    在这里插入图片描述

  • 指令:5 操作[0x6B]读QSPI Flash, 地址:0x0,长度:1024字节】
    在这里插入图片描述

  • 指令 6 操作 [0x6B]- 读整个串行Flash, 测试读速度】
    在这里插入图片描述

  • 指令 E 操作
    在这里插入图片描述

📚测试工程

  • 🔖修改后,基于AC6.16编译器,匹配的是STM32H750VB
通过网盘分享的文件:V7-029_QSPI读写例程,四线DMA方式,读每秒48MB(V1.1)STM32H750.zip
链接: https://pan.baidu.com/s/1whY8HahTrk8LmrNmQ38y0w?pwd=9w9b 提取码: 9w9b

原文地址:https://blog.csdn.net/weixin_42880082/article/details/142940827

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!