HAL_StatusTypeDef HAL_ADC_ConfigChannel()

in Drivers/STM32U5xx_HAL/Src/stm32u5xx_hal_adc.c [2883:3335]


HAL_StatusTypeDef HAL_ADC_ConfigChannel(ADC_HandleTypeDef *hadc, ADC_ChannelConfTypeDef *pConfig)
{
  HAL_StatusTypeDef tmp_hal_status = HAL_OK;
  uint32_t tmp_offset_shifted;
  uint32_t tmp_config_internal_channel;
  __IO uint32_t wait_loop_index = 0;
  uint32_t tmp_adc_is_conversion_on_going_regular;
  uint32_t tmp_adc_is_conversion_on_going_injected;
  uint32_t tmp_channel;

  /* Check the parameters */
  assert_param(IS_ADC_ALL_INSTANCE(hadc->Instance));

  if (hadc->Instance != ADC4)  /* ADC1 or ADC2 */
  {
    assert_param(IS_ADC_REGULAR_RANK(pConfig->Rank));
    assert_param(IS_ADC_SAMPLE_TIME(pConfig->SamplingTime));
    assert_param(IS_ADC_SINGLE_DIFFERENTIAL(pConfig->SingleDiff));
    assert_param(IS_ADC_OFFSET_NUMBER(pConfig->OffsetNumber));
    assert_param(IS_ADC_RANGE(hadc->Instance, ADC_GET_RESOLUTION(hadc), pConfig->Offset));
    /* if ROVSE is set, the value of the OFFSETy_EN bit in ADCx_OFRy register is
    ignored (considered as reset) */
    assert_param(!((pConfig->OffsetNumber != ADC_OFFSET_NONE) && (hadc->Init.OversamplingMode == ENABLE)));

    /* Verification of channel number */
    if (pConfig->SingleDiff != ADC_DIFFERENTIAL_ENDED)
    {
      assert_param(IS_ADC_CHANNEL(pConfig->Channel));
    }
    else
    {
      assert_param(IS_ADC1_DIFF_CHANNEL(pConfig->Channel));
    }
  }
  else
  {
    assert_param(IS_ADC4_SAMPLE_TIME(pConfig->SamplingTime));

    if ((hadc->Init.ScanConvMode == ADC_SCAN_SEQ_FIXED)          ||
        (hadc->Init.ScanConvMode == ADC_SCAN_SEQ_FIXED_BACKWARD))
    {
      assert_param(IS_ADC4_REGULAR_RANK_SEQ_FIXED(pConfig->Rank));
    }
    else
    {
      assert_param(IS_ADC4_REGULAR_NB_CONV(hadc->Init.NbrOfConversion));

      assert_param(IS_ADC4_REGULAR_RANK(pConfig->Rank));
    }
  }

  __HAL_LOCK(hadc);

  /* Parameters update conditioned to ADC state:                              */
  /* Parameters that can be updated when ADC is disabled or enabled without   */
  /* conversion on going on regular group:                                    */
  /*  - Channel number                                                        */
  /*  - Channel rank                                                          */
  if (LL_ADC_REG_IsConversionOngoing(hadc->Instance) == 0UL)
  {
    if (hadc->Instance != ADC4)  /* ADC1 or ADC2 */
    {
      /* ADC channels preselection */
      hadc->Instance->PCSEL |= (1UL << (__LL_ADC_CHANNEL_TO_DECIMAL_NB((uint32_t)pConfig->Channel) & 0x1FUL));

      /* Set ADC group regular sequence: channel on the selected scan sequence rank */
      LL_ADC_REG_SetSequencerRanks(hadc->Instance, pConfig->Rank, pConfig->Channel);

      /* Parameters update conditioned to ADC state:                              */
      /* Parameters that can be updated when ADC is disabled or enabled without   */
      /* conversion on going on regular group:                                    */
      /*  - Channel sampling time                                                 */
      /*  - Channel offset                                                        */
      tmp_adc_is_conversion_on_going_regular = LL_ADC_REG_IsConversionOngoing(hadc->Instance);
      tmp_adc_is_conversion_on_going_injected = LL_ADC_INJ_IsConversionOngoing(hadc->Instance);
      if ((tmp_adc_is_conversion_on_going_regular == 0UL)
          && (tmp_adc_is_conversion_on_going_injected == 0UL)
         )
      {
        /* Set sampling time of the selected ADC channel */
        LL_ADC_SetChannelSamplingTime(hadc->Instance, pConfig->Channel, pConfig->SamplingTime);

        /* Configure the offset: offset enable/disable, channel, offset value */

        /* Shift the offset with respect to the selected ADC resolution. */
        /* Offset has to be left-aligned on bit 11, the LSB (right bits) are set to 0 */
        tmp_offset_shifted = ADC_OFFSET_SHIFT_RESOLUTION(hadc, (uint32_t)pConfig->Offset);

        if (pConfig->OffsetNumber != ADC_OFFSET_NONE)
        {
          /* Set ADC selected offset number */
          LL_ADC_SetOffset(hadc->Instance, pConfig->OffsetNumber, pConfig->Channel, tmp_offset_shifted);
          assert_param(IS_ADC_OFFSET_SIGN(pConfig->OffsetSign));
          assert_param(IS_FUNCTIONAL_STATE(pConfig->OffsetSignedSaturation));
          /* Set ADC selected offset sign */
          LL_ADC_SetOffsetSign(hadc->Instance, pConfig->OffsetNumber, pConfig->OffsetSign);
          /* Set ADC selected offset signed saturation */
          LL_ADC_SetOffsetSignedSaturation(hadc->Instance, pConfig->OffsetNumber,                  \
                                           (pConfig->OffsetSignedSaturation == ENABLE)             \
                                           ? LL_ADC_OFFSET_SIGNED_SATURATION_ENABLE                \
                                           : LL_ADC_OFFSET_SIGNED_SATURATION_DISABLE);
        }
        else
        {
          /* Scan OFR1, OFR2, OFR3, OFR4 to check if the selected channel is enabled.
            If this is the case, offset OFRx is disabled since
            pConfig->OffsetNumber = ADC_OFFSET_NONE. */
          if (((hadc->Instance->OFR1) & ADC_OFR1_OFFSET1_CH) == ADC_OFR_CHANNEL(pConfig->Channel))
          {
            CLEAR_BIT(hadc->Instance->OFR1, ADC_OFR1_SSAT);
          }
          if (((hadc->Instance->OFR2) & ADC_OFR2_OFFSET2_CH) == ADC_OFR_CHANNEL(pConfig->Channel))
          {
            CLEAR_BIT(hadc->Instance->OFR2, ADC_OFR2_SSAT);
          }
          if (((hadc->Instance->OFR3) & ADC_OFR3_OFFSET3_CH) == ADC_OFR_CHANNEL(pConfig->Channel))
          {
            CLEAR_BIT(hadc->Instance->OFR3, ADC_OFR3_SSAT);
          }
          if (((hadc->Instance->OFR4) & ADC_OFR4_OFFSET4_CH) == ADC_OFR_CHANNEL(pConfig->Channel))
          {
            CLEAR_BIT(hadc->Instance->OFR4, ADC_OFR4_SSAT);
          }
        }
      }

      /* Parameters update conditioned to ADC state:                              */
      /* Parameters that can be updated only when ADC is disabled:                */
      /*  - Single or differential mode                                           */
      /*  - Internal measurement channels: Vbat/VrefInt/TempSensor                */
      if (LL_ADC_IsEnabled(hadc->Instance) == 0UL)
      {
        /* Set mode single-ended or differential input of the selected ADC channel */
        LL_ADC_SetChannelSingleDiff(hadc->Instance, pConfig->Channel, pConfig->SingleDiff);

        /* Configuration of differential mode */
        if (pConfig->SingleDiff == ADC_DIFFERENTIAL_ENDED)
        {
          /* Set sampling time of the selected ADC channel */
          /* Note: ADC channel number masked with value "0x1F" to ensure shift value within 32 bits range */
          tmp_channel = __LL_ADC_DECIMAL_NB_TO_CHANNEL((__LL_ADC_CHANNEL_TO_DECIMAL_NB(pConfig->Channel) \
                                                        + 1UL) & 0x1FUL);
          LL_ADC_SetChannelSamplingTime(hadc->Instance, tmp_channel, pConfig->SamplingTime);
        }
        /* Management of internal measurement channels: Vbat/VrefInt/TempSensor.  */
        /* If internal channel selected, enable dedicated internal buffers and    */
        /* paths.                                                                 */
        /* Note: these internal measurement paths can be disabled using           */
        /* HAL_ADC_DeInit().                                                      */

        if (__LL_ADC_IS_CHANNEL_INTERNAL(pConfig->Channel))
        {
          /* Configuration of common ADC parameters                                 */

          tmp_config_internal_channel = LL_ADC_GetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(hadc->Instance));

          /* Software is allowed to change common parameters only when all ADCs   */
          /* of the common group are disabled.                                    */
          if (__LL_ADC_IS_ENABLED_ALL_COMMON_INSTANCE(__LL_ADC_COMMON_INSTANCE(hadc->Instance)) == 0UL)
          {
            /* If the requested internal measurement path has already been enabled, */
            /* bypass the configuration processing.                                 */
            if ((pConfig->Channel == ADC_CHANNEL_TEMPSENSOR)
                && ((tmp_config_internal_channel & LL_ADC_PATH_INTERNAL_TEMPSENSOR) == 0UL))
            {
              if (ADC_TEMPERATURE_SENSOR_INSTANCE(hadc))
              {
                LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(hadc->Instance),
                                               LL_ADC_PATH_INTERNAL_TEMPSENSOR | tmp_config_internal_channel);

                /* Delay for temperature sensor stabilization time */
                /* Wait loop initialization and execution */
                /* Note: Variable divided by 2 to compensate partially              */
                /*       CPU processing cycles, scaling in us split to not          */
                /*       exceed 32 bits register capacity and handle low frequency. */
                wait_loop_index = ((LL_ADC_DELAY_INTERNAL_REGUL_STAB_US / 10UL)                                       \
                                   * ((SystemCoreClock / (100000UL * 2UL)) + 1UL));
                while (wait_loop_index != 0UL)
                {
                  wait_loop_index--;
                }
              }
            }
            else if ((pConfig->Channel == ADC_CHANNEL_VBAT) && ((tmp_config_internal_channel                          \
                                                                 & LL_ADC_PATH_INTERNAL_VBAT) == 0UL))
            {
              if (ADC_BATTERY_VOLTAGE_INSTANCE(hadc))
              {
                LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(hadc->Instance),
                                               LL_ADC_PATH_INTERNAL_VBAT | tmp_config_internal_channel);
              }
            }
            else if ((pConfig->Channel == ADC_CHANNEL_VREFINT)
                     && ((tmp_config_internal_channel & LL_ADC_PATH_INTERNAL_VREFINT) == 0UL))
            {
              if (ADC_VREFINT_INSTANCE(hadc))
              {
                LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(hadc->Instance),
                                               LL_ADC_PATH_INTERNAL_VREFINT | tmp_config_internal_channel);
              }
            }
            else
            {
              /* nothing to do */
            }
          }
          /* If the requested internal measurement path has already been          */
          /* enabled and other ADC of the common group are enabled, internal      */
          /* measurement paths cannot be enabled.                                 */
          else
          {
            /* Update ADC state machine to error */
            SET_BIT(hadc->State, HAL_ADC_STATE_ERROR_CONFIG);

            tmp_hal_status = HAL_ERROR;
          }
        }
      }
    }
    else
    {
      /* Remap Internal Channels for Cut1 vs Cut2 */
      tmp_channel = pConfig->Channel;
      if (HAL_GetREVID() == REV_ID_A) /* STM32U5 silicon Rev.A */
      {
        if (pConfig->Channel == ADC4_CHANNEL_TEMPSENSOR)
        {
          tmp_channel = (LL_ADC_CHANNEL_22 | ADC_CHANNEL_ID_INTERNAL_CH);
        }
        else if (pConfig->Channel == ADC4_CHANNEL_VBAT)
        {
          tmp_channel = (LL_ADC_CHANNEL_23 | ADC_CHANNEL_ID_INTERNAL_CH);
        }
        else if (pConfig->Channel == ADC_CHANNEL_VCORE)
        {
          tmp_channel = (LL_ADC_CHANNEL_VREFINT | LL_ADC_CHANNEL_DIFFERENCIATION_VREFINT_VCORE);
        }
        else if (pConfig->Channel == ADC_CHANNEL_DAC1CH1_ADC4)
        {
          tmp_channel = (LL_ADC_CHANNEL_20 | ADC_CHANNEL_ID_INTERNAL_CH);
        }
        else if (pConfig->Channel == ADC_CHANNEL_DAC1CH2_ADC4)
        {
          tmp_channel = (LL_ADC_CHANNEL_21 | ADC_CHANNEL_ID_INTERNAL_CH);
        }
        else
        {
          tmp_channel = pConfig->Channel;
        }
      }

      /* Configure channel: depending on rank setting, add it or remove it from */
      /* ADC sequencer.                                                         */
      /* If sequencer set to not fully configurable with channel rank set to    */
      /* none, remove the channel from the sequencer.                           */
      /* Otherwise (sequencer set to fully configurable or to to not fully      */
      /* configurable with channel rank to be set), configure the selected      */
      /* channel.                                                               */
      if (pConfig->Rank != ADC4_RANK_NONE)
      {
        /* Regular sequence configuration */
        /* Note: ADC channel configuration requires few ADC clock cycles        */
        /*       to be ready. Processing of ADC settings in this function       */
        /*       induce that a specific wait time is not necessary.             */
        /*       For more details on ADC channel configuration ready,           */
        /*       refer to function "LL_ADC_IsActiveFlag_CCRDY()".               */
        if ((hadc->Init.ScanConvMode == ADC_SCAN_SEQ_FIXED)         ||
            (hadc->Init.ScanConvMode == ADC_SCAN_SEQ_FIXED_BACKWARD))
        {
          /* Sequencer set to not fully configurable:                           */
          /* Set the channel by enabling the corresponding bitfield.            */
          LL_ADC_REG_SetSequencerChAdd(hadc->Instance, tmp_channel);
        }
        else
        {
          /* Sequencer set to fully configurable:                               */
          /* Set the channel by entering it into the selected rank.             */

          /* Memorize the channel set into variable in HAL ADC handle */
          MODIFY_REG(hadc->ADCGroupRegularSequencerRanks,
                     ADC_CHSELR_SQ1 << (pConfig->Rank & 0x1FUL),
                     __LL_ADC_CHANNEL_TO_DECIMAL_NB(tmp_channel) << (pConfig->Rank & 0x1FUL));

          /* If the selected rank is below ADC group regular sequencer length,  */
          /* apply the configuration in ADC register.                           */
          /* Note: Otherwise, configuration is not applied.                     */
          /*       To apply it, parameter'NbrOfConversion' must be increased.   */
          if (((pConfig->Rank >> 2UL) + 1UL) <= hadc->Init.NbrOfConversion)
          {
            if (HAL_GetREVID() <= REV_ID_A) /* STM32U5 silicon Rev.A */
            {
              if (__LL_ADC_CHANNEL_TO_DECIMAL_NB(tmp_channel) >= 20UL)
              {
                tmp_channel = (1UL << (__LL_ADC_CHANNEL_TO_DECIMAL_NB(tmp_channel) - 9UL));
              }
            }
            LL_ADC_REG_SetSequencerRanks(hadc->Instance, pConfig->Rank, tmp_channel);
          }
        }

        /* Set sampling time of the selected ADC channel */
        LL_ADC_SetChannelSamplingTime(hadc->Instance, tmp_channel, pConfig->SamplingTime);


        /* Management of internal measurement channels: VrefInt/TempSensor/Vbat */
        /* internal measurement paths enable: If internal channel selected,     */
        /* enable dedicated internal buffers and path.                          */
        /* Note: these internal measurement paths can be disabled using         */
        /*       HAL_ADC_DeInit() or removing the channel from sequencer with   */
        /*       channel configuration parameter "Rank".                        */
        if (__LL_ADC_IS_CHANNEL_INTERNAL(pConfig->Channel))
        {
          tmp_config_internal_channel = LL_ADC_GetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(hadc->Instance));

          /* If the requested internal measurement path has already been enabled,   */
          /* bypass the configuration processing.                                   */
          if ((pConfig->Channel == ADC4_CHANNEL_TEMPSENSOR)                                                           \
              && ((tmp_config_internal_channel & LL_ADC_PATH_INTERNAL_TEMPSENSOR) == 0UL))
          {
            LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(hadc->Instance),
                                           LL_ADC_PATH_INTERNAL_TEMPSENSOR | tmp_config_internal_channel);

            /* Delay for temperature sensor stabilization time */
            /* Wait loop initialization and execution */
            /* Note: Variable divided by 2 to compensate partially              */
            /*       CPU processing cycles, scaling in us split to not          */
            /*       exceed 32 bits register capacity and handle low frequency. */
            wait_loop_index = ((LL_ADC_DELAY_TEMPSENSOR_STAB_US / 10UL) * (SystemCoreClock / (100000UL * 2UL)));
            while (wait_loop_index != 0UL)
            {
              wait_loop_index--;
            }
          }
          else if ((pConfig->Channel == ADC4_CHANNEL_VBAT) && ((tmp_config_internal_channel                           \
                                                                & LL_ADC_PATH_INTERNAL_VBAT) == 0UL))
          {
            LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(hadc->Instance),
                                           LL_ADC_PATH_INTERNAL_VBAT | tmp_config_internal_channel);
          }
          else if ((pConfig->Channel == ADC_CHANNEL_VREFINT)                                                          \
                   && ((tmp_config_internal_channel & LL_ADC_PATH_INTERNAL_VREFINT) == 0UL))
          {
            LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(hadc->Instance),
                                           LL_ADC_PATH_INTERNAL_VREFINT | tmp_config_internal_channel);
          }
          else if ((pConfig->Channel == ADC_CHANNEL_VCORE)                                                            \
                   && ((tmp_config_internal_channel  & LL_ADC_PATH_INTERNAL_VREFINT) == 0UL))
          {
            if (ADC_VCORE_INSTANCE(hadc))
            {
              LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(hadc->Instance),
                                             LL_ADC_PATH_INTERNAL_VREFINT | tmp_config_internal_channel);
              if (HAL_GetREVID() <= REV_ID_A) /* STM32U5 silicon Rev.A */
              {
                SET_BIT((hadc->Instance->OR), ADC_OR_CHN0SEL);
              }
            }
          }
          else
          {
            /* nothing to do */
          }
          /* If STM32U5 silicon Rev.B, ADC_CHANNEL_DAC1CH1 and ADC_CHANNEL_DAC1CH2 are both on Channel 21
             and selection is done via ADC_OR[0] register */
          if (HAL_GetREVID() == REV_ID_B) /* STM32U5 silicon Rev.B */
          {
            if ((pConfig->Channel == ADC_CHANNEL_DAC1CH2_ADC4)                                                        \
                && ((tmp_config_internal_channel & LL_ADC_PATH_INTERNAL_VREFINT) == 0UL))
            {
              SET_BIT((hadc->Instance->OR), ADC_OR_CHN0SEL);
            }
          }
        }
      }
      else
      {
        /* Regular sequencer configuration */
        /* Note: Case of sequencer set to fully configurable:                   */
        /*       Sequencer rank cannot be disabled, only affected to            */
        /*       another channel.                                               */
        /*       To remove a rank, use parameter 'NbrOfConversion".             */
        if ((hadc->Init.ScanConvMode == ADC_SCAN_SEQ_FIXED)         ||
            (hadc->Init.ScanConvMode == ADC_SCAN_SEQ_FIXED_BACKWARD))
        {
          /* Sequencer set to not fully configurable:                           */
          /* Reset the channel by disabling the corresponding bitfield.         */
          LL_ADC_REG_SetSequencerChRem(hadc->Instance, tmp_channel);
        }

        /* Management of internal measurement channels: Vbat/VrefInt/TempSensor.  */
        /* If internal channel selected, enable dedicated internal buffers and    */
        /* paths.                                                                 */
        if (__LL_ADC_IS_CHANNEL_INTERNAL(pConfig->Channel))
        {
          tmp_config_internal_channel = LL_ADC_GetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(hadc->Instance));

          if (pConfig->Channel == ADC_CHANNEL_TEMPSENSOR)
          {
            LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(hadc->Instance),
                                           ~LL_ADC_PATH_INTERNAL_TEMPSENSOR & tmp_config_internal_channel);
          }
          else if (pConfig->Channel == ADC_CHANNEL_VBAT)
          {
            LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(hadc->Instance),
                                           ~LL_ADC_PATH_INTERNAL_VBAT & tmp_config_internal_channel);
          }
          else if (pConfig->Channel == ADC_CHANNEL_VREFINT)
          {
            LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(hadc->Instance),
                                           ~LL_ADC_PATH_INTERNAL_VREFINT & tmp_config_internal_channel);
          }
          else if (pConfig->Channel == ADC_CHANNEL_VCORE)
          {
            LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(hadc->Instance),
                                           ~LL_ADC_PATH_INTERNAL_VREFINT & tmp_config_internal_channel);
            if (HAL_GetREVID() <= REV_ID_A) /* STM32U5 silicon Rev.A */
            {
              SET_BIT((hadc->Instance->OR), ADC_OR_CHN0SEL);
            }
          }
          else
          {
            /* nothing to do */
          }
          /* If STM32U5 silicon Rev.B, ADC_CHANNEL_DAC1CH1 and ADC_CHANNEL_DAC1CH2 are both on Channel 21
             and selection is done via ADC_OR[0] register */
          if (HAL_GetREVID() == REV_ID_B) /* STM32U5 silicon Rev.B */
          {
            if ((pConfig->Channel == ADC_CHANNEL_DAC1CH2_ADC4)                                                        \
                && ((tmp_config_internal_channel & LL_ADC_PATH_INTERNAL_VREFINT) == 0UL))
            {
              SET_BIT((hadc->Instance->OR), ADC_OR_CHN0SEL);
            }
          }
        }
      }
    }
  }

  /* If a conversion is on going on regular group, no update on regular       */
  /* channel could be done on neither of the channel configuration structure  */
  /* parameters.                                                              */
  else
  {
    /* Update ADC state machine to error */
    SET_BIT(hadc->State, HAL_ADC_STATE_ERROR_CONFIG);
    tmp_hal_status = HAL_ERROR;
  }

  __HAL_UNLOCK(hadc);

  return tmp_hal_status;
}