STM32 ADC Continuous Conversion Mode (DMA, Interrupt, Poll) – Single Channel

In this tutorial, we’ll discuss the STM32 ADC Continuous Conversion Mode (Single-Channel) with DMA, Interrupt, and Polling techniques for reading the ADC conversion results. You’ll learn how STM32 ADC Continuous Conversion mode works and how to use it to read a single channel and get the conversion data using Polling, Interrupt, and DMA using the STM32 HAL API functions.

The practical example we’ll implement in this tutorial is an STM32 LED dimmer using a potentiometer to an analog input pin and PWM to control the LED brightness. We’ll read the analog input pin (channel) in continuous-conversion mode using the DMA, Interrupt, and Polling methods. Without further ado, let’s get right into it!

Table of Contents

  1. STM32 ADC Continuous Conversion Mode (Single-Channel)
  2. STM32 ADC Single-Channel Continuous Mode Example
  3. STM32 STM32 ADC Continuous Conversion Mode Polling (Single-Channel)
  4. STM32 ADC Continuous-Conversion Interrupt Example (Single-Channel)
  5. STM32 ADC Continuous-Conversion DMA Example (Single-Channel)
  6. Wrap Up

STM32 ADC Continuous Conversion Mode (Single-Channel)

In a previous tutorial, we’ve discussed the STM32 ADC Single-Channel Single-Conversion Mode. It was very easy to set up and very convenient for applications where you need to run the ADC in a one-shot to get a single-conversion after which the ADC conversions will be disabled again until you manually trigger a new single-conversion operation. It didn’t matter how we did the ADC result reading process (using Polling, Interrupt, or DMA), the final result was exactly the same.

However, in this tutorial, we’ll explore the STM32 ADC Continuous-Conversion Mode for single-channel. In this mode, the ADC will complete a single-channel conversion and immediately trigger itself automatically to start a new conversion and keep repeating, hence the name “continuous conversion mode”. As it doesn’t need to be repeatedly initiated, you just initiate it once and it’ll keep going until you manually stop it in software.

The STM32 ADC Continuous-Conversion Mode can also be used with Polling, Interrupt, or DMA to get the ADC reading results as soon as the conversion is completed. And that’s what we’ll do in the practical example project hereafter in this tutorial.

STM32-ADC-Continuous-Conversion-Mode-DMA-Interrupt-Polling-Single-Channel

1. STM32 ADC Continuous Conversion Polling

In the polling method, the ADC is started in software at least once. We’ll poll for the conversion completion anywhere in the code where the result is needed. This is obviously a blocking way to read the ADC as the CPU will be busy waiting for the conversion completion event.

However, we can do the polling as often as we wish, so the CPU blockage will at least be somehow predictable compared to the interrupt or DMA methods that we’ll discuss hereafter.

2. STM32 ADC Continuous Conversion Interrupt

The second method is by using interrupts, so we can trigger the ADC to start the continuous 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. In continuous-conversion mode, the ADC will be doing conversions back-to-back with no stop causing thousands and thousands of interrupts per second which unnecessarily significantly increases the CPU load.

3. STM32 ADC Continuous Conversion 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 is done without any CPU intervention and upon DMA transfer completion, it can notify the CPU to process the data or whatever.

Using a short buffer length while enabling the DMA interrupt will also cause unwanted CPU load exactly like what we’ve described earlier in the interrupt-based reading method. The DMA, if properly set up, can alleviate a lot of unintended CPU load that could have been increased otherwise.


STM32 ADC Single-Channel Continuous Mode Example

In this example project, we’ll create an STM32 LED Dimmer using ADC & PWM to read an analog input from a potentiometer to control the brightness of a PWM output going to an LED. This demo will run the STM32 ADC in single-channel continuous-conversion mode using 3 different ADC reading techniques (DMA, Interrupt, and Polling).

  • Set up a new project as usual with system clock @ 72MHz
  • Set up An Analog Input Pin (Channel 7) In Continuous-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 to 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 Continuous-Conversion is used in blocking mode (polling)

Example 2, ADC Continuous-Conversion is used in non-blocking mode (interrupt)

Example 3, ADC Continuous-Conversion is used in non-blocking mode (DMA)


STM32 STM32 ADC Continuous Conversion Mode Polling (Single-Channel)

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, get & 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

Step #1

Open STM32CubeMX, create a new project, and select the target microcontroller.

Step #2

Configure The ADC1 Peripheral, Enable Channel-7. You’ll find that the analog channel has these default configurations which happens to be ok for us in this example project.

STM32 ADC Continuous Conversion Mode Single-Channel CubeMX

Step #3

Configure Timer2 To Operate In PWM Mode With Output On CH1.

STM32 ADC Read Example HAL Code

Step #4

Go to the RCC clock configuration page and enable the HSE external crystal oscillator input.

STM32 RCC External Clock Selection CubeMX

Step #5

Go to the clock configurations page, and select the HSE as a clock source, PLL output, and type in 72MHz for the desired output system frequency. Hit the “ Enter” key, and let the application solve for the required PLL dividers/multipliers to achieve the desired clock rate.

STM32-Blue-Pill-Proteus-Library-Simulation-Clock-Configuration

Step #6

Name & Generate The Project Initialization Code For CubeIDE or The IDE You’re Using.

STM32 ADC Continuous-Conversion Single-Channel (Polling) Example Code

Here is The Application Code For This LAB (main.c)

Wiring

STM32 ADC DMA Example HAL Code And CubeMX Tutorial

STM32 ADC Continuous-Conversion Single-Channel Example Testing


STM32 ADC Continuous-Conversion Interrupt Example (Single-Channel)

The Exact Same Steps As The Previous Example Except For Step 2. 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.

STM32 ADC Example HAL Code And CubeMX Configurations Tutorial

Generate The Project & Open It In The CubeIDE

STM32 ADC Continuous-Conversion Single-Channel (Interrupt) Example Code

Here Is The Application Code Using The Interrupt Method

ADC Continuous-Conversion Interrupt Rate Testing

I’ve configured a GPIO pin to be an output pin and programmed it to toggle HIGH at the beginning of the ADC interrupt handler and drive the pin LOW at the end of the ISR handler. This will give us the rate at which the ADC interrupt is fired, which came out to around 217.4k.

STM32-ADC-Continuous-Conversion-Interrupt-Rate-Single-Channel

❕ Note

The interrupt rate measurement shown above confirms that the CPU is having such a bad time handling 217,400 interrupts each and every single second! This is insane, given that the CPU has to also handle other tasks, interrupts, and whatever. The CPU load is almost 100% and nothing can be predictable in this situation.


STM32 ADC Continuous-Conversion DMA Example (Single-Channel)

Also The Exact Same Steps As The First Example Except For Step 2. The ADC Configuration Will Be As Follows:

Everything in ADC configurations will be as default in normal mode. However, this time the STM32 ADC interrupts are not activated and the DMA is configured instead, and the DMA interrupt is enabled by default in the NVIC controller tab. The STM32 DMA configurations for the ADC will be as shown below.

Generate The Project & Open It In The CubeIDE

STM32 ADC Continuous-Conversion Single-Channel (DMA) Example Code

Here Is The Application Code Using The DMA Method

ADC Continuous-Conversion DMA Code Explanation

Unlike the Interrupt & Polling code examples, the DMA example needs a bit more elaboration so you can easily get what’s going on here. First of all, we’ve configured the DMA channel to operate in circular mode instead of the normal mode. The reason behind this is that in normal mode, the DMA will do the required number of transfers, fire an interrupt, and disable itself.

However, in circular mode, the DMA will roll back to the first address in the buffer and keep triggering itself over and over again until we programmatically stop it in the software. This is very similar to the ADC continuous-conversion mode, right? That’s why we’re using the circular mode in conjunction with the ADC continuous-conversion mode.

Next, we’ve configured the DMA transfer data width to “Half-Word” which is 16 bits. Given that the ADC result is only 12 bits, one DMA transfer will be sufficient. However, the HAL_ADC_Start_DMA() function expects a pointer to uint32_t for the data buffer. That’s why I’ve changed the AD_RES variable to u32 type and commanded the DMA to do 2x Half-Word transfers.

Therefore, when the DMA transfer is completed, we should expect to see that there are two ADC conversions have occurred and the results for both conversions are stored in the two halfs of the u32 AD_RES variable. Here is a screenshot of the live test for the AD_RES variable’s value. The two ADC conversions are almost identical and the results are allocating the high half-word and the low half-word of the u32 variable as you can see.

ADC-Continuous-Conversion-DMA-Read-Example
u32 AD_RES = 0x(0964 0964)

To extract the 12-bit ADC results from the buffer, I’ve discarded the high half-word by casting to u16, then I’ve left-shifted the resulting 12-bit ADC value to scale it up to the 16-bit expected as a PWM duty cycle, and that’s all about it.

ADC Continuous-Conversion DMA Interrupt Rate Testing

I’ve configured a GPIO pin to be an output pin and programmed it to toggle HIGH at the beginning of the DMA transfer completion interrupt handler and drive the pin LOW at the end of the ISR handler. This will give us the rate at which the DMA interrupt is fired, which came out to around 61.73k.

❕ Note

In this example demo, I’ve enabled the DMA interrupt to give you a hint of how it can be incorporated into the final application. However, I’d advise against using it in this application as it, unnecessarily, adds so much CPU load. You’d be better off disabling the DMA interrupt and using the global AD_RES buffer to read the last ADC result and map that to the PWM duty cycle. Which can be done in the main while loop, in a periodic timer-based interrupt, or whatever.

To enable the DMA interrupt, you need to have a sufficiently large buffer so the interrupts are less frequent to keep the system as efficient as possible. As long as the data is not urgently needed for real-time processing, things can be asynchronously accessed in a periodic way to keep things as predictable as possible.


Required Parts For STM32 Examples

All the example Code/LABs/Projects in this STM32 Series of Tutorials are done using the Dev boards & Electronic Parts Below:

QTY.Component NameAmazon.comAliExpresseBay
1STM32-F103 BluePill Board (ARM Cortex-M3 @ 72MHz)AmazonAliExpresseBay
1Nucleo-L432KC (ARM Cortex-M4 @ 80MHz)AmazonAliExpresseBay
1ST-Link V2 DebuggerAmazonAliExpresseBay
2BreadBoardAmazonAliExpresseBay
1LEDs KitAmazonAmazonAliExpresseBay
1Resistors KitAmazonAmazonAliExpresseBay
1Capacitors KitAmazonAmazonAliExpress & AliExpresseBay & eBay
1Jumper Wires PackAmazonAmazonAliExpress & AliExpresseBay & eBay
1Push ButtonsAmazonAmazonAliExpresseBay
1PotentiometersAmazonAliExpresseBay
1Micro USB CableAmazonAliExpresseBay

★ Check The Links Below For The Full Course Kit List & LAB Test Equipment Required For Debugging ★

Download Attachments

You can download all attachment files for this Article/Tutorial (project files, schematics, code, etc..) using the link below. Please consider supporting our work through the various support options listed in the link down below. Every small donation helps to keep this website up and running and ultimately supports the whole community.


Wrap Up

In conclusion, we’ve explored the various methods of reading the STM32 ADC single-channel in continuous-conversion mode using: (DMA, Interrupt, Polling). You can build on top of the examples provided in this tutorial and/or explore the other parts of the STM32 ADC tutorials series for more information about the other STM32 ADC operating modes and conversion schemes.

Be advised that you need to rethink enabling the ADC interrupts while using the continuous conversion mode and also to use a sufficiently large DMA buffer in case you’d like to use the DMA instead. A small DMA buffer will result in excessive interrupts/second making the whole solution less efficient.

This Tutorial is Part of The Following Multi-Part Tutorial Series:

Share This Page With Your Network!
Join Our +25,000 Newsletter Subscribers!

Stay Updated With All New Content Releases. You Also Get Occasional FREE Coupon Codes For Courses & Other Stuff!

Photo of author
Author
Khaled Magdy
Embedded systems engineer with several years of experience in embedded software and hardware design. I work as an embedded SW engineer in the Automotive & e-Mobility industry. However, I still do Hardware design and SW development for DSP, Control Systems, Robotics, AI/ML, and other fields I'm passionate about.
I love reading, writing, creating projects, and teaching. A reader by day and a writer by night, it's my lifestyle. I believe that the combination of brilliant minds, bold ideas, and a complete disregard for what is possible, can and will change the world! I will be there when it happens, will you?

Leave a Comment