AnsweredAssumed Answered

Bug: HAL_SAI_Transmit_DMA does not follow the sequence of RM0351 Rev 4 pg 1329 for the case of Slave Transmitter

Question asked by RokitanskyAschoff on Oct 20, 2016
Latest reply on Nov 7, 2016 by FTITI.Walid
Hi all,

HAL_SAI_Transmit_DMA from stm32l4xx_hal_sai.c v1.5.1 does not follow the sequence described in RM0351 Rev 4 pg 1329:

"In slave mode, the audio frame starts when the audio block is enabled and when a start of
frame is detected.
In Slave TX mode, no underrun event is possible on the first frame after the audio block is
enabled, because the mandatory operating sequence in this case is:
1. Write into the SAI_xDR (by software or by DMA).
2. Wait until the FIFO threshold (FLH) flag is different from 000b (FIFO empty).
3. Enable the audio block in slave transmitter mode."

The result of this is that the SAI can sometimes have an incorrect left / right synchronization. (See [DEAD LINK /public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/STM32L476%20SAI%20frame%20synchronization%20is%20incorrect%20swapped%20left%20right%20if%20HAL_SAI_Transmit_DMA%20is%20called%204%20us%20after%20rising%20edge%20of%E2%80%A6&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&currentviews=7]here)

May I suggest that the function is modified as follows (at least for the case of slave transmitter in I2S)

/**
  * @brief  Transmit an amount of data in non-blocking mode with DMA.
  * @param  hsai: pointer to a SAI_HandleTypeDef structure that contains
  *                the configuration information for SAI module.
  * @param  pData: Pointer to data buffer
  * @param  Size: Amount of data to be sent
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SAI_Transmit_DMA(SAI_HandleTypeDef *hsai, uint8_t *pData, uint16_t Size)
{
  if((pData == NULL) || (Size == 0))
  {
    return  HAL_ERROR;
  }
 
  if(hsai->State == HAL_SAI_STATE_READY)
  {
    /* Process Locked */
    __HAL_LOCK(hsai);
 
    hsai->pBuffPtr = pData;
    hsai->XferSize = Size;
    hsai->XferCount = Size;
    hsai->ErrorCode = HAL_SAI_ERROR_NONE;
    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;
 
    /* Set the DMA Tx abort callback */
    hsai->hdmatx->XferAbortCallback = NULL;
 
    /* Enable the Tx DMA Stream */
    if(HAL_DMA_Start_IT(hsai->hdmatx, (uint32_t)hsai->pBuffPtr, (uint32_t)&hsai->Instance->DR, hsai->XferSize) != HAL_OK)
    {
      __HAL_UNLOCK(hsai);
      return  HAL_ERROR;
    }
 
    /* Enable the interrupts for error handling */
    __HAL_SAI_ENABLE_IT(hsai, SAI_InterruptFlag(hsai, SAI_MODE_DMA));
 
    // Following RM0351 Rev 4 pg 1329:
    // 1) Write into the SAI_xDR (by software or by DMA). (We use DMA as this is the HAL_SAI_Transmit_DMA function)
 
    /* Enable SAI Tx DMA Request */
    hsai->Instance->CR1 |= SAI_xCR1_DMAEN;
 
    // 2) Wait until the FIFO threshold (FLH) flag is different from 000b (FIFO empty).
    // Don't wait for it to be e.g full as SAI might only generate DMA requests up until the FIFO is partially
    // full according to the FTH FifoTHreshold bits
    while((hsai->Instance->SR & SAI_xSR_FLVL) ==  SAI_FIFOSTATUS_EMPTY){}
 
    // 3) Enable the audio block in slave transmitter mode.
    /* Check if the SAI is already enabled */
    if((hsai->Instance->CR1 & SAI_xCR1_SAIEN) == RESET)
    {
      /* Enable SAI peripheral */
      __HAL_SAI_ENABLE(hsai);
    }
 
    /* Process Unlocked */
    __HAL_UNLOCK(hsai);
 
    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}

Outcomes