An Analog-to-Digital Converter (ADC) is an electronic device capable of converting an analog voltage signal into a digital signal with a binary value. Computers or any control system based on a microprocessor cannot interpret analog signals as they only use digital signals. It is necessary to translate or transform them into binary signals.

This Successive Approximation Register (SAR) ADC module has a maximum resolution of 12 bits and 19 multiplexed channels, with 16 of them being external and 3 internal, including a temperature sensor and another for measuring the VBAT voltage.

I could provide a further explanation of how a SAR-type ADC works, but it is already well explained on this page. https://circuitdigest.com/article/how-does-successive-approximation-sar-adc-work-and-where-is-it-best-used

STM32G0 ADC main features

  • High performance
    • 12-bit, 10-bit, 8-bit or 6-bit configurable resolution
    • ADC conversion time: 0.4 μs for 12-bit resolution (2.5Msps), faster conversion times can be obtained by lowering resolution.
    • Self-calibration
    • Programmable sampling time
    • Data alignment with built-in data coherency
    • DMA support
  • Analog input channels
    • 16 external analog inputs
    • 1 channel for internal temperature sensor (VSENSE )
    • 1 channel for internal reference voltage (V REFINT)
    • 1 channel for monitoring external VBAT power supply pin
  • Start-of-conversion can be initiated:
    • By software
    • By hardware triggers with configurable polarity (timer events or GPIO input events)
  • Conversion modes
    • Can convert a single channel or can scan a sequence of channels.
    • Single mode converts selected inputs once per trigger
    • Continuous mode converts selected inputs continuously
    • Discontinuous mode
  • Interrupt generation at the end of sampling, end of conversion, end of sequence conversion, and in case of analog watchdog or overrun events
  • Analog watchdog
  • Oversampler
    • 16-bit data register
    • Oversampling ratio adjustable from 2 to 256x
    • Programmable data shift up to 8-bits
  • ADC supply requirements: 1.62 to 3.6 V
    • ADC input range: V SSA ≤ V IN ≤ V REF+
Internal ADC peripheral

The ADC needs some time in order to sample a signal and then perform a convertion into a digital value, and this time will be affected by the clock speed, a fixed convertion value and the resolution set ( 6, 8, 10 or 12 bits), here is an example for and 1MHz clock and 8 bit resolution

Convertion timing is given by the following formula:
      Tcon = ( Tsampling + Tconv ) / ADC clock 
      Tcon = ( 1.5 + 8.5 ) / 8MHz = 1.us*/

From the point of view of the HAL library, conversion time is given by the values set in the following config structure elements

Tcon = ( AdcHandler.Init.SamplingTimeCommon1 + AdcHandler.Init.Resolution ) / (Fadc / AdcHandler.Init.ClockPrescaler)

STMicroelectronics Official Video Training

STM32CubeG0 ADC HAL driver

The code to control the digital ports is located in the following libraries.

  • stm32g0xx_hal_adc.h
  • stm32g0xx_hal_adc.c
  • stm32g0xx_hal_adc_ex.h
  • stm32g0xx_hal_adc_ex.c

In the stm32g0xx_hal_conf.h file, it is necessary to uncomment the define.

#define HAL_ADC_MODULE_ENABLED

Functions

HAL_StatusTypeDef       HAL_ADC_Init(ADC_HandleTypeDef *hadc);
HAL_StatusTypeDef       HAL_ADC_DeInit(ADC_HandleTypeDef *hadc);
void                    HAL_ADC_MspInit(ADC_HandleTypeDef *hadc);
void                    HAL_ADC_MspDeInit(ADC_HandleTypeDef *hadc);
/* Blocking mode: Polling */
HAL_StatusTypeDef       HAL_ADC_Start(ADC_HandleTypeDef *hadc);
HAL_StatusTypeDef       HAL_ADC_Stop(ADC_HandleTypeDef *hadc);
HAL_StatusTypeDef       HAL_ADC_PollForConversion(ADC_HandleTypeDef *hadc, uint32_t Timeout);
HAL_StatusTypeDef       HAL_ADC_PollForEvent(ADC_HandleTypeDef *hadc, uint32_t EventType, uint32_t Timeout);
/* Non-blocking mode: Interruption */
HAL_StatusTypeDef       HAL_ADC_Start_IT(ADC_HandleTypeDef *hadc);
HAL_StatusTypeDef       HAL_ADC_Stop_IT(ADC_HandleTypeDef *hadc);
/* Non-blocking mode: DMA */
HAL_StatusTypeDef       HAL_ADC_Start_DMA(ADC_HandleTypeDef *hadc, uint32_t *pData, uint32_t Length);
HAL_StatusTypeDef       HAL_ADC_Stop_DMA(ADC_HandleTypeDef *hadc);
/* ADC retrieve conversion value intended to be used with polling or interruption */
uint32_t                HAL_ADC_GetValue(ADC_HandleTypeDef *hadc);
/* ADC IRQHandler and Callbacks used in non-blocking modes (Interruption and DMA) */
void                    HAL_ADC_IRQHandler(ADC_HandleTypeDef *hadc);
void                    HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc);
void                    HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc);
void                    HAL_ADC_LevelOutOfWindowCallback(ADC_HandleTypeDef *hadc);
void                    HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc);
/* Peripheral Control functions*/
HAL_StatusTypeDef       HAL_ADC_ConfigChannel(ADC_HandleTypeDef *hadc, ADC_ChannelConfTypeDef *pConfig);
HAL_StatusTypeDef       HAL_ADC_AnalogWDGConfig(ADC_HandleTypeDef *hadc, ADC_AnalogWDGConfTypeDef *pAnalogWDGConfig);
/* Peripheral State functions */
uint32_t                HAL_ADC_GetState(ADC_HandleTypeDef *hadc);
uint32_t                HAL_ADC_GetError(ADC_HandleTypeDef *hadc);

Initialization Structure

typedef struct
{
  uint32_t ClockPrescaler;        /*!< Select ADC clock source (synhronous clock derived from APB clock or asynchronou */
  uint32_t Resolution;            /*!< Configure the ADC resolution.*/
  uint32_t DataAlign;             /*!< Specify ADC data alignment in conversion data register (right or left)*/
  uint32_t ScanConvMode;          /*!< Configure the sequencer of ADC group regular */
  uint32_t EOCSelection;          /*!< Specify which EOC (End Of Conversion) end of unitary conversion or end of sequence conversions */
  FunctionalState LowPowerAutoWait;       /*!< Select the dynamic low power Auto Delay*/
  FunctionalState LowPowerAutoPowerOff;   /*!< Select the auto-off mode */
  FunctionalState ContinuousConvMode;     /*!< Specify whether the conversion is performed in single mode (one conversion)
                                              or continuous mode for ADC group regular*/
  uint32_t NbrOfConversion;               /*!< Specify the number of ranks that will be converted within the regular group sequencer.*/
  FunctionalState DiscontinuousConvMode;  /*!< Specify whether the conversions sequence of ADC group regular is performed */
  uint32_t ExternalTrigConv;              /*!< Select the external event source used to trigger ADC group regular conversion start.*/
  uint32_t ExternalTrigConvEdge;          /*!< Select the external event edge used to trigger ADC group regular conversion start */
  FunctionalState DMAContinuousRequests;  /*!< Specify whether the DMA requests are performed in one shot mode or in continuous mode */
  uint32_t Overrun;                       /*!< Select the behavior in case of overrun: data overwritten or preserved (default).*/
  uint32_t SamplingTimeCommon1;           /*!< Set sampling time common to a group of channels.*/
  uint32_t SamplingTimeCommon2;           /*!< Set sampling time common to a group of channels, */
  FunctionalState OversamplingMode;       /*!< Specify whether the oversampling feature is enabled or disabled.*/
  ADC_OversamplingTypeDef Oversampling;   /*!< Specify the Oversampling parameters.*/
  uint32_t TriggerFrequencyMode;          /*!< Set ADC trigger frequency mode. */

} ADC_InitTypeDef;

Code Snippets

Exercises

  1. Vary the rotation speed of an LED connected to the eight LEDs using one of the potentiometers on the board.
  2. Display the values of the two potentiometers in Ohms and Volts (since printf does not support floating-point numbers, adapt the display to simulate the floating-point).
  3. Modify the second sample code to have a 12-bit resolution for reading the potentiometers, and take three readings to display their average value.
  4. Control the blinking speed of two LEDs from 100ms to 1000ms in intervals of 50ms. Each LED will be controlled separately using its respective potentiometer.
  5. In the previous program, add a control so that the speed variation only takes effect when the button is held down. Add a control button for each potentiometer.
  6. The MCU has an internal temperature sensor connected to an ADC channel. Display the temperature using semihosting (NOTE: to vary the temperature, rub your fingers and touch the microcontroller with one of them, do not use external heat as it may damage the microcontroller).
  7. Display the value of the first potentiometer in Ohms, the second one in Volts, and also the temperature of the microcontroller. This time, take continuous readings every two seconds using a TIM timer. The timer should trigger the ADC readings automatically without any code intervention.
  8. Modify the third example program to work with DMA