AnsweredAssumed Answered

USB Audio Device over QSPI problem

Question asked by Anna Maria Nestorov on Mar 19, 2018
Latest reply on Mar 21, 2018 by T J

Hi all,

I am trying to implement an Audio USB Device on my STM32F446 micro starting from the following project:

https://github.com/pellepl/verisure1512/tree/master/ext-libs/STM32Cube_FW_F7_V1.2.0/Projects/STM32746G-Discovery/Applications/USB_Device/AUDIO_Standalone

I made some chamges to that project in order to send audio data from the STM32F446 to an FPGA through QSPI.
To do this, I have changed the function HAL_SAI_Transmit_DMA inside stm32fxx_hal_sai.c as follows:

HAL_StatusTypeDef HAL_SAI_Transmit_DMA(SAI_HandleTypeDef *hsai, uint8_t *pData, uint16_t Size){

   uint32_t *tmp;
   uint16_t chunk_dim = 32;

   if ((pData == NULL) || (Size == 0)) {
      return HAL_ERROR;
   }

   int half_buff_size = Size;

   uint16_t m;
   uint16_t N_of_trans = Size / chunk_dim;

   for (m = 0; m < N_of_trans; ++m) {
      //wait until the almost_full of the FPGA's fifo is equal to 0
      while (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_15) == GPIO_PIN_SET);
      BSP_QSPI_Write((uint32_t*) &pData[chunk_dim * m], 0x40, chunk_dim + 6);
   }

   if(hsai->State == HAL_SAI_STATE_READY)
   {
      hsai->pBuffPtr = pData;
      hsai->XferSize = Size;
      hsai->XferCount = Size;

      /* Process Locked */
      __HAL_LOCK(hsai);

      hsai->State = HAL_SAI_STATE_BUSY_TX;

      /* Set the SAI Tx DMA Half transfer complete callback */
      hsai->hdmatx->XferHalfCpltCallback = SAI_DMATxHalfCplt;

      /* Set the SAI TxDMA transfer complete callback */
      hsai->hdmatx->XferCpltCallback = SAI_DMATxCplt;

      /* Set the DMA error callback */
      hsai->hdmatx->XferErrorCallback = SAI_DMAError;

      /* Enable the Tx DMA Stream */
      tmp = (uint32_t*)&pData;
      HAL_DMA_Start_IT(hsai->hdmatx, *(uint32_t*)tmp, (uint32_t)&hsai->Instance->DR, hsai->XferSize);

      /* Check if the SAI is already enabled */
      if((hsai->Instance->CR1 & SAI_xCR1_SAIEN) != SAI_xCR1_SAIEN)
      {
            /* Enable SAI peripheral */
            __HAL_SAI_ENABLE(hsai);
       }

      /* Enable the interrupts for error handling */
      __HAL_SAI_ENABLE_IT(hsai, SAI_InterruptFlag(hsai, SAI_MODE_DMA));

      /* Enable SAI Tx DMA Request */
      hsai->Instance->CR1 |= SAI_xCR1_DMAEN;

      /* Process Unlocked */
      __HAL_UNLOCK(hsai);
      return HAL_OK;
   }

   else
   {
     return HAL_BUSY;

   }

}

The BSP_QSPI_Write function writes data to the QSPI buffer as follows:

uint8_t BSP_QSPI_Write(uint32_t* pData, uint32_t WriteAddr, uint32_t Size){

   QSPI_CommandTypeDef s_command;

   /* Initialize the program command */
   s_command.Instruction = 0x00; // CCR: INSTRUCTION
   s_command.InstructionMode = QSPI_INSTRUCTION_NONE; // CCR: IMODE
   s_command.AddressMode = QSPI_ADDRESS_4_LINES; // CCR: ADMODE
   s_command.AddressSize = QSPI_ADDRESS_8_BITS; // CCR: ADSIZE
   s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; // CCR: ABMODE
   s_command.AlternateBytesSize = QSPI_ALTERNATE_BYTES_8_BITS; // CCR: ABSIZE
   s_command.DummyCycles = 0;//1; // CCR: DCYC
   s_command.DataMode = QSPI_DATA_4_LINES; // CCR: DMODE
   s_command.SIOOMode = QSPI_SIOO_INST_ONLY_FIRST_CMD; // CCR: SIOO
   s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;//QSPI_DDR_HHC_HALF_CLK_DELAY; // CCR: DHHC
   s_command.DdrMode = QSPI_DDR_MODE_ENABLE; // CCR: DDRM

   s_command.Address = WriteAddr; // AR
   s_command.NbData = Size; // DLR

   /* Configure the command */
   if (HAL_QSPI_Command(&QSPIHandle, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
   {
      return QSPI_ERROR;
   }

   else if (Size==38) // DATA PACKETS
   {
      if (HAL_QSPI_Transmit_8(&QSPIHandle, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
      {
         BSP_QSPI_DeInit();
         BSP_QSPI_Init();
         return QSPI_ERROR;
      }
   }
   return QSPI_OK;
}

 

Where:

HAL_StatusTypeDef HAL_QSPI_Transmit_8(QSPI_HandleTypeDef *hqspi,uint32_t *pData, uint32_t Timeout) {

   HAL_StatusTypeDef status = HAL_OK;
   __IO uint32_t *data_reg = &hqspi->Instance->DR;

   /* Process locked */
   __HAL_LOCK(hqspi);

   if (hqspi->State == HAL_QSPI_STATE_READY) {
      if (pData != NULL) {
         hqspi->ErrorCode = HAL_QSPI_ERROR_NONE;

         /* Update state */
         hqspi->State = HAL_QSPI_STATE_BUSY_INDIRECT_TX;

         /* Configure counters and size of the handle */
         hqspi->pTxBuffPtr = pData;

         /* Configure QSPI: CCR register with functional as indirect write */
         MODIFY_REG(hqspi->Instance->CCR, QUADSPI_CCR_FMODE,
         QSPI_FUNCTIONAL_MODE_INDIRECT_WRITE);

         *(__IO uint16_t *) data_reg = 0x0000;
         *(__IO uint32_t *) data_reg = *hqspi->pTxBuffPtr;
         hqspi->pTxBuffPtr++;
         *(__IO uint32_t *) data_reg = *hqspi->pTxBuffPtr;
         hqspi->pTxBuffPtr++;
         *(__IO uint32_t *) data_reg = *hqspi->pTxBuffPtr;
         hqspi->pTxBuffPtr++;
         *(__IO uint32_t *) data_reg = *hqspi->pTxBuffPtr;
         hqspi->pTxBuffPtr++;
         *(__IO uint32_t *) data_reg = *hqspi->pTxBuffPtr;
         hqspi->pTxBuffPtr++;
         *(__IO uint32_t *) data_reg = *hqspi->pTxBuffPtr;
         hqspi->pTxBuffPtr++;
         *(__IO uint32_t *) data_reg = *hqspi->pTxBuffPtr;
         hqspi->pTxBuffPtr++;
         *(__IO uint32_t *) data_reg = *hqspi->pTxBuffPtr;
         *(__IO uint32_t *) data_reg = 0x00000000;

         if (status == HAL_OK) {
            /* Wait until TC flag is set to go back in idle state */
            if (QSPI_WaitFlagStateUntilTimeout(hqspi, QSPI_FLAG_TC, SET, Timeout) != HAL_OK) {
               status = HAL_TIMEOUT;
            } else {
               /* Clear Transfer Complete bit */
               __HAL_QSPI_CLEAR_FLAG(hqspi, QSPI_FLAG_TC);

               /* Clear Busy bit */
               status = HAL_QSPI_Abort(hqspi);
            }
         }

         /* Update QSPI state */
         hqspi->State = HAL_QSPI_STATE_READY;

         } else {
            status = HAL_ERROR;
         }

     } else {
     status = HAL_BUSY;
   }

   /* Process unlocked */
   __HAL_UNLOCK(hqspi);

   return status;
}

 

 

The SAI PLL configuration is the following (48k as input sampling rate):

rcc_ex_clk_init_struct.PLLSAI.PLLSAIM = 10;
rcc_ex_clk_init_struct.PLLSAI.PLLSAIN = 246;
rcc_ex_clk_init_struct.PLLSAI.PLLSAIQ = 2;
rcc_ex_clk_init_struct.PLLSAIDivQ = 2;

Setting the AUDIO_OUT_PACKET_NUM parameter ( usbd_audio.h) equal to 40, I would expect an input buffer equal to 15360 bytes. Therefore, as soon as the read pointer of the buffer is equal to 7680, (USBD_AUDIO_Sync function in usbd_audio.c) the HAL_SAI_Transmit_DMA function is triggered and the STM32 starts to send the buffer to the FPGA through QSPI.

Unfortunately, even if my PC see the STM32 as Audio Device and I can hear some noise from the speakers, I am not able to listen music correctly .

Can anybody help me in understanding where is my mistake?

 

Thanks

Outcomes