Learn how to use STM32 ADC in differential mode to measure voltage differences between two inputs. This tutorial Covers biasing, common‑mode range, CubeMX & HAL code. The project is available to download at the end of this post.

Recommended Resources:
This is the 11th tutorial in the STM32 ADC series. In the previous tutorials we covered how to configure the ADC in STM32 F1, F4 and F7 series and how to use it in the single channel polling, interrupt and DMA modes to read the potentiometer data. We have also covered the Multiple channels in DMA Normal Mode, Circular Mode and Multiple Channels without DMA.
You Do not need to go through the previous tutorials in order to understand this one.
Introducing STM32 ADC Peripheral
The Analog-to-Digital Converter (ADC) in STM32 microcontrollers is a powerful peripheral that allows the conversion of analog input signals into digital values. This capability enables STM32 devices to interface seamlessly with real-world sensors and analog inputs, such as temperature sensors, light sensors, potentiometers, and more. The ADC module is highly configurable and supports a range of resolutions and modes, making it suitable for both basic and advanced embedded applications.
Whether you’re developing a battery-powered IoT device or a high-speed data acquisition system, the STM32 ADC provides flexible configuration options, precision control, and efficient performance.
Here are some important features of STM32 ADC:
- Multi-Channel Support – Capable of reading from multiple analog inputs using internal channel scanning, reducing the need for external hardware.
- High Resolution Options – Offers selectable resolutions (6-, 8-, 10-, or 12-bit), allowing a balance between accuracy and conversion speed.
- Multiple Conversion Modes – Supports single, continuous, scan, and discontinuous modes to suit different application needs.
- Flexible Triggering – Conversion can be started via software or automatically triggered by hardware events like timers or external interrupts.
- Low Power Consumption – Designed with power-saving features, ideal for energy-efficient and battery-operated systems.
What is Differential ADC in STM32?
Differential ADCs are generally used in applications requiring high precision, noise reduction, and increased dynamic range. In this tutorial we will specifically focus on just measuring the voltage difference between the two differential inputs. The ADC data will be based on this differential voltage, which we will later convert into the voltage.
Not all the STM32 devices support differential ADC. I am going to use the STM32H7 based custom development board.
As mentioned in the reference manual both the differential inputs should be biased at Vref/2. The Vref is a custom reference voltage which can be provided externally, but the hardware on the board should be configured accordingly. By default the Vref is connected to the VDDA (& VCC), therefore the Vref is 3.3V.
Biasing means that instead of fluctuating around 0V, the signals should be centred around 1.65V.
We should also take care of the Common mode voltage (CMV). This Common-mode voltage is the average voltage of two input signals in a differential system. This voltage only has a small range in STM32. You can see it in the image from datasheet of the device.
You can see the common mode voltage varies by only 10% from Vref/2 voltage. So if the Vref is at 3.3V, the common mode voltage can vary from around 1.48 V to 1.82V.
When choosing the input voltages on both the differential pairs make sure the average of both voltages lies within this range. It does not mean that it will not work outside this range rather the result outside this range is unpredictable. In certain cases it might work while in other cases it won’t.
The ADC will convert the voltage difference between both the input pins as per the formula shown below.
- If the VINN = 0 and VINP = VREF, the ADC value will be equal to the MAXIMUM.
- Whereas when VINN = VREF and VINP = 0, the ADC value will be equal to the 0.
- When VINN = VINP, the ADC value will be exactly HALF the MAXIMUM.
Below is the image showing the range of ADC value based on the differential inputs. The VDIFF is the voltage difference between the input signals.
We will write our code in such a way that the ADC MAXIMUM (65535) indicates a +3.3V while the ADC MINIMUM (0) indicates a -3.3V. Also the ADC MAX/2 (32768) will indicate 0V.
CUBEMX CONFIGURATION
Below is the image showing the ADC configuration.
I have enabled ADC Channel 3 in the differential Mode. You can see the pin PA6 is configured as the INP (+ve Input) pin whereas the pin PA7 is configured as the INN (-ve Input) pin.
The ADC is configured with continuous conversion mode enabled, so that the next conversion can start automatically as soon as the previous one is finished. I have also enabled the interrupt for ADC.
WIRING DIAGRAM
The Connection for this project is shown in the image below.
I am using Active Pro Debugger to provide variable voltages to the ADC input. The A1 pin is connected to the PA6 (+ve Input) whereas the A0 pin is connected to the PA7 (-ve Input).
THE CODE
uint16_t ADC_VAL;
int voltage;
The variable ADC_VAL will be used to store the Raw ADC value. This value is generated by the ADC based on the difference in voltage between the inputs. The variable voltage will store the voltage data based on the Raw value.
Inside the main function we will start the ADC in interrupt mode. Once the conversion is finished, an interrupt will trigger and the ConvCpltCallback will be called. Inside this callback we will simply set the variable isDone. The data will be processed in the while loop.
int isDone =0;
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
isDone = 1;
}
Below is the main function.
int main ()
{
....
....
HAL_ADC_Start_IT(&hadc1);
while (1)
{
if (isDone)
{
ADC_VAL = HAL_ADC_GetValue(&hadc1);
voltage = (3300*2*ADC_VAL/65535)-3300;
isDone = 0;
}
HAL_Delay(250);
}
}
Inside the while loop we will check if the variable isDone is set. If it is, we will read the Raw ADC value.
Then convert this Raw value to the voltage. By default the Vref is connected to VDDA (&VCC), therefore the Vref = 3.3V. Hence the value 3300 is used here. We want to convert the RAW ADC value to the voltage ranging from +Vref to -Vref, hence this formula is used baed on the examples provided by ST.
RESULT
Below are the images showing the output of the above code. When the +ve Input is at 0V and -ve Input is at 3.3V, the average input voltage is 1.5V, which is within the Common Mode Voltage (CMV ) range. The Raw ADC value is near 0 and the converted voltage (Difference between INP and INN) is -3.3V.
When the +ve Input is at 3.3V and -ve Input is at 0V, the average input voltage is 1.5V, which is within the Common Mode Voltage (CMV ) range. The Raw ADC value is 65535 and the converted voltage (Difference between INP and INN) is 3.3V.
When both the +ve Input and -ve Input are equal at 1.6V, the average input voltage is 1.6V, which is within the Common Mode Voltage (CMV ) range. The Raw ADC value is near Half the MAX and the converted voltage (Difference between INP and INN) is 0V.
When the +ve Input is at 3V and -ve Input is at 1V, the average input voltage is 2.5V, which is far outside the Common Mode Voltage (CMV) range. The ADC behaviour is unpredictable at this point. You can see in the image below the converted voltage is wrong.
Hence while designing the differential ADC, we must take care that the average input voltage of the two inputs must lie within the CMV range.
VIDEO TUTORIAL
You can check the video to see the complete explanation and working of this project.
Check out the Video Below
PROJECT DOWNLOAD
Info
You can help with the development by DONATING Below.
To download the project, click the DOWNLOAD button.
Hi, can we use DACA