![]() |
Previous Tutorial | Tutorial 24 | Next Tutorial | ![]() |
|||
STM32 ADC Read Example – 3 Methods To Read ADC | |||||||
STM32 Course Home Page 🏠 |
In this tutorial, we’ll discuss the different possible methods to Read Analog Input With STM32 ADC. The LAB we’ll implement is an LED dimmer using a potentiometer to analog input pin and PWM to control the LED brightness.
[toc]
Required Components For LABs
All the example code/LABs/projects in the course are going to be done using those boards below.
- Nucleo32-L432KC (ARM Cortex-M4 @ 80MHz) or (eBay)
- Blue Pill STM32-F103 (ARM Cortex-M3 @ 72MHz) or (eBay)
- ST-Link v2 Debugger or (eBay)
QTY | Component Name | 🛒 Amazon.com | 🛒 eBay.com |
2 | BreadBoard | Amazon | eBay |
1 | LEDs Kit | Amazon Amazon | eBay |
1 | Resistors Kit | Amazon Amazon | eBay |
1 | Capacitors Kit | Amazon Amazon | eBay & eBay |
2 | Jumper Wires Pack | Amazon Amazon | eBay & eBay |
1 | 9v Battery or DC Power Supply | Amazon Amazon Amazon | eBay |
1 | Micro USB Cable | Amazon | eBay |
1 | Push Buttons | Amazon Amazon | eBay |
1 | Potentiometers | Amazon Amazon | eBay |
★ Check The Full Course Complete Kit List
Some Extremely Useful Test Equipment For Troubleshooting:
- My Digital Storage Oscilloscope (DSO): Siglent SDS1104 (on Amazon.com) (on eBay)
- FeelTech DDS Function Generator: KKMoon FY6900 (on Amazon.com) (on eBay)
- Logic Analyzer (on Amazon.com) (on eBay)
Affiliate Disclosure: When you click on links in this section and make a purchase, this can result in this site earning a commission. Affiliate programs and affiliations include, but are not limited to, the eBay Partner Network (EPN) and Amazon.com.
STM32 ADC Read Methods
We can read actually configure the ADC module to take samples (conversions) in 3 different ways. Depending on the application types and requirements you can choose the best fit for you.
1 Polling
First of which is the polling method, in this method we’d start an ADC conversion and stop the CPU at this point to wait for the ADC conversion completion. Only after ADC conversion completion, the CPU can resume the main code execution.
2 Interrupts
The second method is by using interrupts, so we can trigger the ADC in order to start a conversion and the CPU continues executing the main code routine. Upon conversion completion, the ADC fires an interrupt and the CPU is notified so that it can switch the context to the ISR handler and save the ADC conversion results.
Despite being an efficient way, the interrupt method can add so much overhead to the CPU and cause very high CPU loading. Especially when you’re doing so many conversions per second.
3 DMA
And here comes the third method which is using the DMA unit that can directly transfer the ADC result from the peripheral to the memory in the manner that you want and program it to. All that being done without any CPU intervention and upon DMA transfers completion to maybe a 1kb in length buffer, it can notify the CPU to process the data of whatever.
STM32 ADC LED Dimmer – LAB19
LAB Number | 19 |
LAB Title | STM32 ADC Read LAB (LED Dimmer With Analog Pot.) |
- Set up a new project as usual with system clock @ 72MHz
- Set up An Analog Input Pin (Channel 7) In single Conversion Mode (The Pot. Pin)
- Set up timer2 in PWM mode with output on channel 1 (The LED Pin)
The Application will have 3 versions each does the same thing which is read the ADC result and move it to the timer CCR register which decides the PWM duty cycle percentage on the output LED pin.
Example 1, ADC is used in blocking mode (polling)
Example 2, ADC is used in non-blocking mode (interrupt)
Example 3, ADC is used in non-blocking mode (DMA)
STM32 ADC Polling Example
In this LAB, our goal is to build a system that initializes the ADC with an analog input pin (channel 7). And also configure a timer module to operate in PWM mode with output on channel 1 pin (LED pin). Therefore, we can start an ADC conversion and map the result to the PWM duty cycle and repeat the whole process over and over again.
And now, let’s build this system step-by-step
Step1: Open CubeMX & Create New Project
Step2: Choose The Target MCU & Double-Click Its Name
Step3: Configure The ADC1 Peripheral, Enable Channel7 & Set it to be triggered by software
You’ll actually find that the analog channel has these default configurations which happens to be ok for us in this LAB.
Step4: Configure Timer2 To Operate In PWM Mode With Output On CH1
Step5: Set The RCC External Clock Source
Step6: Go To The Clock Configuration
Step7: Set The System Clock To Be 72MHz
The ADC peripherals will be assigned a default clock of 12MHz that you can optionally increase to a maximum of 14MHz. But we won’t do that in this example.
Step8: Name & Generate The Project Initialization Code For CubeIDE or The IDE You’re Using
Here is The Application Code For This LAB (main.c)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#include "main.h" ADC_HandleTypeDef hadc1; TIM_HandleTypeDef htim2; void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_ADC1_Init(void); static void MX_TIM2_Init(void); int main(void) { uint16_t AD_RES = 0; HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_ADC1_Init(); MX_TIM2_Init(); HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); // Calibrate The ADC On Power-Up For Better Accuracy HAL_ADCEx_Calibration_Start(&hadc1); while (1) { // Start ADC Conversion HAL_ADC_Start(&hadc1); // Poll ADC1 Perihperal & TimeOut = 1mSec HAL_ADC_PollForConversion(&hadc1, 1); // Read The ADC Conversion Result & Map It To PWM DutyCycle AD_RES = HAL_ADC_GetValue(&hadc1); TIM2->CCR1 = (AD_RES<<4); HAL_Delay(1); } } |
Download The STM32 ADC Read Example Project LAB19
The LAB Connections
The Result For LAB Testing (video)
Another Testing That I’ve Also Done
I did configure a GPIO pin to be an output pin and programmed it to toggle HIGH at the beginning of ADC conversion and drive the pin LOW after the polling function immediately. This is in order to approximately measure the conversion time. It’s around 3.4μs.
STM32 ADC Interrupt Example
The Exact Same Steps As The Previous Example Except For Step 3. The ADC Configuration Will Be As Follows:
All ADC settings will remain the same but we’ll need to enable the interrupt from the NVIC controller tab.
Generate The Project & Open It In The CubeIDE
Here Is The Application Code Using The Interrupt Method
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
#include "main.h" uint16_t AD_RES = 0; ADC_HandleTypeDef hadc1; TIM_HandleTypeDef htim2; void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_ADC1_Init(void); static void MX_TIM2_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_ADC1_Init(); MX_TIM2_Init(); HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); // Calibrate The ADC On Power-Up For Better Accuracy HAL_ADCEx_Calibration_Start(&hadc1); while (1) { // Start ADC Conversion HAL_ADC_Start_IT(&hadc1); // Update The PWM Duty Cycle With Latest ADC Conversion Result TIM2->CCR1 = (AD_RES<<4); HAL_Delay(1); } } void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { // Read & Update The ADC Result AD_RES = HAL_ADC_GetValue(&hadc1); } |
STM32 ADC DMA Example
Also The Exact Same Steps As The First Example Except For Step 3. The ADC Configuration Will Be As Follows:
Everything in ADC configurations will be as default in normal mode. However, this time the ADC interrupts are not activated and the DMA is configured instead and DMA interrupt is enabled by default in the NVIC controller tab. The DMA configurations for the ADC will be as shown down below. Just add a DMA channel and that’s all.
Generate The Project & Open It In The CubeIDE
Here Is The Application Code Using The DMA Method
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#include "main.h" uint16_t AD_RES = 0; ADC_HandleTypeDef hadc1; DMA_HandleTypeDef hdma_adc1; TIM_HandleTypeDef htim2; void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_DMA_Init(void); static void MX_ADC1_Init(void); static void MX_TIM2_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_DMA_Init(); MX_ADC1_Init(); MX_TIM2_Init(); HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); // Calibrate The ADC On Power-Up For Better Accuracy HAL_ADCEx_Calibration_Start(&hadc1); while (1) { // Start ADC Conversion // Pass (The ADC Instance, Result Buffer Address, Buffer Length) HAL_ADC_Start_DMA(&hadc1, &AD_RES, 1); HAL_Delay(1); } } void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { // Conversion Complete & DMA Transfer Complete As Well // So The AD_RES Is Now Updated & Let's Move IT To The PWM CCR1 // Update The PWM Duty Cycle With Latest ADC Conversion Result TIM2->CCR1 = (AD_RES<<4); } |
Stay tuned for the upcoming tutorials and don’t forget to SHARE these tutorials. And consider SUPPORTING this work to keep publishing free content just like this!
![]() |
Previous Tutorial | Tutorial 24 | Next Tutorial | ![]() |
In Polling and Interrupt mode, i am getting the 12-bit output in STM32F429ZI, but in DMA mode i am getting 8-bit output(max value 255 at 3.3V). I checked the ADC DR register in debug mode, the value is 12-bit. I do not understand the reason. need help.
You need to set in the DMA configurations the word size for each transfer to be 2bytes i think this could be the reason
byte size is set to half word.
hello, I need to control the brightness of 4 LEDs with 4 potentiometers. What kind of way should I follow for this?