[其他ST产品] STM32的QSPI通信(学习笔记)

[复制链接]
 楼主| kqh11a 发表于 2023-7-30 15:42 | 显示全部楼层
(4) AddressSize

本成员定义地址长度,可以是8位,16位,24位或者32位。
 楼主| kqh11a 发表于 2023-7-30 15:42 | 显示全部楼层
(5) AlternateBytesSize

本成员定义交替字节长度,可以是8位,16位,24位或者32位。
 楼主| kqh11a 发表于 2023-7-30 15:42 | 显示全部楼层
(6) DummyCycles

本成员定义空指令阶段的持续时间,在 SDR 和 DDR 模式下,它指定 CLK 周期数 (0-31)。
 楼主| kqh11a 发表于 2023-7-30 15:43 | 显示全部楼层
(7) InstructionMode

本成员定义指令阶段的操作模式,00:无指令;01:单线传输指令;10:双线传输指令;11:四线传输指令。
 楼主| kqh11a 发表于 2023-7-30 15:43 | 显示全部楼层
(8) AddressMode

本成员定义地址阶段的操作模式,00:无地址;01:单线传输地址;10:双线传输地址;11:四线传输地址。
 楼主| kqh11a 发表于 2023-7-30 15:43 | 显示全部楼层
(9) AlternateByteMode

本成员定义交替字节阶段的操作模式00:无交替字节;01:单线传输交替字节;10:双线传输交替字节;11:四线传输交替字节。
 楼主| kqh11a 发表于 2023-7-30 15:43 | 显示全部楼层
(10) DataMode

本成员定义数据阶段的操作模式,00:无数据;01:单线传输数据;10:双线传输数据;11:四线传输数据。该字段还定义空指令阶段的操作模式。
 楼主| kqh11a 发表于 2023-7-30 15:43 | 显示全部楼层
(11) NbData

本成员设置数据长度,在间接模式和状态轮询模式下待检索的数据数量(值 + 1)。对状态轮询模式应使用不大于 3 的值(表示 4 字节)。
 楼主| kqh11a 发表于 2023-7-30 15:43 | 显示全部楼层
(12) DdrMode

本成员为地址、交替字节和数据阶段设置 DDR 模式,0:禁止 DDR 模式;1:使能 DDR 模式。
 楼主| kqh11a 发表于 2023-7-30 15:43 | 显示全部楼层
(13) DdrHoldHalfCycle

本成员设置DDR 模式下数据输出延迟 1/4 个 QUADSPI 输出时钟周期,0:使用模拟延迟来延迟数据输出;1:数据输出延迟 1/4 个 QUADSPI 输出时钟周期。仅在 DDR 模式下激活。
 楼主| kqh11a 发表于 2023-7-30 15:43 | 显示全部楼层
(14) SIOOMode

本成员设置仅发送指令一次模式,。IMODE = 00 时,该位不起作用。0:在每个事务中发送指令;1:仅为第一条命令发送指令。
 楼主| kqh11a 发表于 2023-7-30 15:44 | 显示全部楼层
三、QSPI—读写串行FLASH实验
9158364c614be58ec5.png
 楼主| kqh11a 发表于 2023-7-30 15:44 | 显示全部楼层
(1) 初始化通讯使用的目标引脚及端口时钟;
  1.       GPIO_InitTypeDef GPIO_InitStruct;

  2.      /* 使能 QSPI 及 GPIO 时钟 */
  3.      QSPI_FLASH_CLK_ENABLE();
  4.      QSPI_FLASH_CLK_GPIO_ENABLE();
  5.      QSPI_FLASH_BK1_IO0_CLK_ENABLE();
  6.      QSPI_FLASH_BK1_IO1_CLK_ENABLE();
  7.      QSPI_FLASH_BK1_IO2_CLK_ENABLE();
  8.      QSPI_FLASH_BK1_IO3_CLK_ENABLE();
  9.      QSPI_FLASH_CS_GPIO_CLK_ENABLE();
 楼主| kqh11a 发表于 2023-7-30 15:44 | 显示全部楼层
(2) 配置SPI外设的模式、地址、速率等参数并使能SPI外设;


  1.      /* QSPI_FLASH 模式配置 */
  2.      QSPIHandle.Instance = QUADSPI;
  3.      QSPIHandle.Init.ClockPrescaler = 2;
  4.      QSPIHandle.Init.FifoThreshold = 4;
  5.      QSPIHandle.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE;
  6.      QSPIHandle.Init.FlashSize = 23;
  7.      QSPIHandle.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_8_CYCLE;
  8.      QSPIHandle.Init.ClockMode = QSPI_CLOCK_MODE_0;
  9.      HAL_QSPI_Init(&QSPIHandle);
 楼主| kqh11a 发表于 2023-7-30 15:44 | 显示全部楼层
(3)初始化QSPI接口;
  1. uint8_t BSP_QSPI_Init(void)

  2.   {

  3.       QSPI_CommandTypeDef s_command;

  4.       uint8_t value = W25Q128FV_FSR_QE;

  5.      /* QSPI存储器复位 */

  6.      if (QSPI_ResetMemory() != QSPI_OK) {

  7.          return QSPI_NOT_SUPPORTED;

  8.      }

  9.      /* 使能写操作 */

  10.      if (QSPI_WriteEnable() != QSPI_OK) {

  11.          return QSPI_ERROR;

  12.      }

  13.      /* 设置四路使能的状态寄存器,使能四通道IO2和IO3引脚 */

  14.      s_command.InstructionMode   = QSPI_INSTRUCTION_1_LINE;

  15.      s_command.Instruction       = WRITE_STATUS_REG2_CMD;

  16.      s_command.AddressMode       = QSPI_ADDRESS_NONE;

  17.      s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;

  18.      s_command.DataMode          = QSPI_DATA_1_LINE;

  19.      s_command.DummyCycles       = 0;

  20.      s_command.NbData            = 1;

  21.      s_command.DdrMode           = QSPI_DDR_MODE_DISABLE;

  22.      s_command.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;

  23.      s_command.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;

  24.      /* 配置命令 */

  25. if (HAL_QSPI_Command(&QSPIHandle, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK){

  26.          return QSPI_ERROR;

  27.      }

  28.      /* 传输数据 */

  29. if (HAL_QSPI_Transmit(&QSPIHandle, &value, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK){

  30.          return QSPI_ERROR;

  31.      }

  32.     /* 自动轮询模式等待存储器就绪 */

  33.      if (QSPI_AutoPollingMemReady(W25Q128FV_SUBSECTOR_ERASE_MAX_TIME) != QSPI_OK) {

  34.          return QSPI_ERROR;

  35.      }
  36.      return QSPI_OK;
  37. }
 楼主| kqh11a 发表于 2023-7-30 15:44 | 显示全部楼层
(4) 编写基本SPI按字节收发的函数;
  1. /**接收函数
  2.     * [url=home.php?mod=space&uid=247401]@brief[/url]  从QSPI存储器中读取大量数据.
  3.     * @param  pData: 指向要读取的数据的指针
  4.     * @param  ReadAddr: 读取起始地址
  5.     * @param  Size: 要读取的数据大小
  6.     * @retval QSPI存储器状态
  7.     */
  8. uint8_t BSP_QSPI_Read(uint8_t* pData, uint32_t ReadAddr, uint32_t Size)
  9.   {
  10.      QSPI_CommandTypeDef s_command;
  11.      /* 初始化读命令 */
  12.      s_command.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
  13.      s_command.Instruction       = READ_CMD;
  14.      s_command.AddressMode       = QSPI_ADDRESS_1_LINE;
  15.      s_command.AddressSize       = QSPI_ADDRESS_24_BITS;
  16.      s_command.Address           = ReadAddr;
  17.      s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
  18.      s_command.DataMode          = QSPI_DATA_1_LINE;
  19.      s_command.DummyCycles       = 0;
  20.      s_command.NbData            = Size;
  21.      s_command.DdrMode           = QSPI_DDR_MODE_DISABLE;
  22.      s_command.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;
  23.      s_command.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;

  24.      /* 配置命令 */
  25. if (HAL_QSPI_Command(&QSPIHandle, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK){
  26.          return QSPI_ERROR;
  27.      }

  28.      /* 接收数据 */
  29.      if(HAL_QSPI_Receive(&QSPIHandle, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)!= HAL_OK) {
  30.          return QSPI_ERROR;
  31.      }
  32.      return QSPI_OK;
  33. }



  34. 发送函数
  35. /**
  36.    * @brief  将大量数据写入QSPI存储器
  37.    * @param  pData: 指向要写入数据的指针
  38.    * @param  WriteAddr: 写起始地址
  39.    * @param  Size: 要写入的数据大小
  40.    * @retval QSPI存储器状态
  41.    */

  42. uint8_t BSP_QSPI_Write(uint8_t* pData, uint32_t WriteAddr, uint32_t Size)

  43. {
  44.      QSPI_CommandTypeDef s_command;

  45.      uint32_t end_addr, current_size, current_addr;

  46.      /* 计算写入地址和页面末尾之间的大小 */

  47.      current_addr = 0;

  48.      while (current_addr <= WriteAddr) {

  49.          current_addr += W25Q128FV_PAGE_SIZE;

  50.      }

  51.      current_size = current_addr - WriteAddr;

  52.      /* 检查数据的大小是否小于页面中的剩余位置 */

  53.      if (current_size > Size) {

  54.          current_size = Size;

  55.      }

  56.      /* 初始化地址变量 */

  57.      current_addr = WriteAddr;

  58.      end_addr = WriteAddr + Size;

  59.      /* 初始化程序命令 */

  60.      s_command.InstructionMode   = QSPI_INSTRUCTION_1_LINE;

  61.      s_command.Instruction       = QUAD_INPUT_PAGE_PROG_CMD;

  62.      s_command.AddressMode       = QSPI_ADDRESS_1_LINE;

  63.      s_command.AddressSize       = QSPI_ADDRESS_24_BITS;

  64.      s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;

  65.      s_command.DataMode          = QSPI_DATA_4_LINES;

  66.      s_command.DummyCycles       = 0;

  67.      s_command.DdrMode           = QSPI_DDR_MODE_DISABLE;

  68.      s_command.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;

  69.      s_command.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;

  70.      /* 逐页执行写入 */

  71.      do {

  72.          s_command.Address = current_addr;

  73.          s_command.NbData  = current_size;

  74.          /* 启用写操作 */

  75.          if (QSPI_WriteEnable() != QSPI_OK) {

  76.              return QSPI_ERROR;
  77.          }

  78.          /* 配置命令 */

  79. if(HAL_QSPI_Command(&QSPIHandle, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {

  80.              return QSPI_ERROR;

  81.          }

  82.         /* 传输数据 */

  83. if(HAL_QSPI_Transmit(&QSPIHandle, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {

  84.              return QSPI_ERROR;
  85.          }

  86.          /* 配置自动轮询模式等待程序结束 */

  87.          if(QSPI_AutoPollingMemReady(HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != QSPI_OK) {

  88.              return QSPI_ERROR;

  89.          }

  90.          /* 更新下一页编程的地址和大小变量 */

  91.          current_addr += current_size;

  92.          pData += current_size;

  93.          current_size = ((current_addr + W25Q128FV_PAGE_SIZE) > end_addr) ? (end_addr-current_addr) : W25Q128FV_PAGE_SIZE;

  94.      } while (current_addr < end_addr);

  95.      return QSPI_OK;
  96. }
 楼主| kqh11a 发表于 2023-7-30 15:45 | 显示全部楼层
(5) 编写对FLASH擦除及读写操作的的函数;

由于FLASH存储器的特性决定了它只能把原来为“1”的数据位改写成“0”,而原来为“0”的数据位不能直接改写为“1”。所以这里涉及到数据“擦除”的概念,在写入前,必须要对目标存储矩阵进行擦除操作,把矩阵中的数据位擦除为“1”,在数据写入的时候,如果要存储数据“1”,那就不修改存储矩阵 ,在要存储数据“0”时,才更改该位。
 楼主| kqh11a 发表于 2023-7-30 15:45 | 显示全部楼层
通常,对存储矩阵擦除的基本操作单位都是多个字节进行,如本例子中的FLASH芯片支持“扇区擦除”、“块擦除”以及“整片擦除”,

3573964c6152a15e21.png
 楼主| kqh11a 发表于 2023-7-30 15:46 | 显示全部楼层
扇区擦除指令的第一个字节为指令编码,紧接着发送的3个字节用于表示要擦除的存储矩阵地址。要注意的是在扇区擦除指令前,还需要先发送“写使能”指令,发送扇区擦除指令后,通过读取寄存器状态等待扇区擦除操作完毕。
  1. /**
  2.     * @brief  擦除QSPI存储器的指定块
  3.     * @param  BlockAddress: 需要擦除的块地址
  4.     * @retval QSPI存储器状态
  5.     */
  6. uint8_t BSP_QSPI_Erase_Block(uint32_t BlockAddress)
  7. {
  8.       QSPI_CommandTypeDef s_command;
  9.       /* 初始化擦除命令 */
  10.      s_command.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
  11.      s_command.Instruction       = SECTOR_ERASE_CMD;
  12.      s_command.AddressMode       = QSPI_ADDRESS_1_LINE;
  13.      s_command.AddressSize       = QSPI_ADDRESS_24_BITS;
  14.      s_command.Address           = BlockAddress;
  15.      s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
  16.      s_command.DataMode          = QSPI_DATA_NONE;
  17.      s_command.DummyCycles       = 0;
  18.      s_command.DdrMode           = QSPI_DDR_MODE_DISABLE;
  19.      s_command.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;
  20.      s_command.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;
  21.      /* 启用写操作 */
  22.      if (QSPI_WriteEnable() != QSPI_OK) {
  23.          return QSPI_ERROR;
  24.      }

  25.      /* 发送命令 */
  26. if(HAL_QSPI_Command(&QSPIHandle, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
  27.          return QSPI_ERROR;
  28.      }

  29.      /* 配置自动轮询模式等待擦除结束 */
  30.      if (QSPI_AutoPollingMemReady(W25Q128FV_SUBSECTOR_ERASE_MAX_TIME) != QSPI_OK) {
  31.          return QSPI_ERROR;
  32.      }
  33.      return QSPI_OK;
  34. }
cemaj 发表于 2023-8-4 13:27 | 显示全部楼层
STM32 QSPI读取时每一个字节后都有延时
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 在线客服 返回列表 返回顶部
快速回复 在线客服 返回列表 返回顶部