STM32 ADC | PART 3 Multi-Channel DMA (Normal Mode)

In this tutorial you will learn to configure STM32 ADC for multi-channel readings using DMA in normal mode. The tutorial Includes CubeMX setup, HAL code, and support for STM32F1/F4/H7 series. The project is available to download at the end of the post.

STM32 ADC Multi-Channel DMA

Recommended Resources:

This is the 3rd 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.

You should take a look at the following tutorials before continuing here:

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.

CONFIGURATION

We will connect 4 potentiometers to the 4 channels of the same ADC. As I mentioned we will setup the DMA in Normal mode, so it will only transfer the data once.

We will see the available configuration for all three MCUs, F103C8, F446RE and H750VB.

F103C8 Configuration

We will start with the F103C8. Below is the image showing the configuration for the it.

STM32F103 ADC Multi-channel Configuration

I have enabled 4 channels of the ADC1. This is where the 4 potentiometers will be connected to.

Since we are using 4 different Channels of ADC1, set the Number of Conversions (under ADC_Regular_ConversionMode) to 4. This will automatically enable the Scan Conversion Mode, which is necessary in case of Multiple Channels.

You can configure the Rank and Sampling Time for each channel. The Rank determines the sequence in which the channels will be converted.

The Continuous Conversion Mode should be disabled. Doing so will make sure the ADC does not start another conversion for all the channels automatically. This way the ADC will only convert the data when we want it to do.

In the DMA section, add the DMA request for the ADC1. Also make sure that the DMA is configured in the Normal Mode. In this mode the DMA will not transfer the data continuously, rather it will transfer once and then stop.

The F103C8 has 12 bit ADC resolution by default and we don’t have the option to configure it. Therefore the data width for the DMA must be set to Half-Word (16 bits).

F446RE Configuration

Below is the image showing the configuration for the F446RE.

STM32F4 ADC Multi-channel Configuration

I have enabled 4 channels of the ADC1. This is where the 4 potentiometers will be connected to.

Since we are using 4 different Channels of ADC1, set the Number of Conversions (under ADC_Regular_ConversionMode) to 4. This will automatically enable the Scan Conversion Mode, which is necessary in case of Multiple Channels.

You can configure the Rank and Sampling Time for each channel. The Rank determines the sequence in which the channels will be converted.

The Continuous Conversion Mode should be disabled. Doing so will make sure the ADC does not start another conversion for all the channels automatically. This way the ADC will only convert the data when we want it to do.

The DMA Continuous Request must also be disabled. This is to make sure that the DMA does not request the data continuously from the ADC. This mode should be kept disabled while using the DMA in Normal mode.

The End of Conversion Selection should be set at the end of all the conversions. Doing this will make sure that the Conversion complete interrupt in only triggered when all the 4 channels (one complete sequence) have been converted.

In the DMA section, add the DMA request for the ADC1. Also make sure that the DMA is configured in the Normal Mode. In this mode the DMA will not transfer the data continuously, rather it will transfer once and then stop. Set the data width as per the ADC Resolution. I have configured the ADC with 12bits resolution, therefore the data width is set to half word (16bits).

H750VB Configuration

Below is the image showing the configuration for the F446RE.

STM32H7 ADC Multi-channel Configuration

I have enabled 4 channels of the ADC1. This is where the 4 potentiometers will be connected to.

Since we are using 4 different Channels of ADC1, set the Number of Conversions (under ADC_Regular_ConversionMode) to 4. This will automatically enable the Scan Conversion Mode, which is necessary in case of Multiple Channels.

You can configure the Rank and Sampling Time for each channel. The Rank determines the sequence in which the channels will be converted.

The Continuous Conversion Mode should be disabled. Doing so will make sure the ADC does not start another conversion for all the channels automatically. This way the ADC will only convert the data when we want it to do.

The Conversion Data Management Mode should be set to DMA One Shot. This is to make sure that the DMA does not request the data continuously from the ADC. This mode should be used while using the DMA in Normal mode.

The End of Conversion Selection should be set at the end of sequence of conversion. Doing this will make sure that the Conversion complete interrupt in only triggered when all the 4 channels (one complete sequence) have been converted.

In the DMA section, add the DMA request for the ADC1. Also make sure that the DMA is configured in the Normal Mode. In this mode the DMA will not transfer the data continuously, rather it will transfer once and then stop. Set the data width as per the ADC Resolution. I have configured the ADC resolution of 16bits, therefore the data width is set to Half-Word (16bits).

Cortex M7 Related

In this section we will cover the configuration and code exclusively needed for the cortex M7 devices.

We know that as per ST’s recommendation, we should use the Instruction and Data cache to improve the system performance. But if the cache are enabled, the DMA will always have the cache coherency issue. This is why we will use the Memory Processing Unit (MPU) of the cortex M7 to deal with the cache coherency.

Below is the image showing the MPU configuration in the cortex M7 Devices.

Cortex-M7 DMA MPU Configuration

As you can see in the image above, I have enabled both Instruction and Data Cache (ICache & DCache).

We also need to enable the MPU in the MPU_PRIVILEGED_DEFAULT mode. Here I am choosing the Base Address region 0x30000000, this is the region for the RAM_D2 and we will relocate our buffer in this region.

  • Here I have selected the 32 Bytes region, since it’s the least size available. The ADC Buffer is going to be 20 bytes in size.
  • The rest of the configuration is to set the region as non-cacheable region. This would prevent the cache coherency issue between the CPU and the DMA.

In the main file, we need to relocate the ADC_VAL array to the RAM_D2 Region, 0x30000000. We can do this by using the section attribute as shown below.

__attribute__((section(".adcarray"))) uint16_t ADC_VAL[4];

Here I am relocating the ADC_VAL array to the section “.adcarray“. The section (.adcarray) will be defined in the flash script file as shown below.

  .mysection (NOLOAD):
  {
    . = ABSOLUTE(0x30000000);
    *(.adcarray)
  } >RAM_D2

As you can see above, the section (.adcarray) is linked to the region RAM_D2 (0x30000000).

The rest of the code remains the same as we used in the DMA section.

WIRING DIAGRAM

Below is the image showing the connection between the potentiometers and the H750.

Potentiometers connected to STM32

As you can see in the image above, the 4 potentiometers are connected to the 4 pins of the MCU. These 4 pins represents 4 channels of the ADC. The potentiometers are connected to the 3.3V from the MCU.

THE CODE

The code will remain the same for all the STM32 MCUs. If there are any changes, I will specify in this section itself.

Definitions

Let’s define some variables, which we will use in the main function later.

uint16_t ADC_VAL[4];
//__attribute__((section(".adcarray"))) uint16_t ADC_VAL[4];  // cortex M7 only
int isADCFinished = 0;
int count = 0;

ADC_VAL is an array of 4 elements of 16 bit in size. We will use this array to store the converted ADC Value. Even if you are using 10bits, 12bits or 14bits resolution the ADC_VAL will still be defined as a 16 bit variable.

The variable isADCFinished will be used to determine whether the ADC conversion has been finished.

The count variable will keep track if the rest of the while loop is working fine.

The main function

Below is the main function.

int main ()
{
  ....
  ....
  HAL_ADC_Start_DMA(&hadc1, ADC_VAL, 4);
  while (1)
  {
    count++;
    HAL_Delay(500);

    if (isADCFinished == 1)
    {
	  //process data
	  isADCFinished = 0;
	  HAL_ADC_Start_DMA(&hadc1, ADC_VAL, 4);
    }
  }
}

Inside the main function, we will start the ADC in the DMA mode. The parameter ADC_VAL is the array where we want to store the converted data, and 4 is the number of conversions we want the DMA to transfer.

Once the DMA finishes the transfer of the converted data from all the channels, an interrupt will trigger and the Conversion complete callback will be called.

Conversion Complete Callback

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
	isADCFinished = 1;
}

Inside the callback we will set the variable isADCFinished. This will indicate that the DMA has finished the transfer and we can now process the data anywhere in the code.

The DMA will stop after transferring the data for all the channel once. Therefore we need to start the ADC in DMA mode whenever we want to convert the ADC data.

Inside the while loop we will check the state of the variable isADCFinished. If this variable is set, we will process the converted data and then start the ADC in DMA mode again. Here I am starting the ADC again, just to show the working for the sake of the tutorial. You can start the ADC when you want to fetch the converted data from the ADC.

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.

You May Also Like..

Subscribe
Notify of

0 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments

🛈 Advertising Disclosure

This website relies on advertisements as its main source of revenue to support the creation of free, high-quality content.
If you enjoy our work and would like to help us continue, please consider disabling your ad blocker while browsing here.

Your support truly makes a difference — thank you!

Share this Article

Recent Posts

Join Our Community

Weekly Newsletter

Subscribe to our newsletter to get our news