HAL_StatusTypeDef HAL_SAI_Init()

in Drivers/STM32U5xx_HAL/Src/stm32u5xx_hal_sai.c [368:651]


HAL_StatusTypeDef HAL_SAI_Init(SAI_HandleTypeDef *hsai)
{
  uint32_t tmpregisterGCR;
  uint32_t ckstr_bits;
  uint32_t syncen_bits;

  /* Check the SAI handle allocation */
  if (hsai == NULL)
  {
    return HAL_ERROR;
  }

  /* check the instance */
  assert_param(IS_SAI_ALL_INSTANCE(hsai->Instance));

  /* Check the SAI Block parameters */
  assert_param(IS_SAI_AUDIO_FREQUENCY(hsai->Init.AudioFrequency));
  assert_param(IS_SAI_BLOCK_PROTOCOL(hsai->Init.Protocol));
  assert_param(IS_SAI_BLOCK_MODE(hsai->Init.AudioMode));
  assert_param(IS_SAI_BLOCK_DATASIZE(hsai->Init.DataSize));
  assert_param(IS_SAI_BLOCK_FIRST_BIT(hsai->Init.FirstBit));
  assert_param(IS_SAI_BLOCK_CLOCK_STROBING(hsai->Init.ClockStrobing));
  assert_param(IS_SAI_BLOCK_SYNCHRO(hsai->Init.Synchro));
  assert_param(IS_SAI_BLOCK_MCK_OUTPUT(hsai->Init.MckOutput));
  assert_param(IS_SAI_BLOCK_OUTPUT_DRIVE(hsai->Init.OutputDrive));
  assert_param(IS_SAI_BLOCK_NODIVIDER(hsai->Init.NoDivider));
  assert_param(IS_SAI_BLOCK_FIFO_THRESHOLD(hsai->Init.FIFOThreshold));
  assert_param(IS_SAI_MONO_STEREO_MODE(hsai->Init.MonoStereoMode));
  assert_param(IS_SAI_BLOCK_COMPANDING_MODE(hsai->Init.CompandingMode));
  assert_param(IS_SAI_BLOCK_TRISTATE_MANAGEMENT(hsai->Init.TriState));
  assert_param(IS_SAI_BLOCK_SYNCEXT(hsai->Init.SynchroExt));
  assert_param(IS_SAI_BLOCK_MCK_OVERSAMPLING(hsai->Init.MckOverSampling));

  /* Check the SAI Block Frame parameters */
  assert_param(IS_SAI_BLOCK_FRAME_LENGTH(hsai->FrameInit.FrameLength));
  assert_param(IS_SAI_BLOCK_ACTIVE_FRAME(hsai->FrameInit.ActiveFrameLength));
  assert_param(IS_SAI_BLOCK_FS_DEFINITION(hsai->FrameInit.FSDefinition));
  assert_param(IS_SAI_BLOCK_FS_POLARITY(hsai->FrameInit.FSPolarity));
  assert_param(IS_SAI_BLOCK_FS_OFFSET(hsai->FrameInit.FSOffset));

  /* Check the SAI Block Slot parameters */
  assert_param(IS_SAI_BLOCK_FIRSTBIT_OFFSET(hsai->SlotInit.FirstBitOffset));
  assert_param(IS_SAI_BLOCK_SLOT_SIZE(hsai->SlotInit.SlotSize));
  assert_param(IS_SAI_BLOCK_SLOT_NUMBER(hsai->SlotInit.SlotNumber));
  assert_param(IS_SAI_SLOT_ACTIVE(hsai->SlotInit.SlotActive));

  /* Check the SAI PDM parameters */
  assert_param(IS_FUNCTIONAL_STATE(hsai->Init.PdmInit.Activation));
  if (hsai->Init.PdmInit.Activation == ENABLE)
  {
    assert_param(IS_SAI_PDM_MIC_PAIRS_NUMBER(hsai->Init.PdmInit.MicPairsNbr));
    assert_param(IS_SAI_PDM_CLOCK_ENABLE(hsai->Init.PdmInit.ClockEnable));
    /* Check that SAI sub-block is SAI1 sub-block A, in master RX mode with free protocol */
    if ((hsai->Instance != SAI1_Block_A) ||
        (hsai->Init.AudioMode != SAI_MODEMASTER_RX) ||
        (hsai->Init.Protocol != SAI_FREE_PROTOCOL))
    {
      return HAL_ERROR;
    }
  }

  if (hsai->State == HAL_SAI_STATE_RESET)
  {
    /* Allocate lock resource and initialize it */
    hsai->Lock = HAL_UNLOCKED;

#if (USE_HAL_SAI_REGISTER_CALLBACKS == 1)
    /* Reset callback pointers to the weak predefined callbacks */
    hsai->RxCpltCallback     = HAL_SAI_RxCpltCallback;
    hsai->RxHalfCpltCallback = HAL_SAI_RxHalfCpltCallback;
    hsai->TxCpltCallback     = HAL_SAI_TxCpltCallback;
    hsai->TxHalfCpltCallback = HAL_SAI_TxHalfCpltCallback;
    hsai->ErrorCallback      = HAL_SAI_ErrorCallback;

    /* Init the low level hardware : GPIO, CLOCK, NVIC and DMA */
    if (hsai->MspInitCallback == NULL)
    {
      hsai->MspInitCallback = HAL_SAI_MspInit;
    }
    hsai->MspInitCallback(hsai);
#else
    /* Init the low level hardware : GPIO, CLOCK, NVIC and DMA */
    HAL_SAI_MspInit(hsai);
#endif /* USE_HAL_SAI_REGISTER_CALLBACKS */
  }

  /* Disable the selected SAI peripheral */
  if (SAI_Disable(hsai) != HAL_OK)
  {
    return HAL_ERROR;
  }

  hsai->State = HAL_SAI_STATE_BUSY;

  /* SAI Block Synchro Configuration -----------------------------------------*/
  /* This setting must be done with both audio block (A & B) disabled         */
  switch (hsai->Init.SynchroExt)
  {
    case SAI_SYNCEXT_DISABLE :
      tmpregisterGCR = 0;
      break;
    case SAI_SYNCEXT_OUTBLOCKA_ENABLE :
      tmpregisterGCR = SAI_GCR_SYNCOUT_0;
      break;
    case SAI_SYNCEXT_OUTBLOCKB_ENABLE :
      tmpregisterGCR = SAI_GCR_SYNCOUT_1;
      break;
    default :
      tmpregisterGCR = 0;
      break;
  }

  switch (hsai->Init.Synchro)
  {
    case SAI_ASYNCHRONOUS :
      syncen_bits = 0;
      break;
    case SAI_SYNCHRONOUS :
      syncen_bits = SAI_xCR1_SYNCEN_0;
      break;
    case SAI_SYNCHRONOUS_EXT_SAI1 :
      syncen_bits = SAI_xCR1_SYNCEN_1;
      break;
    case SAI_SYNCHRONOUS_EXT_SAI2 :
      syncen_bits = SAI_xCR1_SYNCEN_1;
      tmpregisterGCR |= SAI_GCR_SYNCIN_0;
      break;
    default :
      syncen_bits = 0;
      break;
  }

  if ((hsai->Instance == SAI1_Block_A) || (hsai->Instance == SAI1_Block_B))
  {
    SAI1->GCR = tmpregisterGCR;
  }
  else
  {
    SAI2->GCR = tmpregisterGCR;
  }

  if (hsai->Init.AudioFrequency != SAI_AUDIO_FREQUENCY_MCKDIV)
  {
    uint32_t freq = 0;
    uint32_t tmpval;

    /* In this case, the MCKDIV value is calculated to get AudioFrequency */
    if ((hsai->Instance == SAI1_Block_A) || (hsai->Instance == SAI1_Block_B))
    {
      freq = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SAI1);
    }
    if ((hsai->Instance == SAI2_Block_A) || (hsai->Instance == SAI2_Block_B))
    {
      freq = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SAI2);
    }

    /* Configure Master Clock Divider using the following formula :
       - If NODIV = 1 :
         MCKDIV[5:0] = SAI_CK_x / (FS * (FRL + 1))
       - If NODIV = 0 :
         MCKDIV[5:0] = SAI_CK_x / (FS * (OSR + 1) * 256) */
    if (hsai->Init.NoDivider == SAI_MASTERDIVIDER_DISABLE)
    {
      /* NODIV = 1 */
      uint32_t tmpframelength;

      if (hsai->Init.Protocol == SAI_SPDIF_PROTOCOL)
      {
        /* For SPDIF protocol, frame length is set by hardware to 64 */
        tmpframelength = 64U;
      }
      else if (hsai->Init.Protocol == SAI_AC97_PROTOCOL)
      {
        /* For AC97 protocol, frame length is set by hardware to 256 */
        tmpframelength = 256U;
      }
      else
      {
        /* For free protocol, frame length is set by user */
        tmpframelength = hsai->FrameInit.FrameLength;
      }

      /* (freq x 10) to keep Significant digits */
      tmpval = (freq * 10U) / (hsai->Init.AudioFrequency * tmpframelength);
    }
    else
    {
      /* NODIV = 0 */
      uint32_t tmposr;
      tmposr = (hsai->Init.MckOverSampling == SAI_MCK_OVERSAMPLING_ENABLE) ? 2U : 1U;
      /* (freq x 10) to keep Significant digits */
      tmpval = (freq * 10U) / (hsai->Init.AudioFrequency * tmposr * 256U);
    }
    hsai->Init.Mckdiv = tmpval / 10U;

    /* Round result to the nearest integer */
    if ((tmpval % 10U) > 8U)
    {
      hsai->Init.Mckdiv += 1U;
    }

    /* For SPDIF protocol, SAI shall provide a bit clock twice faster the symbol-rate */
    if (hsai->Init.Protocol == SAI_SPDIF_PROTOCOL)
    {
      hsai->Init.Mckdiv = hsai->Init.Mckdiv >> 1;
    }
  }

  /* Check the SAI Block master clock divider parameter */
  assert_param(IS_SAI_BLOCK_MASTER_DIVIDER(hsai->Init.Mckdiv));

  /* Compute CKSTR bits of SAI CR1 according ClockStrobing and AudioMode */
  if ((hsai->Init.AudioMode == SAI_MODEMASTER_TX) || (hsai->Init.AudioMode == SAI_MODESLAVE_TX))
  {
    /* Transmit */
    ckstr_bits = (hsai->Init.ClockStrobing == SAI_CLOCKSTROBING_RISINGEDGE) ? 0U : SAI_xCR1_CKSTR;
  }
  else
  {
    /* Receive */
    ckstr_bits = (hsai->Init.ClockStrobing == SAI_CLOCKSTROBING_RISINGEDGE) ? SAI_xCR1_CKSTR : 0U;
  }

  /* SAI Block Configuration -------------------------------------------------*/
  /* SAI CR1 Configuration */
  hsai->Instance->CR1 &= ~(SAI_xCR1_MODE | SAI_xCR1_PRTCFG |  SAI_xCR1_DS |      \
                           SAI_xCR1_LSBFIRST | SAI_xCR1_CKSTR | SAI_xCR1_SYNCEN | \
                           SAI_xCR1_MONO | SAI_xCR1_OUTDRIV  | SAI_xCR1_DMAEN |  \
                           SAI_xCR1_NODIV | SAI_xCR1_MCKDIV | SAI_xCR1_OSR |     \
                           SAI_xCR1_MCKEN);

  hsai->Instance->CR1 |= (hsai->Init.AudioMode | hsai->Init.Protocol |           \
                          hsai->Init.DataSize | hsai->Init.FirstBit  |           \
                          ckstr_bits | syncen_bits |                             \
                          hsai->Init.MonoStereoMode | hsai->Init.OutputDrive |   \
                          hsai->Init.NoDivider | (hsai->Init.Mckdiv << 20) |     \
                          hsai->Init.MckOverSampling | hsai->Init.MckOutput);

  /* SAI CR2 Configuration */
  hsai->Instance->CR2 &= ~(SAI_xCR2_FTH | SAI_xCR2_FFLUSH | SAI_xCR2_COMP | SAI_xCR2_CPL);
  hsai->Instance->CR2 |= (hsai->Init.FIFOThreshold | hsai->Init.CompandingMode | hsai->Init.TriState);

  /* SAI Frame Configuration -----------------------------------------*/
  hsai->Instance->FRCR &= (~(SAI_xFRCR_FRL | SAI_xFRCR_FSALL | SAI_xFRCR_FSDEF | \
                             SAI_xFRCR_FSPOL | SAI_xFRCR_FSOFF));
  hsai->Instance->FRCR |= ((hsai->FrameInit.FrameLength - 1U) |
                           hsai->FrameInit.FSOffset |
                           hsai->FrameInit.FSDefinition |
                           hsai->FrameInit.FSPolarity   |
                           ((hsai->FrameInit.ActiveFrameLength - 1U) << 8));

  /* SAI Block_x SLOT Configuration ------------------------------------------*/
  /* This register has no meaning in AC 97 and SPDIF audio protocol */
  hsai->Instance->SLOTR &= (~(SAI_xSLOTR_FBOFF | SAI_xSLOTR_SLOTSZ |  \
                              SAI_xSLOTR_NBSLOT | SAI_xSLOTR_SLOTEN));

  hsai->Instance->SLOTR |= hsai->SlotInit.FirstBitOffset | hsai->SlotInit.SlotSize | \
                           (hsai->SlotInit.SlotActive << 16) | ((hsai->SlotInit.SlotNumber - 1U) <<  8);

  /* SAI PDM Configuration ---------------------------------------------------*/
  if (hsai->Instance == SAI1_Block_A)
  {
    /* Disable PDM interface */
    SAI1->PDMCR &= ~(SAI_PDMCR_PDMEN);
    if (hsai->Init.PdmInit.Activation == ENABLE)
    {
      /* Configure and enable PDM interface */
      SAI1->PDMCR = (hsai->Init.PdmInit.ClockEnable |
                     ((hsai->Init.PdmInit.MicPairsNbr - 1U) << SAI_PDMCR_MICNBR_Pos));
      SAI1->PDMCR |= SAI_PDMCR_PDMEN;
    }
  }

  /* Initialize the error code */
  hsai->ErrorCode = HAL_SAI_ERROR_NONE;

  /* Initialize the SAI state */
  hsai->State = HAL_SAI_STATE_READY;

  /* Release Lock */
  __HAL_UNLOCK(hsai);

  return HAL_OK;
}