STM32 QSPI接口驱动GD/W25Qxx配置简要

news2024/11/23 13:27:32

STM32 QSPI接口GD/W25Qxx配置简要


  • 📝本篇会具体涉及介绍Winbond(华邦)和GD(兆易创新) NOR flash相关型号指令差异。由于网络上可以搜索到很多相关QSPI相关知识内容,不对QSPI通讯协议做深度解析。
  • 🔖首先确保所使用的STM32型号,硬件上是否支持QSPI功能。
  • 由于GD25QXX和对应的W25Qxx 芯片型号较多,芯片出厂,默认状态寄存器关键位存在差异,在没有进行状态寄存器关键位做配置的情况下。两者的驱动代码不能互为通用,代码上可以通过型号辨别,以及对状态寄存器关键位的使能配置,可以整合为一套通用的驱动代码。
  • 🌿QSPI支持三种工作模式:间接模式、状态轮询模式和内存映射模式:
  • 间接模式:使用QSPI寄存器执行全部操作。
  • 状态轮询模式:周期性读取外部FLASH状态寄存器,当标志位置1时会产生中断(如擦除或烧写完成,产生中断)。
  • 内存映射模式:外部FLASH映射到微控制器地址空间,从而系统将其视作内部存储器进行访问。

在间接模式下也可以切换为内存映射模式进行访问,切换过程需要重新对QSPI外设进行复位和重新初始化QSPI操作,代码操作比较麻烦,好处就是提升了读取访问速度。如果需要进行擦除或编程操作,还需要切换为间接模式下进行。

  • QUADSPI 功能框图(见STM32H750参考手册)
    在这里插入图片描述
    在这里插入图片描述

图中BK2只有在双闪存模式下才需要。

  • 4线SPI结构框图
    在这里插入图片描述

  • 🧲QSPI通讯序列
    QUADSPI 通过命令与 FLASH 通信 每条命令包括指令、地址、交替字节、空指令和数据这 五个阶段 任一阶段均可跳过,但至少要包含指令、地址、交替字节或数据阶段之一。
    nCS 在每条指令开始前下降,在每条指令完成后再次上升
    在这里插入图片描述

对于通讯的整个过程,只需要留意3个状态:WIP(BUSY)用于查看擦除/编程操作是否正在进行、QE用于使能4线模式(Quad)和WREN写使能位其实和WIP位对应,该位决定接下来是否可以进行编程、擦写操作。

  • 📜GD25Q64读写测试:
    在这里插入图片描述
  • 📜W25Q64读写测试:
    在这里插入图片描述
  • 基于QSPI片外flash下载算法实现的片外启动APP程序:
    在这里插入图片描述

📗 NOR FLASH 存储器介绍

  • 🍁Winbond(华邦)W25Q64存储器功能控制结构图
    在这里插入图片描述
  • GD(兆易创新)GD25Q64
    在这里插入图片描述
  • 内存空间结构: 一页(Page)256字节,4K(4096 字节)为一个扇区(Sector),16个扇区为1块(Block),容量为8M字节,共有128个块,2048 个扇区。
  • 支持页编程。每页大小由256字节组成,每页的256字节用一次页编程指令即可完成。
  • 擦除指令通用: 16页(1个扇区4K)、32K、64K、全片擦除操作。

两者常规操作指令互为通用。在QE位都配置为使能状态情况下,除了在读取厂商ID有差异外,读取数据功能函数通用。测试了基于W25QXX制作的片外QSPI下载算法文件都可以兼容GD25XX使用。(前提是下载算法中没有对应闪存厂商ID校验,芯片相关状态寄存器位没有写保护)

🌼Nor Flash操作流程
  • 写入操作时:

写入操作前,必须先进行写使能位.
每个数据位只能由1改写为0,不能由0改写为1
写入数据前必须先擦除,擦除后,所有数据位变为1。.(删除目录实际上是填充0xff)
擦除必须按最小擦除单元进行(一个扇区:4kb)
连续写入多字节时,最多写入一页(256字节)的数据,超过页尾位置的数据,会回到页首覆盖写入
写入操作结束后,芯片进入忙状态,不响应新的读写操作

  • 读取操作时:

直接调用读取时序,快速读取(Fast Read Quad I/O (EBh)),需要QE=1,可以不需要使能Write Enable (WREN) (06H),没有页的限制,读取操作结束后不会进入忙状态,但不能在忙状态时读取.

📘Nor flash状态寄存器介绍

  • 🌿winbond(华邦)W25QXX状态寄存器访问指令
    在这里插入图片描述
  • 📄具体状态寄存器位说明
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
📘 GD25QXX状态寄存器
  • 🌿GD(兆易创新)GD25QXX状态寄存器总表:

在这里插入图片描述

🔰状态寄存器差异化说明

  • 🌿QE位:该位属于非易失性(non-volatile),数据在断电后仍然能够保持的特性。一旦该位配置位1,将一直保持其状态,不会断电丢失。
  • 🔖winbond(华邦)W25QXX,该位出厂默认状态是根据芯片具体型号决定该位默认状态是否为1
  • When the QE bit is set to a 0 state (factory default for part numbers with ordering options “IM”&“JM”).
  • When the QE bit is set to a 1 (factory fixed default for part numbers with ordering options “IQ” & “JQ”),
  • 🔖GD(兆易创新)GD25Qxx,该位出厂默认状态是0,如需要使用QSPI 模式,必须先将该位配置为1才行。
  • ✨QE位的使能,对于QSPI 4线Quad访问方式尤为重要。

📘指令介绍

  • 🌿winbond(华邦)W25Q64指令:
    在这里插入图片描述
    在这里插入图片描述

  • 🌿GD(兆易创新)GD25Q64指令集

在这里插入图片描述

在这里插入图片描述

🛠状态寄存器配置

  • 🌿状态寄存器1,QE位配置流程说明

👉针对GD(兆易创新)GD25Q64,经测试,如需修改QE位,可以通过使用写状态寄存器1(0x01)命令,读取2个字节数据(S0-S7、S8-S16),再进行修改。

  • 🔖个人测试时,如果单独写状态寄存器2的QE位,会写入不成功。
  • QE位是否配置成功,可以通过读状态寄存器(0x35),进行查询。
  • Write Status Register (WRSR) (01H or 31H or 11H)
    The Write Status Register (WRSR) command allows new values to be written to the Status Register. Before it can be accepted, a Write Enable (WREN) command must previously have been executed. After the Write Enable (WREN)
    command has been decoded and executed, the device sets the Write Enable Latch (WEL).
    The Write Status Register (WRSR) command has no effect on S15, S10, S1 and S0 of the Status Register. For command code of “01H” / “31H” / “11H”, the Status Register bits S7~S0 / S15~S8 / S23~S16 would be written. CS# must be driven high after the eighth bit of the data byte has been latched in. Otherwise, the Write Status Register (WRSR) command is not executed. As soon as CS# is driven high, the self-timed Write Status Register cycle (whose duration is tW) is initiated. While the Write Status Register cycle is in progress, the Status Register may still be read to check the value of the Write In Progress (WIP) bit. The Write In Progress (WIP) bit is 1 during the self-timed Write Status Register cycle, and is 0 when it is completed. When the cycle is completed, the Write Enable Latch (WEL) is reset.
    The Write Status Register (WRSR) command allows the user to change the values of the Block Protect (BP4, BP3, BP2, BP1, and BP0) bits, to define the size of the area that is to be treated as read-only. The Write Status Register (WRSR) command also allows the user to set or reset the Status Register Protect (SRP1 and SRP0) bits in accordance with the Write Protect (WP#) signal. The Status Register Protect (SRP1 and SRP0) bits and Write Protect (WP#) signal allow the device to be put in the Hardware Protected Mode. The Write Status Register (WRSR) command is not executed once the Hardware Protected Mode is entered

  • 🧬 时序图:
    在这里插入图片描述

  • 🌿读状态寄存器代码实现:

int qspi_flash_read_status1(uint8_t *status) {//读状态寄存器1
  QSPI_CommandTypeDef command;

  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;

  command.Instruction = READ_STATUS_REG_CMD; //0x05,读取:S7-S0
  command.AddressMode = QSPI_ADDRESS_NONE;
  command.DataMode = QSPI_DATA_1_LINE;
  command.DummyCycles = 0;
  command.NbData = 1;//1个字节

  if (HAL_QSPI_Command(&hqspi, &command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) !=
      HAL_OK) {
    return HAL_ERROR;
  }

  if (HAL_QSPI_Receive(&hqspi, status, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) !=
      HAL_OK) {
    return HAL_ERROR;
  }
  return HAL_OK;
}

int qspi_flash_read_status2(uint8_t *status) {读状态寄存器2
  QSPI_CommandTypeDef command= {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;

  command.Instruction = READ_STATUS2_REG_CMD;//0x35读取:S15-S8
  command.AddressMode = QSPI_ADDRESS_NONE;
  command.DataMode = QSPI_DATA_1_LINE;
  command.DummyCycles = 0;
  command.NbData = 1;
 
  if (HAL_QSPI_Command(&hqspi, &command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) !=
      HAL_OK) {
    return HAL_ERROR;
  }

  if (HAL_QSPI_Receive(&hqspi, status, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) !=
      HAL_OK) {
    return HAL_ERROR;
  }
  return HAL_OK;
}
  • 🌿写状态寄存器代码实现:
int qspi_flash_write_status(uint8_t status1_val, uint8_t status2_val) {写状态寄存器1-2
	
  QSPI_CommandTypeDef command = {0};
  uint8_t buf[2];
  TxCplt = 0;
  qspi_flash_write_enable();

  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_ONLY_FIRST_CMD;

  command.Instruction = WRITE_STATUS_REG_CMD;
  command.DummyCycles = 0;
  command.AddressMode = QSPI_ADDRESS_NONE;
  command.DataMode = QSPI_DATA_1_LINE;
  command.NbData = 2;//2个字节(S0-S7,S7-S16)
  buf[0] = status1_val;
  buf[1] = status2_val;

  if (HAL_QSPI_Command(&hqspi, &command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) !=
      HAL_OK) {
    return HAL_ERROR;
  }

  if (HAL_QSPI_Transmit(&hqspi, buf, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) !=
      HAL_OK) {
    return HAL_ERROR;
  }

  if (qspi_flash_atuo_polling_mem_ready() != HAL_OK) {
    return HAL_ERROR;
  }
  return HAL_OK;
}


📘读数据操作

  • 主要介绍4线QUAD操作指令。
  • 🌿Quad I/O Fast Read (EBH)
    Quad I/O快速读取命令可以通过在输入3字节地址(A23-A0)之后设置“连续读取模式”位(M7-0),从而进一步降低命令开销。如果“连续读模式”位(M5-4)=(1,0),则下一个四I/O快速读命令(在CS#升降之后)不需要EBH命令代码。如果“连续读模式”位(M5-4)=(1,0),下一个命令需要该命令代码,从而恢复正常操作。重置命令之前也可以用来在发出正常命令前重置(M7-0)。

在这里插入图片描述
在这里插入图片描述

⏰Dummy Cycle空指令周期
  • 🌿在STM32H7参考手册上,QUADSPI 通信配置寄存器 (QUADSPI_CCR),指定 CLK 周期数范围一般: 0 - 31。

对于stm32H7一般在快速读取数据时,默认设定的是6个Dummy Cycle,适用的最大工作频率是104MHz.验证这个参数是否设定合理,验证方法,读写数据页内容多次进行查看,验证数据是否正确即可。如果该参数设置不对,读取的数据和写入的数据对应不上,或者会出全部是0、或者是一个其他的uint8_t类型任意的固定或不固定的数值,也可能出现读取出来的数据和写入的数据和对应的地址错位的情况。

  • Winbond(华邦)W25Q64:没有找到相关描述
  • GD(兆易创新)GD25Q64
    在这里插入图片描述
  • 🌿读数据实现
/**
 * @brief   读NOR Flash
 * @note    从指定地址开始读取指定长度的数据
 * @param   pbuf   : 读取到数据保存的地址
 * @param   pbuf   : 指定开始读取的地址
 * @param   datalen: 指定读取数据的字节数
 * @retval  无
 */
void norflash_read(uint8_t *pbuf, uint32_t addr, uint16_t datalen)
{
    QSPI_CommandTypeDef qspi_command_struct = {0};
	qspi_flash_write_enable();//使能QE位
    qspi_command_struct.InstructionMode = QSPI_INSTRUCTION_1_LINE;
    qspi_command_struct.Instruction = QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD;//Quad I/O Fast Read EBH
    qspi_command_struct.AddressMode = QSPI_ADDRESS_4_LINES;
    qspi_command_struct.AddressSize = QSPI_ADDRESS_24_BITS;
    qspi_command_struct.Address = addr;
    qspi_command_struct.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    qspi_command_struct.DummyCycles = 6; //此处必须要有空闲等待时间,否则数据和地址会出现错位
    qspi_command_struct.DataMode = QSPI_DATA_4_LINES;
    qspi_command_struct.NbData = datalen;
    qspi_command_struct.DdrMode = QSPI_DDR_MODE_DISABLE;
    qspi_command_struct.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    qspi_command_struct.SIOOMode = QSPI_SIOO_INST_ONLY_FIRST_CMD;

    HAL_QSPI_Command(&hqspi, &qspi_command_struct, HAL_QSPI_TIMEOUT_DEFAULT_VALUE);

    HAL_QSPI_Receive(&hqspi, pbuf, HAL_QSPI_TIMEOUT_DEFAULT_VALUE);
}

📗擦除和写入操作

之所以将擦除和写入放在一起说明,是因为nor flash写操作前,需要先对当前要写入数据的地址扇区进行擦除操作,目的是为了将要写入的区域,保证为空(0xff),才能保证写入的数据的准确性。需要注意的是,最小擦除大小为扇区单位(4K)。

  • 📝扇区擦除操作:
int qspi_flash_erase_sector(uint32_t address) {

	uint32_t EraseStartAddress =  address;
	  EraseStartAddress &=0x0FFFFFFF; //最大可寻址范围
  EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x1000;//起始地址-扇区地址
  QSPI_CommandTypeDef command = {0};
  qspi_flash_write_enable();//写使能QE=1
  command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
  command.AddressSize = QSPI_ADDRESS_24_BITS;//24位地址,可寻址范围小于或等于16MB
  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;

  command.Instruction = SUBSECTOR_ERASE_4_BYTE_ADDR_CMD;//擦除指令0x20
  command.AddressMode = QSPI_ADDRESS_1_LINE;
  command.Address = EraseStartAddress;
  command.DataMode = QSPI_DATA_NONE;
  command.DummyCycles = 0;

  if (HAL_QSPI_Command(&hqspi, &command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) !=
      HAL_OK) {
    return HAL_ERROR;
  }

  if (qspi_flash_atuo_polling_mem_ready() != HAL_OK) {
    return HAL_ERROR;
  }

  return HAL_OK;
}

  • 📑写操作
/* 扇区缓存 */
static uint8_t g_norflash_buf[4096];

/**
 * @brief   写NOR Flash
 * @note    在指定地址开始写入指定长度的数据,该函数带擦除操作
 *          NOR Flash一般是:256个字节为一个Page,4096个字节为一个Sector,16个Sector为1个Block
 *          擦除的最小单位为Sector
 * @param   pbuf   : 待写入数据的起始地址
 * @param   addr   : 指定开始写入数据的地址
 * @param   datalen: 指定写入数据的字节数
 * @retval  无
 */
void norflash_write(uint8_t *pbuf, uint32_t addr, uint16_t datalen)
{
    uint32_t secpos;
    uint16_t secoff;
    uint16_t secremain;
    uint16_t i;
    uint8_t *norflash_buf;

    norflash_buf = g_norflash_buf;                                      /* 扇区缓存 */
    secpos = addr / 4096;                                               /* 扇区索引 */
    secoff = addr % 4096;                                               /* 写入起始地址在当前扇区的偏移量 */
    secremain = 4096 - secoff;                                          /* 计算当扇区剩余的字节数 */
    if (datalen <= secremain)                                           /* 不大于4096个字节 */
    {
        secremain = datalen;
    }

    while (1)
    {
        norflash_read(norflash_buf, secpos * 4096, 4096);               /* 读出当前整个扇区的内容,用于擦除扇区后恢复非写入区域的数据 */

        for (i=0; i<secremain; i++)                                     /* 校验数据 */
        {
            if (norflash_buf[secoff + i] != 0xFF)                       /* 写入区域中有非0xFF的数据,就需要擦除 */
            {
                break;
            }
        }

        if (i < secremain)                                              /* 需要擦除 */
        {
//                            /* 擦除整个扇区 */
        qspi_flash_erase_sector(secpos) ;
            for (i=0; i<secremain; i++)                                 /* 将待写入的数据,先写入扇区缓存 */
            {
                norflash_buf[i + secoff] = pbuf[i];
            }

            norflash_write_nocheck(norflash_buf, secpos * 4096, 4096);  /* 写入整个扇区 */
        }
        else                                                            /* 不需要擦除,可以直接写入 */
        {
            norflash_write_nocheck(pbuf, addr, secremain);              /* 直接写扇区剩余的空间 */
        }

        if (datalen == secremain)                                       /* 写入结束 */
        {
            break;
        }
        else                                                            /* 写入未结束 */
        {
            secpos++;                                                   /* 扇区索引增1 */
            secoff = 0;                                                 /* 扇区偏移位置为0 */
            pbuf += secremain;                                          /* 偏移pbuf指针地址secremain个已写入的字节数 */
            addr += secremain;                                          /* 偏移写入地址secremain个已写入的字节数 */
            datalen -= secremain;                                       /* 计算剩余写入的字节数 */
            if (datalen > 4096)                                         /* 剩余数据量超过4096,只能先写入一个扇区数据 */
            {
                secremain = 4096;
            }
            else                                                        /* 剩余数据量不超过4096,可一次性写入 */
            {
                secremain = datalen;
            }
        }
    }
}

📓复位nor flash操作

  • GD25Q64复位使能和复位指令:Enable Reset (66H) and Reset (99H)
  • 复位内容:Volatile settings:易失性设置,包括易失性状态寄存器位、写使能锁存状态(WEL)、编程/擦除暂停状态、读取参数设置(P7-P0)、连续读取模式位设置(M7-M0)和环绕位设置(W6-W4).
  • 复位流程:执行复位命令序列包括:先发送使能复位命令(66H),然后将CS#拉高,再发送复位命令(99H),最后将CS#拉高。设备在接受复位命令后会花费大约tRST / tRST_E的时间来完成复位,在此期间不会接受任何其他命令。

在这里插入图片描述

  • 🌿High-Z时间:

在这里插入图片描述

  • STM32H750 ,时钟配置为400MHz,执行一个__NOP( )控制指令时间:1/400MHz=2.5ns,
  • 🌿复位命令发出之后,到nor flash 执行完内部复位所需的时间:12ms
    在这里插入图片描述

  • 来自:https://doc.embedfire.com/mcu/stm32/h743prov/hal/zh/latest/book/QSPI.html#qspi-7

**
* @brief  复位QSPI存储器。
* @param  QSPIHandle: QSPI句柄
* @retval 无
*/
static uint8_t QSPI_ResetMemory()
{
    QSPI_CommandTypeDef s_command;
    /* 初始化复位使能命令 */
    s_command.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
    s_command.Instruction       = RESET_ENABLE_CMD;//0x66
    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(&QSPIHandle, &s_command,
        HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
        return QSPI_ERROR;
    }

    /* 发送复位存储器命令 0x99*/
    s_command.Instruction = RESET_MEMORY_CMD;
    if (HAL_QSPI_Command(&QSPIHandle, &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;
}

📗QSPI外设引脚配置

  • 🔖QSPI外设引脚配置不是固定的,可以根据实际使用情况,可以映射到任意支持的引脚上。

在这里插入图片描述

  • 🔨QSPI引脚配置:需要将外设引脚的速度设置为非常高(Very HIGH)。
    在这里插入图片描述
  • 🔖需要注意CS引脚配置,不能使能Fast Mode.
void MX_QUADSPI_Init(void)
{

  /* USER CODE BEGIN QUADSPI_Init 0 */

  /* USER CODE END QUADSPI_Init 0 */

  /* USER CODE BEGIN QUADSPI_Init 1 */

  /* USER CODE END QUADSPI_Init 1 */
  hqspi.Instance = QUADSPI;
  hqspi.Init.ClockPrescaler = 1; //200MHz/(1+1)=100MHz
  hqspi.Init.FifoThreshold = 32; //最大支持32
  hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE;
  hqspi.Init.FlashSize = 22;//8MB= 2^[FSIZE+1] =1<<23
  
  hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_6_CYCLE;//信号(CS)的高电平持续时间
  hqspi.Init.ClockMode = QSPI_CLOCK_MODE_0;/*时钟模式选择模式0,nCS为高电平(片选释放),传输数据CLK保持低电平*/
  hqspi.Init.FlashID = QSPI_FLASH_ID_1;
  hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE;
  if (HAL_QSPI_Init(&hqspi) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN QUADSPI_Init 2 */

  /* USER CODE END QUADSPI_Init 2 */

}
  • 有关信号时间参数信息:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 📝QSPI初始化配置:(参考野火内容介绍:https://doc.embedfire.com/mcu/stm32/h743prov/hal/zh/latest/book/QSPI.html#qspi-7

/**
* @brief  初始化QSPI存储器
* @retval QSPI存储器状态
*/
uint8_t BSP_QSPI_Init(void)
{
    QSPI_CommandTypeDef s_command;
    uint8_t value = W25Q256JV_FSR_QE;//0X02

    /* QSPI存储器复位 */
    if (QSPI_ResetMemory() != QSPI_OK) {
        return QSPI_NOT_SUPPORTED;
    }
    /* 使能写操作 */
    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(&QSPIHandle, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
        return QSPI_ERROR;
    }
    /* 传输数据 */
    if (HAL_QSPI_Transmit(&QSPIHandle, &value, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
        return QSPI_ERROR;
    }
    /* 自动轮询模式等待存储器就绪 */
    if (QSPI_AutoPollingMemReady(W25Q256JV_SUBSECTOR_ERASE_MAX_TIME) != QSPI_OK) {
        return QSPI_ERROR;
    }

    /* 配置地址模式为 4 字节 */
    if (QSPI_Addr_Mode_Init() != QSPI_OK) {
        return QSPI_ERROR;
    }

    return QSPI_OK;
}
  • 🍕相关参考和资料推荐
  • 📌安富莱_STM32-V7:https://www.armbbs.cn/forum.php?mod=viewthread&tid=91590&highlight=STM32-V7
  • 📍QSPI驱动例程可以参考:正点原子STM32H750中的相关例程:http://47.111.11.73/docs/boards/stm32/zdyz_stm32h750_polaris.html
  • 🎈或者参考野火版的开发板资料:https://gitee.com/Embedfire-stm32h7-pro/ebf_stm32h743_pro_code_v
  • 🥕间接模式切换内存映射模式项目参考:https://github.com/ziancube/GD25Qxx-Stm32h747

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2209442.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2022年10月自考《数据库系统原理》04735试题

目录 一.单选题 二.填空题 三.设计题 四.简答题 五.综合题 一.单选题 1.数据库系统管理员的英文缩写是 (书中)P29页 A.Data B.DB C.DBA D.DBS 2.客户/服务器模式中&#xff0c;客户端和服务器可以同时工作在同一台计算机上&#xff0c;该方式称为 (书中)P37页 A.单机方…

使用Copilot自动在Rstudio中写代码,提高效率!

原文链接&#xff1a;使用Copilot自动在Rstudio中写代码&#xff0c;提高效率&#xff01; 2022年教程总汇 2023年教程总汇 引言 今天我们分享&#xff0c;在Rstuido中使用copilot自动写代码&#xff0c;提高你的分析和绘图效率。 copilot是2024年9月后引入到Rstuido中&…

如何在 IDEA 中导入 Java 项目的 Git 仓库并启动

目录 前言1. 从 Git 仓库导入 Java 项目2. 配置 Maven2.1 配置 Maven 仓库和设置文件2.2 加载依赖 3. 配置 Tomcat 并运行项目3.1 配置 Tomcat3.2 配置 Server URL3.3 启动项目 4. 常见问题与解决方法4.1 Maven 依赖无法下载4.2 Tomcat 部署失败4.3 项目启动后无法访问 结语 前…

从RNN讲起——序列数据处理网络

文章目录 RNN&#xff08;Recurrent Neural Network&#xff0c;循环神经网络&#xff09;1. 什么是RNN&#xff1f;2. 经典RNN的结构3. RNN的主要特点4. RNN存在问题——长期依赖&#xff08;Long-TermDependencies&#xff09;问题 LSTM&#xff08;Long Short-Term Memory&a…

使用libssh2实现多线程模式的文件上传与下载

使用libssh2实现多线程模式的文件上传与下载 一、准备工作二、初始化SSH连接三、文件上传与下载四、多线程处理五、总结libssh2 是一个开源的SSH库,用于在C/C++程序中实现SSH2协议的功能。通过libssh2,我们可以方便地进行远程登录、执行命令、上传和下载文件等操作。在多线程…

一区大黄蜂!人工蜂群算法优化!ABC-CNN-LSTM-MATT多特征分类预测

一区大黄蜂&#xff01;人工蜂群算法优化&#xff01;ABC-CNN-LSTM-MATT多特征分类预测 目录 一区大黄蜂&#xff01;人工蜂群算法优化&#xff01;ABC-CNN-LSTM-MATT多特征分类预测分类效果基本介绍程序设计参考资料 分类效果 基本介绍 1.Matlab实现ABC-CNN-LSTM-MATT人工蜂群…

c++关于内存的知识点上速成

温馨提示&#xff1a;本篇文章的内容涉及的是c内存的管理方式 c内存管理的方式 new的使用方式 类型 对象名 new 类型 注意&#xff1a;如果对象名前面的类型有星号&#xff0c;后面的类型&#xff08;new后面的&#xff09;不需要星号 样例&#xff1a; delete的使用方…

VMDK 0X80BB0005 VirtualBOX虚拟机错误处理-数据恢复——未来之窗数据恢复

打开虚拟盘文件in7.vmdk 失败. Could not get the storage format of the medium 7\win7.vmdk (VERR_NOT_SUPPORTED). 返回 代码:VBOX_E_IPRT_ERROR (0X80BB0005) 组件:MediumWrap 界面:IMedium {a a3f2dfb1} 被召者:IVirtualBox {768 cd607} 被召者 RC:VBOX_E_OBJECT_NOT_F…

生成式专题的第四节课--CycleGAN

CycleGAN&#xff08;Cycle-Consistent Generative Adversarial Network&#xff0c;循环生成对抗网络&#xff09;是一种用于无监督图像转换的深度学习模型&#xff0c;即一种用于图像到图像转换任务的生成对抗网络&#xff08;GAN&#xff09;的变体&#xff0c;它可以在没有…

团标大数据(2024年09月)

一、总体数据 截至2024年09月30日&#xff0c;共有8240家社会团体在全国团体标准信息平台注册&#xff0c;其中民政部登记注册的有973家&#xff0c;地方民政部门登记注册的有7267家。社会团体在平台共计公布89857项团体标准&#xff0c;其中民政部登记注册的社会团体公布3603…

点云数据与多相机图像融合实现3D场景的彩色可视化

引言 在现代3D计算机视觉和机器人感知领域&#xff0c;点云数据和图像信息的融合正变得越来越重要。点云数据提供了精确的几何结构&#xff0c;而图像则包含了丰富的颜色和纹理细节。将这两种数据源结合起来&#xff0c;我们能够创建更加逼真和信息丰富的3D场景表示。本文将深…

【C++】拆分详解 - vector

文章目录 一、vector的介绍二、vector的使用1. 构造2. 迭代器3. vector 空间增长问题4. 增删查改5. vector 迭代器失效问题5.1 底层空间改变&#xff08;扩容、缩容&#xff09;5.2 指定位置元素的删除操作5.3 Linux与VS平台差异 三、vector 模拟实现0. 整体框架1. 构造 / 析构…

4个方法教你快速取消Word文档底色

在使用Word编辑文档时&#xff0c;我们有时会遇到文字或段落带有不必要的底色&#xff0c;这不仅影响文档的美观&#xff0c;还可能干扰阅读。那么&#xff0c;如何轻松去除这些底色呢&#xff1f;以下是几种实用的方法&#xff1a; 方法1&#xff1a;使用底纹功能键 首先&…

点可云ERP进销存V8版本——其他收入单使用说明进

其他收入单用于记录除销售内容外其他收入资金&#xff0c;如&#xff1a;废品出售、安装维修服务等。新增保存之后&#xff0c;对应资金账户将增加金额额度&#xff0c;并做存储记录&#xff0c;可在现金银行报表中体现。 新增操作 接下来我们讲解新增单据步骤。如上图所示&am…

怎样设置Windows系统不会自动同步时间

一、背景 我们在进行测试一些软件的时候需要调整Windows系统的时间到指定的日期,并且希望这个手动调整的日期可以固定住不变,不希望电脑重启后恢复到当前的最新日期。 二、操作方法 注意:如下的操作方法是以Windows7系统为例进行演示说明: 1、选中右下角的日期然后点击鼠…

C++笔记---红黑树的插入删除

1. 红黑树的概念 红黑树是一棵二叉搜索树&#xff0c;他的每个结点增加一个存储位来表示结点的颜色&#xff0c;可以是红色或者黑色。 通过对任何一条从根到叶子的路径上各个结点的颜色进行约束&#xff0c;红黑树确保对于任意一个结点&#xff0c;没有一条到NULL结点的路径会…

【C++算法】9.滑动窗口_长度最小的子数组

文章目录 题目链接&#xff1a;题目描述&#xff1a;解法C 算法代码&#xff1a;图解 题目链接&#xff1a; 209. 长度最小的子数组 题目描述&#xff1a; 解法 解法一&#xff1a;暴力求解&#xff08;会超时&#xff09; 暴力枚举出所有子数组的和。 查找子数组n2&#xff0…

【hot100-java】K 个一组翻转链表

链表篇 参考题解 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode next) { this.val val; this.next next; }* }*/ …

文件传输遗漏

查看失败的 Failed to transfer file ‘E:\m3dmpre\datasets\mvtec3d\foam\train\good\xyz\184.tiff’. Could not close the output stream for file “sftp://172.29.6.20/home/cszx/zgp/datasets/mvtec3d/foam/train/good/xyz/184.tiff”. 将faild的全部重传一遍

知识二: 马尔科夫决策过程

强化学习从入门到精通&#xff08;马尔科夫决策过程&#xff09;&#xff08;7天入门强化学习&#xff09; 知识二&#xff1a;马尔科夫决策过程 先介绍马尔可夫过程&#xff08;Markov process&#xff09;以及马尔可夫奖励过程&#xff08;Markov reward process&#xff0…