I have already written a tutorial about ADC in STM32 and how to use different methods to get values from sensors using ADC. But the CubeMx is upgrading very often recently and they are changing things a lot. So today I am writing this updated method of reading ADC values using all possible ways using latest CubeMx at the time of writing.
I am going to read single channel first using all three methods i.e. Pollforconversion, interrupt, and DMA and than I will read multiple channels using the possible ways to do so.
Before we start conversions, Let’s introduce you to some of the concepts we are going to use in ADC
Single conversion mode
In Single conversion mode the ADC does one conversion and than stops. You can select single conversion in CubeMx by setting continuousconversionmode as DISABLED.
Continuous conversion mode
In continuous conversion mode ADC starts another conversion as soon as it finishes one. This method is more efficient if you want to convert continuously. You can select continuous conversion by setting continuousconversionmode as ENABLED.
This mode is used to scan a group of analog channels. This mode will be automatically selected if you are doing conversions for more than 1 channel. A single conversion is performed for each channel of the group. After each end of conversion the next channel of the group is converted automatically. If the continuousconversionmode is ENABLED, conversion does not stop at the last selected group channel but continues again from the first selected group channel. When using scan mode, DMA bit must be set and the direct memory access controller is used to transfer the converted data of regular group channels to SRAM after each update of the ADC_DR register.
According to the datasheet, the total conversion time is calculated as follows:
Tconv = Sampling time + 12.5 cycles
Example: With an ADCCLK = 14 MHz and a sampling time of 1.5 cycles: Tconv = 1.5 + 12.5 = 14 cycles = (14cycles/14MHz) = 1 µs
Single channel using Pollforconversion
Pollforconversion is the easiest way to get the ADC value. We have to keep monitoring for the conversion in the blocking mode using HAL_ADC_PollForConversion(ADC_HandleTypeDef* hadc, uint32_t Timeout)
Once the conversion is complete, we can read the value using HAL_ADC_GetValue(ADC_HandleTypeDef* hadc)
The setup of CubeMx will be as shown in the picture below. I am using only 1 channel and the continuous conversion is DISABLED. Also the sampling time is 13.5 cycles which is around 1 us, as the ADC clock is 12MHz.
the code is as follows
Single channel using Interrupt
Pollforconversion uses blocking mode to monitor for the conversion and is not an efficient way to use ADC. Using Interrupt is an alternate way to do so and let’s see How to use it
First we need to enable continuous conversion mode otherwise after single conversion, ADC will stop and we have to restart it. Also make sure you enable the interrupt in the NVIC tab as shown below
First we have to start the ADC in the interrupt mode by using the function below
Now whenever the conversion is complete, a callback function is called and we are going to write the rest of the code inside it
HAL_ADC_GetValue(ADC_HandleTypeDef* hadc) reads the vaue from ADC and stores it in the variable adc_val.
Also note that if the continuousconversion mode is disabled, ADC will stop here and we have to again start the it in interrupt mode.
Single channel using DMA
DMA is another way of getting data from ADC. Like interrupt mode, DMA method also works in a non-blocking mode. That means we can use the rest of the program while the ADC would work in the background and when needed, we can get the value.
In DMA method, whenever the conversion is complete, the ADC values are written to the buffer which we can read anytime we want. The setup for the DMA is shown below
Circular DMA mode will ensure that the new value will override the old one and this way it can continue to work as a circular buffer.
Make sure the data width is selected as word. Because the resolution is 12 bit and only word can store the value here.
To start the ADC in DMA mode we have to use the function below
HAL_ADC_Start_DMA (&hadc1, &buffer, 1);
This will start the ADC1 in DMA mode and the converted value will be stored in the buffer.
We can read the buffer at any point in the code and get the ADC value
adc_val = buffer;
You can buy me a
coffee sensor 🙂
download the CODE below