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

news2024/9/24 15:18:10

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

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

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

相关文章

谷粒商城实战笔记-踩坑-跨域问题

一&#xff0c;When allowCredentials is true, allowedOrigins cannot contain the special value “*” since that cannot be set on the “Access-Control-Allow-Origin” response header. To allow credentials to a set of origins, list them explicitly or consider u…

shell-awk文本处理工具

1、awk概述 AWK 是一种处理文本文件的语言&#xff0c;是一个强大的文本分析工具。 它是专门为文本处理设计的编程语言&#xff0c;也是行处理软件&#xff0c;通常用于扫描、过滤、统计汇总工作 数据可以来自标准输入也可以是管道或文件 在 linux 上常用的是 gawk,awk …

PyTorch+AlexNet代码实训

参考文章&#xff1a;https://blog.csdn.net/red_stone1/article/details/122974771 数据集&#xff1a; 打标签&#xff1a; import os# os.path.join: 每个参数都是一个路径段&#xff0c;将它们连接起来形成有效的路径名。 train_txt_path os.path.join("data"…

Bazaar v1.4.3 任意文件读取漏洞复现(CVE-2024-40348)

0x01 产品简介 Bazarr是Sonarr和Radarr的配套应用程序&#xff0c;可根据您的要求管理和下载字幕。 0x02 漏洞概述 Bazarr存在任意文件读取漏洞&#xff0c;该漏洞是由于Bazaar v1.4.3的组件/api/swaggerui/static中存在一个问题&#xff0c;允许未经身份验证的攻击者可利用…

硅纪元AI应用推荐 | 豆包整容成了浏览器,让你的电脑秒变AI PC

“硅纪元AI应用推荐”栏目&#xff0c;为您精选最新、最实用的人工智能应用&#xff0c;无论您是AI发烧友还是新手&#xff0c;都能在这里找到提升生活和工作的利器。与我们一起探索AI的无限可能&#xff0c;开启智慧新时代&#xff01; 亲爱的技术宅们、办公高手们&#xff0c…

Tomcat项目本地部署

今天来分享一下如何于本机上在不适用idea等辅助工具的前提下&#xff0c;部署多个tomcat的web项目 我这里以我最近写的SSM项目哈米音乐为例&#xff0c;简单介绍一下项目的大致组成&#xff1a; 首先&#xff0c;项目分为4个模块&#xff0c;如下图所示&#xff1a; 其中&…

SQL 语句中的字符串有单引号导致报错的解决

1.问题 SQL 语句执行对象中&#xff0c;本内容的字符串内含有单引号导致查询或插入数据库报错&#xff0c; 例如 str 关键字 AND 附近有语法错误 2.解决 字符串中的 ’ → 替换 ”&#xff0c;则查询语句成功&#xff0c;故程式中要备注替换 单引号。

无法解析插件 org.apache.maven.plugins:maven-war-plugin:3.2.3(已解决)

文章目录 1、问题出现的背景2、解决方法 1、问题出现的背景 最开始我想把springboot项目转为javaweb项目&#xff0c;然后我点击下面这个插件 就转为javaweb项目了&#xff0c;但是我后悔了&#xff0c;想要还原成springboot项目&#xff0c;点开项目结构关于web的都移除了&am…

运放-增益带宽积-datasheet参数

在运放开环增益频率曲线中&#xff0c;在一定频率范围内&#xff0c;运放的开环增益与对应的频率乘积为常数&#xff1a;增益带宽积&#xff08;Gain Bandwidth Product&#xff0c; GBP 或者 GBW&#xff09;&#xff0c;即开环增益*频率增益带宽积。 这里有一个误区&#xf…

CompletableFuture异步线程不执行,卡死问题

1、生产上突然发现大量业务数据没执行&#xff0c;通过日志分析有段代码没执行。 2、分析原因可能是异步线程没执行导致&#xff0c;直接上代码场景 3、异步线程调用远程外部接口 超时或多次异常&#xff0c;导致服务无法再开启异步线程&#xff0c;同时代码中其他用到异步线程…

人人可学的AI与高科技普及视频课,零基础,通俗易懂,深入浅出

课程内容&#xff1a; 1 第0课:开课词&#xff0c;欢迎词 ev.mp4 2 第1课:我们为什么要学习Al ev.mp4 3 第2课:AI算法模型的基本概念MOVev,mp4 4 第3课:什么是生成性Al ev,mp4 5 第4课:人工智能的三驾马车 ev.mp4 6 加餐附加课1-谷歌双子座Gemini ev,mp4 7 第5课:关于Al…

为什么idea建议使用“+”拼接字符串

今天在敲代码的时候&#xff0c;无意间看到这样一个提示&#xff1a; 英文不太好&#xff0c;先问问ChatGPT&#xff0c;这个啥意思&#xff1f; IDEA 提示你&#xff0c;可以将代码中的 StringBuilder 替换为简单的字符串连接方式。 提示信息中说明了使用 StringBuilder 进行…

分布式相关理论详解

目录 1.绪论 2.什么是分布式系统&#xff0c;和集群的区别 3.CAP理论 3.1 什么是CAP理论 3.2 一致性 3.2.1 计算机的一致性说明 1.事务中的一致性 2.并发场景下的一致性 3.分布式场景下的一致性 3.2.2 一致性分类 3.2.3 强一致性 1.线性一致性 a) 定义 a) Raft算法…

数据危机!4大硬盘数据恢复工具,教你如何正确挽回珍贵记忆!

在这个数字化的时代&#xff0c;硬盘里的数据对我们来说简直太重要了。但糟糕的是&#xff0c;数据丢失这种事时不时就会发生&#xff0c;可能是因为不小心删了&#xff0c;硬盘坏了&#xff0c;或者中了病毒。遇到这种情况&#xff0c;很多人可能就慌了&#xff0c;不知道怎么…

鸿蒙(HarmonyOS)下拉选择控件

一、操作环境 操作系统: Windows 11 专业版、IDE:DevEco Studio 3.1.1 Release、SDK:HarmonyOS 3.1.0&#xff08;API 9&#xff09; 二、效果图 三、代码 SelectPVComponent.ets Component export default struct SelectPVComponent {Link selection: SelectOption[]priva…

模拟信号介绍

定义&#xff1a; 模拟信号是指用连续变化的物理量表示的信息&#xff0c;其信号的幅度、频率或相位随时间作连续变化&#xff0c;或在一段连续的时间间隔内&#xff0c;其代表信息的特征量可以在任意瞬间呈现为任意数值的信号。我们通常又把模拟信号称为连续信号&#xff0c;它…

挑战房市预测领头羊:KNN vs. 决策树 vs. 线性回归

挑战房市预测领头羊&#xff08;KNN&#xff0c;决策树&#xff0c;线性回归&#xff09; 1. 介绍1.1 K最近邻&#xff08;KNN&#xff09;&#xff1a;与邻居的友谊1.1.1 KNN的基础1.1.2 KNN的运作机制1.1.3 KNN的优缺点 1.2 决策树&#xff1a;解码房价的逻辑树1.2.1 决策树的…

AttributeError: ‘list‘ object has no attribute ‘text‘

AttributeError: ‘list‘ object has no attribute ‘text‘ 目录 AttributeError: ‘list‘ object has no attribute ‘text‘ 【常见模块错误】 【解决方案】 示例代码 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英…

前端三大主流框架Vue React Angular有何不同?

前端主流框架&#xff0c;Vue React Angular&#xff0c;大家可能都经常在使用&#xff0c;Vue React&#xff0c;国内用的较多&#xff0c;Angualr相对用的少一点。但是大家有思考过这三大框架的不同吗&#xff1f; 一、项目的选型上 中小型项目&#xff1a;Vue2、React居多…

人工智能AI合集:Ollama部署对话语言大模型-网页访问

目录 &#x1f345;点击这里查看所有博文 随着人工智能技术的飞速发展&#xff0c;AI已经不再是遥不可及的高科技概念&#xff0c;而是逐渐融入到我们的日常生活中。从智能手机的语音助手到家庭中的智能音箱&#xff0c;再到工业自动化和医疗诊断&#xff0c;AI的应用无处不在…