Using the ADC
In this tutorial, we’ll learn how to use the ADC with STM32 Cube IDE.
Requirements
ADC? Like the band AC/DC?
The Analog-to-Digital Converter (ADC) is a system that, as the name suggests, converts an analog signal into a digital signal. The reverse process is done by a Digital-to-Analog Converter (DAC). This allows us to transform voltages into a format that the microcontroller can process and analyze. For example, this is how you can read the values from a potentiometer, a microphone, or a light sensor. If you want to explore this technology further, check out this webpage that explains ADCs in more depth (and no, I’m not sponsored!).
How it works
Red signal: Analog, Blue signal: Digital
- Sampling: The analog signal is sampled at regular time intervals (sampling frequency).
- Quantization: Each sampled value is approximated to the nearest discrete value in the binary range.
- Encoding: The quantized values are encoded into a sequence of bits. The resolution of the ADC, expressed in bits, determines how many quantization levels are possible. If we denote
as the number of bits, the number of levels is:
For example, on the STM32 board used in this tutorial (NUCLEO H7A3ZI-Q), the ADC is 12-bit: it can represent
levels (from 0 to 4095).
Configuration
Generally, on STM32 boards, pins A0 to A5 have ADC capability (but it’s always a good habit to check the datasheet!). We’ll do a simple test to check the minimum and maximum ADC levels.
To do this, connect one analog pin to +5V (here A0) and another to GND (here A1):
Wiring
Now, in CubeIDE:
Create a new project and open CubeMX (.ioc window).
Identify which pins correspond to A0 and A1. You can find mappings between “Ax” and “PAx” by checking the pinout configuration of your board. In my case, A0 ↔ PA3 and A1 ↔ PC0.
Then configure each pin as ADC input (e.g., “ADCx_INPx”). My setup:
PA3: ADC1_INP15
PC0: ADC2_INP10
Now configure the pins with an ADC (like “ADCx_INPx”). Here’s the config I used:
- PA3: ADC1_INP15
- PC0: ADC2_INP10
Tip
Note that a pin may support multiple ADCs. I could’ve swapped ADC1 and ADC2 or even used ADC3. However, make sure not to configure the same ADC on both pins simultaneously!
In the “ADC” tab on the left, select ADC1 and configure as follows:
- Check IN15 Single-ended
- Set Resolution to ADC 12-bit resolution
Tip
If you get clock frequency errors, go to the “Clock configuration” tab and tweak the values of DIVM2, DIVN2, and DIVP2 to fix them.
Repeat for ADC2.
Programming
Here’s how to program ADC1 in a .c
file. Programming ADC2 or any other is done in a similar way.
To start ADC conversion, add the two lines below to your main.c
file (in the “Core/Src” folder):
To start ADC conversion, add the lines below in your main.c
file (in the “Core/Src” folder):
Inside the private variables section:
43/* Private variables ---------------------------------------------------------*/
44
45UART_HandleTypeDef huart2;
46
47/* USER CODE BEGIN PV */
48int value_a0;
49/* USER CODE END PV */
Inside the int main(void){}
function:
65int main(void)
66{
67 /* USER CODE BEGIN 1 */
68 HAL_ADC_Start(&hadc1);
69 /* USER CODE END 1 */
HAL_ADC_Start(&hadc1);
starts conversion for ADC1. int value_a0
is used to store the value read on the A0 pin.
Warning
Be sure to write your code between the “/* USER CODE BEGIN */” and “/* USER CODE END */” tags. Otherwise, your code will be deleted when building!
Inside a loop (e.g. the existing while(1){}
), add the two lines below:
HAL_ADC_PollForConversion(&hadc, 1)
; waits for ADC conversion to complete. value_a0 = HAL_ADC_GetValue(&hadc1)
; retrieves the converted value and stores it in value_a0
.
99/* Infinite loop */
100/* USER CODE BEGIN WHILE */
101while (1)
102{
103 HAL_ADC_PollForConversion(&hadc, 1);
104 value_a0 = HAL_ADC_GetValue(&hadc1);
105 /* USER CODE END WHILE */
106
107 /* USER CODE BEGIN 3 */
108}
Once compiled and uploaded to your board, if you display the values from A0 (value_a0
) and A1 (value_a1
) using the above method, you’ll get value_a0 = 4095
and value_a1 = 0
, which matches the resolution. If you go further, you can send these values to a serial plotter to visualize the signal:
Blue signal: A0, Orange signal: A1
To go further, you can try connecting connecting other components to these pins, like a potentiometer as shown in the introduction to embedded programming
Interpreting ADC values using sensitivity
If you want to convert ADC readings into human-readable voltages instead of LSBs (Least Significant Bit), you’ll need the ADC sensitivity. Use the formula:
Where:
- range: Voltage range (e.g., 0 to 5V)
- resolution: Bit resolution of your ADC
In my case, my voltage values range from 0 to 5V for a 12-bit ADC. So range = 5V, resolution = 12 bits, that gives us:
Then you can multiply this sensitivity by your ADC value to get a voltage reading:
- For 0 LSB:
- For 4095 LSB:
- For 1024 LSB:
Credits
- Writer: Ousmane THIONGANE
- Lastest update: June 2025
- Reviewer: