STM32存储左右互搏 QSPI总线读写64 Mbit容量SRAM VTI7064

STM32存储左右互搏 QSPI总线读写64 Mbit容量SRAM VTI7064

QSPI(Quad-SPI)设备有两种常见操作模式,一种QSPI设备上电后直接进入QSPI模式,操作时命令,地址和数据都是多线传输。另一种QSPI设备上电后进入常规SPI操作模式,可以通过发送SPI指令,切换设备进入QSPI模式,之后也可以发送QSPI指令切换回SPI模式,故这种设备可以工作在数据率较低的SPI模式,也可工作在数据率更高的QSPI模式。

VTI7064是高容量SPI/QSPI双模式SRAM,容量达到64 Mbit,即8M Byte,可用作嵌入式系统MCU的扩展SRAM,相比FLASH而言访问速度更快且写操作不需要预擦除,适合用作图片或计算数据的缓存。有常压和低压版本:
在这里插入图片描述
在这里插入图片描述
VTI7064的操作命令表,从中可见在QSPI模式的总线时钟速度可达84MHz:
在这里插入图片描述
部分STM32芯片具有Dual QSPI总线接口,可以接单片或双片QSPI设备,在接双片QSPI VTI7064时,达到最高数据传输率。STM32芯片的QSPI接口可达到133MHz时钟频率,高于VTI7064, 故Dual QSPI接2片VTI7064时,理论最高数据率为84M Bytes/s或104M Bytes/s。此时是最接近于并行SDRAM 133MHz的数据率实现,速率虽然仍低于SDRAM,但接口线数少于SDRAM。
在这里插入图片描述
STM32的QSPI介绍可以参考官方文档:
Quad-SPI interface on STM32 microcontrollers and microprocessors (AN4760)
当前如下STM32芯片具有QSPI功能:
在这里插入图片描述

STM32 QSPI接口配置参数

STM32 QSPI接口配置参数有如下一些:
在这里插入图片描述

ClockPrescaler:时钟分频因子,取值>=0, 设置为N时,真实的计算分频因子为N+1,所以设置为0时为1分频即不分频,设置为7时为8分频, 需要注意分频是从哪个时钟分支进行分频(不同系列芯片存在不同):
在这里插入图片描述

Fifo Threshold: 设置发送或接收时,超过多少字节触发一次Fifo门限中断,取值1~32。FIFO模式常用于在源数据传输到目标之前临时存放这些数据,也就是说数据先存放到FIFO中,待FIFO数据量达到一定阈值,再将数据传输到目标, Fifo Threshold描述为缓冲区大小。但实际上并非如此,ST已经修改了Fifo Threshold的用处。对应的Fifo-threshold中断,并非如文档描述所言,在Fifo每次达到设置的阀值时触发,经过代码分析,实际已改为如下功能实现
在这里插入图片描述
可参考Fifo-threshold中断的响应函数官方代码理解分析:

/**
  * @brief Handle QSPI interrupt request.
  * @param hqspi QSPI handle
  * @retval None
  */
void HAL_QSPI_IRQHandler(QSPI_HandleTypeDef *hqspi)
{
  __IO uint32_t *data_reg;
  uint32_t flag = READ_REG(hqspi->Instance->SR);
  uint32_t itsource = READ_REG(hqspi->Instance->CR);

  /* QSPI Fifo Threshold interrupt occurred ----------------------------------*/
  if(((flag & QSPI_FLAG_FT) != 0U) && ((itsource & QSPI_IT_FT) != 0U))
  {
    data_reg = &hqspi->Instance->DR;    

    if(hqspi->State == HAL_QSPI_STATE_BUSY_INDIRECT_TX)
    {
      /* Transmission process */
      while(__HAL_QSPI_GET_FLAG(hqspi, QSPI_FLAG_FT) != RESET)
      {
        if (hqspi->TxXferCount > 0U)
        {
          /* Fill the FIFO until the threshold is reached */
          *((__IO uint8_t *)data_reg) = *hqspi->pTxBuffPtr;
          hqspi->pTxBuffPtr++;
          hqspi->TxXferCount--;
        }
        else
        {
          /* No more data available for the transfer */
          /* Disable the QSPI FIFO Threshold Interrupt */
          __HAL_QSPI_DISABLE_IT(hqspi, QSPI_IT_FT);
          break;
        }
      }
    }
    else if(hqspi->State == HAL_QSPI_STATE_BUSY_INDIRECT_RX)
    {
      /* Receiving Process */
      while(__HAL_QSPI_GET_FLAG(hqspi, QSPI_FLAG_FT) != RESET)
      {
        if (hqspi->RxXferCount > 0U)
        {
          /* Read the FIFO until the threshold is reached */
          *hqspi->pRxBuffPtr = *((__IO uint8_t *)data_reg);
          hqspi->pRxBuffPtr++;
          hqspi->RxXferCount--;
        }
        else
        {
          /* All data have been received for the transfer */
          /* Disable the QSPI FIFO Threshold Interrupt */
          __HAL_QSPI_DISABLE_IT(hqspi, QSPI_IT_FT);
          break;
        }
      }
    }
    else
    {
      /* Nothing to do */
    }

    /* FIFO Threshold callback */
#if (USE_HAL_QSPI_REGISTER_CALLBACKS == 1)
    hqspi->FifoThresholdCallback(hqspi);
#else
    HAL_QSPI_FifoThresholdCallback(hqspi);
#endif
  }

  /* QSPI Transfer Complete interrupt occurred -------------------------------*/
  else if(((flag & QSPI_FLAG_TC) != 0U) && ((itsource & QSPI_IT_TC) != 0U))
  {
    /* Clear interrupt */
    WRITE_REG(hqspi->Instance->FCR, QSPI_FLAG_TC);

    /* Disable the QSPI FIFO Threshold, Transfer Error and Transfer complete Interrupts */
    __HAL_QSPI_DISABLE_IT(hqspi, QSPI_IT_TC | QSPI_IT_TE | QSPI_IT_FT);

    /* Transfer complete callback */
    if(hqspi->State == HAL_QSPI_STATE_BUSY_INDIRECT_TX)
    {
      if ((hqspi->Instance->CR & QUADSPI_CR_DMAEN) != 0U)
      {
        /* Disable using MDMA by clearing DMAEN, note that DMAEN bit is "reserved"
           but no impact on H7 HW and it minimize the cost in the footprint */
        CLEAR_BIT(hqspi->Instance->CR, QUADSPI_CR_DMAEN);

        /* Disable the MDMA channel */
        __HAL_MDMA_DISABLE(hqspi->hmdma);
      }


      /* Change state of QSPI */
      hqspi->State = HAL_QSPI_STATE_READY;

      /* TX Complete callback */
#if (USE_HAL_QSPI_REGISTER_CALLBACKS == 1)
      hqspi->TxCpltCallback(hqspi);
#else
      HAL_QSPI_TxCpltCallback(hqspi);
#endif
    }
    else if(hqspi->State == HAL_QSPI_STATE_BUSY_INDIRECT_RX)
    {
      if ((hqspi->Instance->CR & QUADSPI_CR_DMAEN) != 0U)
      {
        /* Disable using MDMA by clearing DMAEN, note that DMAEN bit is "reserved"
           but no impact on H7 HW and it minimize the cost in the footprint */
        CLEAR_BIT(hqspi->Instance->CR, QUADSPI_CR_DMAEN);

        /* Disable the MDMA channel */
        __HAL_MDMA_DISABLE(hqspi->hmdma);
      }
      else
      {
        data_reg = &hqspi->Instance->DR;
        while(READ_BIT(hqspi->Instance->SR, QUADSPI_SR_FLEVEL) != 0U)
        {
          if (hqspi->RxXferCount > 0U)
          {
            /* Read the last data received in the FIFO until it is empty */
            *hqspi->pRxBuffPtr = *((__IO uint8_t *)data_reg);
            hqspi->pRxBuffPtr++;
            hqspi->RxXferCount--;
          }
          else
          {
            /* All data have been received for the transfer */
            break;
          }
        }
      }


      /* Change state of QSPI */
      hqspi->State = HAL_QSPI_STATE_READY;

      /* RX Complete callback */
#if (USE_HAL_QSPI_REGISTER_CALLBACKS == 1)
      hqspi->RxCpltCallback(hqspi);
#else
      HAL_QSPI_RxCpltCallback(hqspi);
#endif
    }
    else if(hqspi->State == HAL_QSPI_STATE_BUSY)
    {
      /* Change state of QSPI */
      hqspi->State = HAL_QSPI_STATE_READY;

      /* Command Complete callback */
#if (USE_HAL_QSPI_REGISTER_CALLBACKS == 1)
      hqspi->CmdCpltCallback(hqspi);
#else
      HAL_QSPI_CmdCpltCallback(hqspi);
#endif
    }
    else if(hqspi->State == HAL_QSPI_STATE_ABORT)
    {
      /* Reset functional mode configuration to indirect write mode by default */
      CLEAR_BIT(hqspi->Instance->CCR, QUADSPI_CCR_FMODE);

      /* Change state of QSPI */
      hqspi->State = HAL_QSPI_STATE_READY;

      if (hqspi->ErrorCode == HAL_QSPI_ERROR_NONE)
      {
        /* Abort called by the user */

        /* Abort Complete callback */
#if (USE_HAL_QSPI_REGISTER_CALLBACKS == 1)
        hqspi->AbortCpltCallback(hqspi);
#else
        HAL_QSPI_AbortCpltCallback(hqspi);
#endif
      }
      else
      {
        /* Abort due to an error (eg :  MDMA error) */

        /* Error callback */
#if (USE_HAL_QSPI_REGISTER_CALLBACKS == 1)
        hqspi->ErrorCallback(hqspi);
#else
        HAL_QSPI_ErrorCallback(hqspi);
#endif
      }
    }
    else
    {
     /* Nothing to do */
    }
  }

  /* QSPI Status Match interrupt occurred ------------------------------------*/
  else if(((flag & QSPI_FLAG_SM) != 0U) && ((itsource & QSPI_IT_SM) != 0U))
  {
    /* Clear interrupt */
    WRITE_REG(hqspi->Instance->FCR, QSPI_FLAG_SM);

    /* Check if the automatic poll mode stop is activated */
    if(READ_BIT(hqspi->Instance->CR, QUADSPI_CR_APMS) != 0U)
    {
      /* Disable the QSPI Transfer Error and Status Match Interrupts */
      __HAL_QSPI_DISABLE_IT(hqspi, (QSPI_IT_SM | QSPI_IT_TE));

      /* Change state of QSPI */
      hqspi->State = HAL_QSPI_STATE_READY;
    }

    /* Status match callback */
#if (USE_HAL_QSPI_REGISTER_CALLBACKS == 1)
    hqspi->StatusMatchCallback(hqspi);
#else
    HAL_QSPI_StatusMatchCallback(hqspi);
#endif
  }

  /* QSPI Transfer Error interrupt occurred ----------------------------------*/
  else if(((flag & QSPI_FLAG_TE) != 0U) && ((itsource & QSPI_IT_TE) != 0U))
  {
    /* Clear interrupt */
    WRITE_REG(hqspi->Instance->FCR, QSPI_FLAG_TE);

    /* Disable all the QSPI Interrupts */
    __HAL_QSPI_DISABLE_IT(hqspi, QSPI_IT_SM | QSPI_IT_TC | QSPI_IT_TE | QSPI_IT_FT);

    /* Set error code */
    hqspi->ErrorCode |= HAL_QSPI_ERROR_TRANSFER;

    if ((hqspi->Instance->CR & QUADSPI_CR_DMAEN) != 0U)
    {
      /* Disable using MDMA by clearing DMAEN, note that DMAEN bit is "reserved"
         but no impact on H7 HW and it minimize the cost in the footprint */
      CLEAR_BIT(hqspi->Instance->CR, QUADSPI_CR_DMAEN);

      /* Disable the MDMA channel */
      hqspi->hmdma->XferAbortCallback = QSPI_DMAAbortCplt;
      if (HAL_MDMA_Abort_IT(hqspi->hmdma) != HAL_OK)
      {
        /* Set error code to DMA */
        hqspi->ErrorCode |= HAL_QSPI_ERROR_DMA;

        /* Change state of QSPI */
        hqspi->State = HAL_QSPI_STATE_READY;

        /* Error callback */
#if (USE_HAL_QSPI_REGISTER_CALLBACKS == 1)
        hqspi->ErrorCallback(hqspi);
#else
        HAL_QSPI_ErrorCallback(hqspi);
#endif
      }
    }
    else
    {
      /* Change state of QSPI */
      hqspi->State = HAL_QSPI_STATE_READY;

      /* Error callback */
#if (USE_HAL_QSPI_REGISTER_CALLBACKS == 1)
      hqspi->ErrorCallback(hqspi);
#else
      HAL_QSPI_ErrorCallback(hqspi);
#endif
    }
  }

  /* QSPI Timeout interrupt occurred -----------------------------------------*/
  else if(((flag & QSPI_FLAG_TO) != 0U) && ((itsource & QSPI_IT_TO) != 0U))
  {
    /* Clear interrupt */
    WRITE_REG(hqspi->Instance->FCR, QSPI_FLAG_TO);

    /* Timeout callback */
#if (USE_HAL_QSPI_REGISTER_CALLBACKS == 1)
    hqspi->TimeOutCallback(hqspi);
#else
    HAL_QSPI_TimeOutCallback(hqspi);
#endif
  }

   else
  {
   /* Nothing to do */
  }
}

Sample Shifting: STM32接收数据延迟半周期采样,即由于时钟沿从STM32管脚到QSPI设备管脚的延时+QSPI设备接收到时钟沿后的数据输出延时+QSPI设备数据传输到STM32管脚延时较大时,可以选择STM32延迟半时钟周期采样接收数据。只是内部延迟半时钟周期采样,并不和半周期后的信号沿对应,所以最后一次采样不需要增加时钟边沿输出。

Flash Size: 设置要访问的QSPI设备可访问空间大小,针对字节单位,以2的N次方的表达方式设置,取值0~31, 设置为N时实际计算取值为N+1, 所以取值为0时,对应2字节; 取值为9时,对应1K字节,取值为19时,对应1M字节,取值为22时,对应8M字节。需要注意如果连接了2片QSPI设备,这里的Flash Size设置要对应总的字节地址数量,即单片QSPI设备取值设置时的2倍,如2片8M Bytes的QSPI设备,取值设置成23:
在这里插入图片描述

Chip Select High Time: 因为QSPI接口的片选是硬件控制,和SPI接口不同(可以设置片选为软件控制并指定任意GPIO为片选输出管脚),管脚为特定管脚且时序由芯片内部QSPI模块控制,在连续发送两个命令时,每个命令发送都会有片选拉低和拉高的过程,而在两个命令发送之间的片选高电平延时默认为1个时钟周期,但如果QSPI设备要求有更多的延时,则可以根据需要设置为更多的时钟周期延时。
在这里插入图片描述
在这里插入图片描述
Clock Mode:设置QSPI接口空闲时(片选为高电平)时的时钟输出电平,有Low和High两个选项,实际上是指定片选有效后第一个时钟上升沿之前的时序特性,QSPI设备固定为时钟上升沿采样数据电平。设置Clock Mode为Low,则在片选后的第一个时钟上升沿之前,不发送一个下降沿,效果对应SPI接口配置时的Mode 0; 设置Clock Mode为High,则在片选后的第一个时钟上升沿之前,发送一个下降沿,效果对应SPI接口配置时的Mode 3:
在这里插入图片描述
在这里插入图片描述
Dual Flash: 在配置为单路QSPI接口模式(Bank1 或 Bank2)时,Dual Flash选项固定为Disable。在配置为双路(Dual Bank)QSPI接口模式时,Dual Flash选项可控制两路QSPI接口工作在独立模式还是联合模式,Dual Flash选型配置为Enable时为联合模式,两路QSPI接口同时发送和同时接收进行操作。Dual Flash选型配置为Disable时为独立模式,可以控制单路QSPI接口工作,此时另外一路QSPI接口不工作,并可以切换,因为QSPI的访问要在IO管脚上发送命令和地址,所以不工作的一路QSPI的IO管脚不会发出命令和地址,而片选和时钟仍然可以共用。在代码控制阶段通过库函数里相应的变量(FlashID)进行选择通道:
在这里插入图片描述
在这里插入图片描述
FlashID的取值:
在这里插入图片描述

另外,如果QSPI设备支持DDR模式(时钟上升沿和下降沿都采样数据),则在库函数实施阶段可以进一步提高数据吞吐率。这里VTI7064并不支持DDR模式。

STM32 QSPI操作模式

STM32 QPSI控制器有三种操作模式:

  1. 第一种模式为间接模式,通过读写STM32内部控制寄存器,触发QSPI硬件模块对外部QSPI设备进行操作,为常规操作模式,这里的范例实现采用间接模式。

  2. 第二种模式为状态标志自动轮询模式,可以设定周期性的轮询某个状态标志,当状态变化则发出中断通知,此操作由硬件模块执行,不占用MCU时间。这里的范例不采用此模式。

  3. 第三种为内存映射模式,即将外部QSPI设备当做内部FLASH使用,外部QSPI设备可以存放代码,STM32可以进行读取。这里的范例不采用此模式。

STM32 工程配置

这里以STM32H743VIT6为范例芯片,连接两片VTI7064 SRAM, 以STM32CUBEIDE为开发环境,实现STM32 QSPI对SRAM的访问。

首先建立基本工程并配置时钟:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
本范例采用USB虚拟串口作为通讯端口,所以配置USB端口:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
USB的时钟部分配置,注意使能QSPI后这里也设置时钟为240MHz:
在这里插入图片描述
USB端口配置为虚拟串口,采用默认配置即可:
在这里插入图片描述
在这里插入图片描述
然后进行QSPI接口的配置,首先配置为Bank1单独模式(后面会描述双Bank的配置和使用):
在这里插入图片描述

接口总线要跑得快,需要良好的PCB设计,这里范例先将QSPI的访问速度降下来,Clock Prescaler设置为3,实际上就是4分频,时钟部分设置QSPI时钟为240MHz,因此最后产生的QSPI运行时钟为60MHz。Fifo Threshold设置大于0的值,随便设置,前面已对QSPI的Fifo Threshold及各部分做了介绍。这里因为是独立的QSPI接口模式,所以直接在Flash ID选项处选择采用Bank1或是Bank2。Dual Flash选项则为不可选的Disabled。

另外QSPI接口也可以弱化为单线或双线模式,既然采用了QSPI总线,必然是对访问速度有要求,所以直接用4线模式:
在这里插入图片描述
实际上,配置为4线访问模式后,在代码里还可以实时切换为单线和双线协议访问模式。上面的设置更体现GPIO物理管脚的占用量。

范例里会实现Block, Interrupt, DMA三种访问,所以打开DMA开关,但是不需要做更复杂的DMA转发,所以采用默认单字节传送配置即可(Buffer Transfer Length和前面设置的Fifo Threshold一致):
在这里插入图片描述
使能中断:
在这里插入图片描述
可以看到相应Bank所占用的管脚,将管脚信号速度级别调为最高:
在这里插入图片描述
检查中断相关代码的自动生成已点上:
在这里插入图片描述
保存并生成初始工程代码:
在这里插入图片描述

STM32 工程代码

这里范例设计的逻辑为通过USB虚拟串口进行控制:

发送0x01为初始化VTI7064;
发送0x02为读VTI7064 ID, 验证接口访问是否正常
发送0x03为发送SPI指令控制VTI7064进入Quad SPI模式
发送0x04为发送Quad SPI指令控制VTI7064退出Quad SPI模式
发送0x05 + 一串十六进制数据,STM32将这些数据以Block写模式写入VTI7064
发送0x06, STM32以Block读模式从VTI7064读出数据
发送0x07 + 一串十六进制数据,STM32将这些数据以Interrupt写模式写入VTI7064
发送0x08, STM32以Interrupt读模式从VTI7064读出数据
发送0x09 + 一串十六进制数据,STM32将这些数据以DMA写模式写入VTI7064
发送0x0A, STM32以DMA读模式从VTI7064读出数据
发送0x0B, 测试发送数小于Fifo Threshold接收到的中断数量
发送0x0C, 测试发送数大于Fifo Threshold接收到的中断数量
实际上0x0B和0x0C测试都只能收到1次中断,原理前面已介绍。
发送0x55, 针对双Bank切换模式,切换选中的Bank为Bank1
发送0xAA, 针对双Bank切换模式,切换选中的Bank为Bank2

范例逻辑做了简化,无论从串口收到多少数据,都是以1024个字节进行写读操作,可自行调整实时写读数量。
USB虚拟串口的介绍可参考: STM32 USB VCOM和HID的区别,配置及Echo功能实现(HAL)

USB虚拟串口接收部分的代码:
在这里插入图片描述

static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
  /* USER CODE BEGIN 6 */

  extern uint8_t cmd;
  extern uint8_t DB[1024];

  if(Buf[0]==0x01) //to initialize VTI7064
  {
	  cmd = 0x01;
  }
  else if(Buf[0]==0x02) //to read id of VTI7064
  {
	  cmd = 0x02;
  }
  else if(Buf[0]==0x03) //to enter Quad mode of VTI7064
  {
	  cmd = 0x03;
  }
  else if(Buf[0]==0x04) //to leav Quad mode of VTI7064
  {
	  cmd = 0x04;
  }
  else if(Buf[0]==0x05) //to write data to VTI7064 in block mode
  {
	  if(((*Len-1)<=sizeof(DB))&&((*Len-1)>0))
	  {
		  memset(DB, 0, sizeof(DB));

		  for(uint32_t i=0; i<((*Len)-1); i++)
		  {
			  DB[i] = Buf[i+1];
		  }
		  cmd = 0x05;
	  }

  }
  else if(Buf[0]==0x06) //to read data from VTI7064 in block mode
  {
	  cmd = 0x06;
  }
  else if(Buf[0]==0x07) //to write data to VTI7064 in INT mode
  {
	  if(((*Len-1)<=sizeof(DB))&&((*Len-1)>0))
	  {
		  memset(DB, 0, sizeof(DB));

		  for(uint32_t i=0; i<((*Len)-1); i++)
		  {
			  DB[i] = Buf[i+1];
		  }
		  cmd = 0x07;
	  }

  }
  else if(Buf[0]==0x08) //to read data from VTI7064 in INT mode
  {
	  cmd = 0x08;
  }
  else if(Buf[0]==0x09) //to write data to VTI7064 in DMA mode
  {
	  if(((*Len-1)<=sizeof(DB))&&((*Len-1)>0))
	  {
		  memset(DB, 0, sizeof(DB));

		  for(uint32_t i=0; i<((*Len)-1); i++)
		  {
			  DB[i] = Buf[i+1];
		  }
		  cmd = 0x09;
	  }

  }
  else if(Buf[0]==0x0a) //to read data from VTI7064 in DMA mode
  {
	  cmd = 0x0a;
  }
  else if(Buf[0]==0x0b) //to test threshold INT with data sending < threshold
  {
	  cmd = 0x0b;
  }
  else if(Buf[0]==0x0c) //to test threshold INT with data sending > threshold
  {
	  cmd = 0x0c;
  }
  else if(Buf[0]==0x55) //to switch to use bank1 interfaced QSPI device for dual-bank switch mode (dual flash mode disable)
  {
	  cmd = 0x55;
  }
  else if(Buf[0]==0xaa) //to switch to use bank2 interfaced QSPI device for dual-bank switch mode (dual flash mode disable)
  {
	  cmd = 0xaa;
  }
  else;

  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
  USBD_CDC_ReceivePacket(&hUsbDeviceFS);
  return (USBD_OK);
  /* USER CODE END 6 */
}

main.c主程序代码:

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
//Written by Pegasus Yu in 2023
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usb_device.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

QSPI_HandleTypeDef hqspi;
MDMA_HandleTypeDef hmdma_quadspi_fifo_th;

/* USER CODE BEGIN PV */
/*
QSPI TX in block mode for command byte
cmd: command to be sent to device
addr: address to be sent to device
mode: operation mode set as
	mode[1:0] for command transmission mode ( 00: no command; 01: single-line transmission; 10: dual-line transmission; 11: four-line transmission )
	mode[3:2] for address transmission mode ( 00: no address; 01: single-line transmission; 10: dual-line transmission; 11: four-line transmission )
	mode[5:4] for address length ( 00:8-bit address; 01: 16-bit address; 10: 24-bit address; 11: 32-bit address )
	mode[7:6] for data transmission mode ( 00: no command; 01: single-line transmission; 10: dual-line transmission; 11: four-line transmission )
dmcycle: dummy clock cycle
*/
HAL_StatusTypeDef QSPI_Send_CMD(uint8_t cmd,uint32_t addr,uint8_t mode,uint8_t dmcycle)
{
	QSPI_CommandTypeDef Cmdhandler;

	Cmdhandler.Instruction=cmd;		//set cmd
	Cmdhandler.Address=addr;		//set address
	Cmdhandler.DummyCycles=dmcycle; //set dummy circle number

	/*set cmd transmission mode*/
	if(((mode>>0)&0x03) == 0)
	Cmdhandler.InstructionMode=QSPI_INSTRUCTION_NONE;
	else if(((mode>>0)&0x03) == 1)
	Cmdhandler.InstructionMode=QSPI_INSTRUCTION_1_LINE;
	else if(((mode>>0)&0x03) == 2)
	Cmdhandler.InstructionMode=QSPI_INSTRUCTION_2_LINES;
	else if(((mode>>0)&0x03) == 3)
	Cmdhandler.InstructionMode=QSPI_INSTRUCTION_4_LINES;

	/*set address transmission mode*/
	if(((mode>>2)&0x03) == 0)
	Cmdhandler.AddressMode=QSPI_ADDRESS_NONE;
	else if(((mode>>2)&0x03) == 1)
	Cmdhandler.AddressMode=QSPI_ADDRESS_1_LINE;
	else if(((mode>>2)&0x03) == 2)
	Cmdhandler.AddressMode=QSPI_ADDRESS_2_LINES;
	else if(((mode>>2)&0x03) == 3)
	Cmdhandler.AddressMode=QSPI_ADDRESS_4_LINES;

	/*set address length*/
	if(((mode>>4)&0x03) == 0)
	Cmdhandler.AddressSize=QSPI_ADDRESS_8_BITS;
	else if(((mode>>4)&0x03) == 1)
	Cmdhandler.AddressSize=QSPI_ADDRESS_16_BITS;
	else if(((mode>>4)&0x03) == 2)
	Cmdhandler.AddressSize=QSPI_ADDRESS_24_BITS;
	else if(((mode>>4)&0x03) == 3)
	Cmdhandler.AddressSize=QSPI_ADDRESS_32_BITS;

	/*set data transmission mode*/
	if(((mode>>6)&0x03) == 0)
	Cmdhandler.DataMode=QSPI_DATA_NONE;
	else if(((mode>>6)&0x03) == 1)
	Cmdhandler.DataMode=QSPI_DATA_1_LINE;
	else if(((mode>>6)&0x03) == 2)
	Cmdhandler.DataMode=QSPI_DATA_2_LINES;
	else if(((mode>>6)&0x03) == 3)
	Cmdhandler.DataMode=QSPI_DATA_4_LINES;

	Cmdhandler.SIOOMode=QSPI_SIOO_INST_EVERY_CMD;				/*Send instruction on every transaction*/
	Cmdhandler.AlternateByteMode=QSPI_ALTERNATE_BYTES_NONE;		/*No alternate bytes*/
	Cmdhandler.DdrMode=QSPI_DDR_MODE_DISABLE;					/*Double data rate mode disabled*/
	Cmdhandler.DdrHoldHalfCycle=QSPI_DDR_HHC_ANALOG_DELAY;      /*Delay the data output using analog delay in DDR mode*/

	return HAL_QSPI_Command(&hqspi,&Cmdhandler,5000);
}

//QSPI RX in block mode for data
//buf : buffer address for RX data
//datalen : RX data length
//return : 0, OK
//         others, error code
HAL_StatusTypeDef QSPI_Receive(uint8_t* buf,uint32_t datalen)
{
    hqspi.Instance->DLR=datalen-1;
    return HAL_QSPI_Receive(&hqspi,buf,5000);
}

//QSPI TX in block mode for data
//buf : buffer address for TX data
//datalen : TX data length
//return : 0, OK
//         others, error code
HAL_StatusTypeDef QSPI_Transmit(uint8_t* buf,uint32_t datalen)
{
    hqspi.Instance->DLR=datalen-1;
    return HAL_QSPI_Transmit(&hqspi,buf,5000);
}



/*
QSPI TX in INT mode for command byte
cmd: command to be sent to device
addr: address to be sent to device
mode: operation mode set as
	mode[1:0] for command transmission mode ( 00: no command; 01: single-line transmission; 10: dual-line transmission; 11: four-line transmission )
	mode[3:2] for address transmission mode ( 00: no address; 01: single-line transmission; 10: dual-line transmission; 11: four-line transmission )
	mode[5:4] for address length ( 00:8-bit address; 01: 16-bit address; 10: 24-bit address; 11: 32-bit address )
	mode[7:6] for data transmission mode ( 00: no command; 01: single-line transmission; 10: dual-line transmission; 11: four-line transmission )
dmcycle: dummy clock cycle
*/
HAL_StatusTypeDef QSPI_Send_CMD_INT(uint8_t cmd,uint32_t addr,uint8_t mode,uint8_t dmcycle)
{
	QSPI_CommandTypeDef Cmdhandler;

	Cmdhandler.Instruction=cmd;	    //set cmd
	Cmdhandler.Address=addr;        //set address
	Cmdhandler.DummyCycles=dmcycle;	//set dummy circle number

	/*set cmd transmission mode*/
	if(((mode>>0)&0x03) == 0)
	Cmdhandler.InstructionMode=QSPI_INSTRUCTION_NONE;
	else if(((mode>>0)&0x03) == 1)
	Cmdhandler.InstructionMode=QSPI_INSTRUCTION_1_LINE;
	else if(((mode>>0)&0x03) == 2)
	Cmdhandler.InstructionMode=QSPI_INSTRUCTION_2_LINES;
	else if(((mode>>0)&0x03) == 3)
	Cmdhandler.InstructionMode=QSPI_INSTRUCTION_4_LINES;

	/*set address transmission mode*/
	if(((mode>>2)&0x03) == 0)
	Cmdhandler.AddressMode=QSPI_ADDRESS_NONE;
	else if(((mode>>2)&0x03) == 1)
	Cmdhandler.AddressMode=QSPI_ADDRESS_1_LINE;
	else if(((mode>>2)&0x03) == 2)
	Cmdhandler.AddressMode=QSPI_ADDRESS_2_LINES;
	else if(((mode>>2)&0x03) == 3)
	Cmdhandler.AddressMode=QSPI_ADDRESS_4_LINES;

	/*set address length*/
	if(((mode>>4)&0x03) == 0)
	Cmdhandler.AddressSize=QSPI_ADDRESS_8_BITS;
	else if(((mode>>4)&0x03) == 1)
	Cmdhandler.AddressSize=QSPI_ADDRESS_16_BITS;
	else if(((mode>>4)&0x03) == 2)
	Cmdhandler.AddressSize=QSPI_ADDRESS_24_BITS;
	else if(((mode>>4)&0x03) == 3)
	Cmdhandler.AddressSize=QSPI_ADDRESS_32_BITS;

	/*set data transmission mode*/
	if(((mode>>6)&0x03) == 0)
	Cmdhandler.DataMode=QSPI_DATA_NONE;
	else if(((mode>>6)&0x03) == 1)
	Cmdhandler.DataMode=QSPI_DATA_1_LINE;
	else if(((mode>>6)&0x03) == 2)
	Cmdhandler.DataMode=QSPI_DATA_2_LINES;
	else if(((mode>>6)&0x03) == 3)
	Cmdhandler.DataMode=QSPI_DATA_4_LINES;

	Cmdhandler.SIOOMode=QSPI_SIOO_INST_EVERY_CMD;				/*Send instruction on every transaction*/
	Cmdhandler.AlternateByteMode=QSPI_ALTERNATE_BYTES_NONE;		/*No alternate bytes*/
	Cmdhandler.DdrMode=QSPI_DDR_MODE_DISABLE;					/*Double data rate mode disabled*/
	Cmdhandler.DdrHoldHalfCycle=QSPI_DDR_HHC_ANALOG_DELAY;      /*Delay the data output using analog delay in DDR mode*/

	return HAL_QSPI_Command_IT(&hqspi,&Cmdhandler);
}

//QSPI RX in INT mode for data
//buf : buffer address for RX data
//datalen : RX data length
//return : 0, OK
//         others, error code
HAL_StatusTypeDef QSPI_Receive_INT(uint8_t* buf,uint32_t datalen)
{
    hqspi.Instance->DLR=datalen-1;
    return HAL_QSPI_Receive_IT(&hqspi,buf);
}

//QSPI TX in INT mode for data
//buf : buffer address for TX data
//datalen : TX data length
//return : 0, OK
//         others, error code
HAL_StatusTypeDef QSPI_Transmit_INT(uint8_t* buf,uint32_t datalen)
{
    hqspi.Instance->DLR=datalen-1;
    return HAL_QSPI_Transmit_IT(&hqspi,buf);
}

//QSPI RX in DMA mode for data
//buf : buffer address for RX data
//datalen : RX data length
//return : 0, OK
//         others, error code
HAL_StatusTypeDef QSPI_Receive_DMA(uint8_t* buf,uint32_t datalen)
{
    hqspi.Instance->DLR=datalen-1;
    return HAL_QSPI_Receive_DMA(&hqspi,buf);
}

//QSPI TX in DMA mode for data
//buf : buffer address for TX data
//datalen : TX data length
//return : 0, OK
//         others, error code
HAL_StatusTypeDef QSPI_Transmit_DMA(uint8_t* buf,uint32_t datalen)
{
    hqspi.Instance->DLR=datalen-1;
    return HAL_QSPI_Transmit_DMA(&hqspi,buf);
}



uint8_t VTI706_Quad_Status = 0;
void VTI7064_Rst_Init(void)
{
	if(VTI706_Quad_Status==0) QSPI_Send_CMD(0x66, 0x00, 0x01, 0);
}

uint64_t VTI7064_Read_ID(void)
{
	uint64_t VID = 0;
	if(VTI706_Quad_Status==0)
	{
	  QSPI_Send_CMD(0x9F, 0x00, 0x65, 0);

	  if(QSPI_Receive((uint8_t *)&VID, 8)==0) return VID;
	  else return 0;
	}
	else return 0;
}

void VTI7064_Enter_Quad(void)
{
	if(VTI706_Quad_Status==0) QSPI_Send_CMD(0x35, 0x00, 0x01, 0);
	VTI706_Quad_Status = 1;
}

void VTI7064_Exit_Quad(void)
{
	if(VTI706_Quad_Status==1) QSPI_Send_CMD(0xF5, 0x00, 0x03, 0);
	VTI706_Quad_Status = 0;
}

void VTI7064_Quad_Write(uint32_t addr, uint8_t * data, uint32_t datalen)
{
	if(VTI706_Quad_Status==1)
	{
		QSPI_Send_CMD(0x38, addr, 0xEF, 0);
		QSPI_Transmit(data, datalen);
	}
}

void VTI7064_Quad_Read(uint32_t addr, uint8_t * data, uint32_t datalen)
{
	if(VTI706_Quad_Status==1)
	{
		QSPI_Send_CMD(0xEB, addr, 0xEF, 6);
		QSPI_Receive(data, datalen);

	}
}

void VTI7064_Quad_Write_INT(uint32_t addr, uint8_t * data, uint32_t datalen)
{
	if(VTI706_Quad_Status==1)
	{
		QSPI_Send_CMD(0x38, addr, 0xEF, 0);
		QSPI_Transmit_INT(data, datalen);
	}
}

void VTI7064_Quad_Read_INT(uint32_t addr, uint8_t * data, uint32_t datalen)
{
	if(VTI706_Quad_Status==1)
	{
		QSPI_Send_CMD(0xEB, addr, 0xEF, 6);
		QSPI_Receive_INT(data, datalen);

	}
}

void VTI7064_Quad_Write_DMA(uint32_t addr, uint8_t * data, uint32_t datalen)
{
	if(VTI706_Quad_Status==1)
	{
		QSPI_Send_CMD(0x38, addr, 0xEF, 0);
		QSPI_Transmit_DMA(data, datalen);
	}
}

void VTI7064_Quad_Read_DMA(uint32_t addr, uint8_t * data, uint32_t datalen)
{
	if(VTI706_Quad_Status==1)
	{
		QSPI_Send_CMD(0xEB, addr, 0xEF, 6);
		QSPI_Receive_DMA(data, datalen);

	}
}
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_MDMA_Init(void);
static void MX_QUADSPI_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#define BufSize 1024
uint8_t cmd = 0x00;
uint8_t cmd_int = 0x00;
uint8_t DB[BufSize];

uint64_t VTI7064_ID = 0;
char * console;

QSPI_HandleTypeDef * hqspi1;
uint32_t FifoThreshold_NUM = 0;
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
	hqspi1 = &hqspi;

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */
  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */
  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USB_DEVICE_Init();
  MX_MDMA_Init();
  MX_QUADSPI_Init();
  /* USER CODE BEGIN 2 */
  memset(DB, 0, sizeof(DB));
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
   {
 	  if(cmd==0x01) //Reset and initialization in block mode
 	  {
 		  cmd = 0;

 	 	  VTI7064_Rst_Init();
 	 	  console = "\r\nInitialization Done\r\n";
 	 	  CDC_Transmit_FS(console, strlen(console));
 	  }
 	  else if(cmd==0x02) //Get ID in block mode
 	  {
 		  cmd = 0;

 		  VTI7064_ID = VTI7064_Read_ID();
 		  CDC_Transmit_FS(&VTI7064_ID, 8);
 	  }
 	  else if(cmd==0x03) //Enter Quad mode in block mode
 	  {
 		  cmd = 0;

 		  if(VTI706_Quad_Status==0)
 		  {
 			  VTI7064_Enter_Quad();

 			  console = "\r\nEnter Quad Mode\r\n";
 			  CDC_Transmit_FS(console, strlen(console));
 		  }
 	  }
 	  else if(cmd==0x04) //Exit Quad mode in block mode
 	  {
 		  cmd = 0;

 		  if(VTI706_Quad_Status==1)
 		  {
 			  VTI7064_Exit_Quad();

 			  console = "\r\nExit Quad Mode\r\n";
 			  CDC_Transmit_FS(console, strlen(console));
 		  }
 	  }
 	  else if(cmd==0x05) //to write data to VTI7064 in block mode
 	  {
 		  cmd = 0;

     		  if(VTI706_Quad_Status==1)
     		  {
     			  VTI7064_Quad_Write(0, DB, sizeof(DB));

     			  console = "\r\nWrite Operation Done\r\n";
     			  CDC_Transmit_FS(console, strlen(console));
     		  }
 	  }
 	  else if(cmd==0x06) //to read data from VTI7064 in block mode
 	  {
 		  cmd = 0;

     		  if(VTI706_Quad_Status==1)
     		  {
     			  memset(DB, 0, sizeof(DB));
     			  VTI7064_Quad_Read(0, DB, sizeof(DB));
     		  }

     		  CDC_Transmit_FS(DB, sizeof(DB));
 	  }
 	  else if(cmd==0x07) //to write data to VTI7064 in INT mode
 	  {
 		  cmd = 0;

     		  if(VTI706_Quad_Status==1)
     		  {
     			  cmd_int = 7;
     			  VTI7064_Quad_Write_INT(0, DB, sizeof(DB));
     		  }

 	  }
 	  else if(cmd==0x08) //to read data from VTI7064 in INT mode
 	  {
 		  cmd = 0;

     		  if(VTI706_Quad_Status==1)
     		  {
     			  cmd_int = 8;
     			  memset(DB, 0, sizeof(DB));
     			  VTI7064_Quad_Read_INT(0, DB, sizeof(DB));
     		  }

 	  }
 	  else if(cmd==0x09) //to write data to VTI7064 in DMA mode
 	  {
 		  cmd = 0;

     		  if(VTI706_Quad_Status==1)
     		  {
     			  cmd_int = 9;
     			  VTI7064_Quad_Write_DMA(0, DB, sizeof(DB));
     		  }

 	  }
 	  else if(cmd==0x0a) //to read data from VTI7064 in DMA mode
 	  {
 		  cmd = 0;

     		  if(VTI706_Quad_Status==1)
     		  {
     			  cmd_int = 10;
     			  memset(DB, 0, sizeof(DB));
     			  VTI7064_Quad_Read_DMA(0, DB, sizeof(DB));
     		  }

 	  }
 	  else if(cmd==0x0b) //to test threshold INT with data sending < threshold
 	  {
 		  cmd = 0;

 		  cmd_int = 11;
 		  FifoThreshold_NUM = 0;
 		  if(VTI706_Quad_Status==1)
 		  {
 			  VTI7064_Quad_Write_INT(0, DB, 4); //Sending 4-byte data
 		  }

 	  }
 	  else if(cmd==0x0c) //to test threshold INT with data sending > threshold
 	  {
 		  cmd = 0;

 		  cmd_int = 12;
 		  FifoThreshold_NUM = 0;
 		  if(VTI706_Quad_Status==1)
 		  {
 			  VTI7064_Quad_Write_INT(0, DB, sizeof(DB)); //Sending full data exceeding Fifo size
 		  }
 	  }
 	  else if(cmd==0x55) //to switch to use bank1 interfaced QSPI device for dual-bank switch mode (dual flash mode disable)
 	  {
 		  cmd = 0;
 		  HAL_QSPI_SetFlashID(&hqspi, QSPI_FLASH_ID_1);
		  console = "\r\nBank 1 was selected!\r\n";
		  CDC_Transmit_FS(console, strlen(console));
 	  }
 	  else if(cmd==0xaa) //to switch to use bank2 interfaced QSPI device for dual-bank switch mode (dual flash mode disable)
 	  {
 		  cmd = 0;
 		  HAL_QSPI_SetFlashID(&hqspi, QSPI_FLASH_ID_2);
		  console = "\r\nBank 2 was selected!\r\n";
		  CDC_Transmit_FS(console, strlen(console));
 	  }
 	  else;
   }

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Supply configuration update enable
  */
  HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);

  /** Configure the main internal regulator output voltage
  */
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}

  __HAL_RCC_SYSCFG_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0);

  while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI48|RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSI48State = RCC_HSI48_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 4;
  RCC_OscInitStruct.PLL.PLLN = 480;
  RCC_OscInitStruct.PLL.PLLP = 2;
  RCC_OscInitStruct.PLL.PLLQ = 2;
  RCC_OscInitStruct.PLL.PLLR = 2;
  RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_1;
  RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
  RCC_OscInitStruct.PLL.PLLFRACN = 0;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
                              |RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
  RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief QUADSPI Initialization Function
  * @param None
  * @retval None
  */
static 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 */
  /* QUADSPI parameter configuration*/
  hqspi.Instance = QUADSPI;
  hqspi.Init.ClockPrescaler = 3;
  hqspi.Init.FifoThreshold = 32;
  hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE;
  hqspi.Init.FlashSize = 22;
  hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_2_CYCLE;
  hqspi.Init.ClockMode = QSPI_CLOCK_MODE_0;
  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 */

}

/**
  * Enable MDMA controller clock
  */
static void MX_MDMA_Init(void)
{

  /* MDMA controller clock enable */
  __HAL_RCC_MDMA_CLK_ENABLE();
  /* Local variables */

  /* MDMA interrupt initialization */
  /* MDMA_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(MDMA_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(MDMA_IRQn);

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOE_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();

}

/* USER CODE BEGIN 4 */
void HAL_QSPI_FifoThresholdCallback(QSPI_HandleTypeDef *hqspi)
{
	if(hqspi==hqspi1)
	{
		FifoThreshold_NUM += 1; //Count int times
	}

}
void HAL_QSPI_CmdCpltCallback(QSPI_HandleTypeDef *hqspi)
{

	 //unused

}

void HAL_QSPI_RxCpltCallback(QSPI_HandleTypeDef *hqspi)
{
	if(hqspi==hqspi1)
	{
		if((cmd_int==8)||(cmd_int==10))
		{
			cmd_int = 0;
			CDC_Transmit_FS(DB, sizeof(DB));
		}
	}
}
void HAL_QSPI_TxCpltCallback(QSPI_HandleTypeDef *hqspi)
{
	if(hqspi==hqspi1)
	{
		if((cmd_int==7)||(cmd_int==9))
		{
			cmd_int =0;
			console = "\r\nWrite Operation Done\r\n";
			CDC_Transmit_FS(console, strlen(console));
		}

		if((cmd_int==11)||(cmd_int==12))
		{
			cmd_int =0;
			CDC_Transmit_FS(&FifoThreshold_NUM, 4);
		}
	}
}
/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */


代码测试效果

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

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

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

STM32 QSPI双区切换访问模式

设置为双区切换访问模式,升级代码生成即可。
在这里插入图片描述
在这里插入图片描述
在代码里可以进行要访问的Bank的切换
在这里插入图片描述

STM32 QSPI双区合并访问模式

设置为双区合并访问模式,,升级代码生成即可。逻辑代码实现不变化,但每次操作写或读时,会把STM32内部偶地址对应Bank1, 基地址对应Bank2进行数据写读。也即STM32内存的一个字节数据会写入到一个Bank,而不是分成两个4位写到两个Bank,读也是一样。在四线QSPI模式,写一个字节到一个Bank,实际上会有两次写过程(每次写4位),读也是一样。
在这里插入图片描述
在这里插入图片描述

STM32 QSPI片选节约

STM32 QSPI可以选择片选节约方式,如果到外部存储设备的片选一直外拉有效,可以选择Disable。也可以2个Bank的片选共用,或者片选各用。
在这里插入图片描述

范例代码下载

STM32 QSPI总线读写64 Mbit容量SRAM VTI7064范例

–End

  • 17
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
根据引用和引用的内容,可以总结出STM32h750 QSPI W25Q64驱动的一些关键信息。 首先,W25Q64是一种SPI NOR Flash芯片,它被连接到STM32h750的QSPI(Quad SPI)接口上。W25Q64的引脚连接为PB2、PB6、PF6、PF7、PF8和PF9。 在设置QSPI时,一些关键的配置参数需要注意。首先是时钟预分频器(clock prescaler),根据W25Q256的最高时钟频率为104MHz,因此需要将分频设置为2。其次是闪存大小(FLASH SIZE),W25Q64的大小为8MB,所以需要将设置为2的(22-1)次方。时钟模式(Clock Mode)应设置为Low,表示CLK空闲时为低电平。芯片选择(Chip Select)需要设置为High Time为5,以确保高电平持续时间大于50ns。 另外,为了保证正常的工作,所有的QSPI引脚都应该设置为very high,而NCS脚(PB6)必须设置为PULL-UP。关于为什么要设置为PULL-UP,具体原因在引用中没有提及。 最后,需要注意W25Q64与W25Q256之间的一些区别。首先是地址位数,W25Q64只支持24位地址,而W25Q256支持24位和32位地址。其次是读写状态寄存器的不同,W25Q64的读状态寄存器为05h和35h,而W25Q256的为05h、35h和15h。写状态寄存器也有所不同,W25Q64的为01h,而W25Q256的为01h、31h和11h。 综上所述,STM32h750的QSPI可以通过相应的配置来驱动W25Q64芯片。需要注意的是,具体的配置参数和引脚连接可能还取决于具体的硬件设计和应用需求。<span class="em">1</span><span class="em">2</span> #### 引用[.reference_title] - *1* [STM32H750 QSPI间接模式 W25Q64](https://blog.csdn.net/smallerlang/article/details/127921384)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [STM32H753 STM32H743 STM32H750 QSPI W25Q256 下载算法](https://blog.csdn.net/c101028/article/details/132073746)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PegasusYu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值