HomeSTM32STM32 ADC SeriesSTM32 ADC Part 3 – Multiple Channels with DMA Normal Mode

STM32 ADC Multiple Channels Using DMA Normal Mode

In this part of the STM32 ADC tutorial series, we will learn how to read data from multiple channels using DMA Normal Mode. Unlike circular DMA, Normal Mode transfers the converted values once and then stops, giving you full control over when the next conversion should happen. This method is useful when you want to collect a batch of analog data at specific intervals. We will configure the ADC in CubeMX, write the code, and test the results on STM32 boards.

This is the 3rd tutorial in the STM32 ADC series. I am going to use the STM32H750 based development board for this series because it has advanced ADC features. But I will also cover the configuration changes required for some of the popular series that includes STM32F103C8 and STM32F446RE.
This tutorial is a continuation of the previous one, so the configuration remains the same. This is the continuation of PART1 and PART2, so you must go read them first.

STM32 ADC Multiple Channels DMA Normal Mode Video Tutorial

This tutorial explains how to configure STM32 ADC to read multiple channels using DMA Normal Mode. You’ll learn how to set the number of conversions, enable scan mode, configure DMA in Normal mode, and handle the results in code. To make things easier, I’ve created a complete video walkthrough that demonstrates the configuration, coding, and output. Follow the written steps here while watching the video for a clearer understanding.

Watch the Video

Hardware Required for STM32 ADC Tutorial

To follow this tutorial, you will need the following hardware:

  • STM32F446 Dev Board (e.g., STM32F4, STM32F7, STM32H7, or any board with ADC support)
  • Potentiometer (to provide an analog input signal)
  • Breadboard (for easy circuit connections)
  • Jumper Wires (male-to-male for connections)
  • USB Cable (to connect the STM32 board to the PC for programming and debugging)

STM32 ADC Multiple Channels Wiring Diagram

The image below shows the connection between STM32H750 and the multiple Potentiometers.

Multiple Potentiometers connected to different ADC channels in STM32.

As shown in the image above, four potentiometers are connected to four pins of the MCU. All potentiometers share the same 3.3V supply and GND from the MCU. Only the wiper (signal pin) of each potentiometer is connected to a unique ADC-capable pin on the STM32.

The pin connection is described in the table below.

PotentiometerPin 1 (VCC)Pin 2 (Signal / Wiper)Pin 3 (GND)MCU Pin (ADC Channel)
Potentiometer 13.3VMiddle Pin → Pink WireGNDPC5 (ADC Channel 15)
Potentiometer 23.3VMiddle Pin → Orange WireGNDPC4 (ADC Channel 14)
Potentiometer 33.3VMiddle Pin → Blue WireGNDPA6 (ADC Channel 6)
Potentiometer 43.3VMiddle Pin → Green WireGNDPA6 (ADC Channel 6)

What is Multi-Channel DMA Normal Mode?

In STM32, the ADC (Analog-to-Digital Converter) can sample multiple channels (e.g., multiple potentiometers or sensors). To efficiently move the conversion results from the ADC peripheral to memory, we can use DMA (Direct Memory Access).

  • In Multi-Channel DMA Normal Mode, the ADC samples several channels in sequence, and the DMA transfers each conversion result into memory (usually an array).
  • Once the DMA converts and transfers all the configured channels, it stops automatically.
  • At this point, the CPU processes the results, and if more conversions are needed, you must restart the DMA manually.

Why use Normal Mode vs Circular / Interrupt?

  • Normal Mode is straightforward and efficient for one-time or periodic data captures. It prevents continuous overwriting of the buffer, giving the CPU a clear dataset to work with.
  • Circular Mode, on the other hand, continuously refreshes the buffer with new ADC data. It’s useful when you need constant, real-time sampling without manually restarting DMA.
  • Interrupt Mode doesn’t use DMA; instead, it relies on the CPU to handle each conversion. While simple, it adds CPU load and is less efficient for multiple channels.

Advantages of Normal Mode

  • Easy to implement.
  • Suitable for periodic or triggered sampling.
  • Prevents accidental data overwrite.

Limitations of Normal Mode

  • DMA stops automatically after one sequence of conversions.
  • Requires the CPU (or software trigger) to restart the DMA if continuous sampling is the need.
  • Not suitable for applications that require uninterrupted, high-speed, real-time data streams.

Best Use Cases for Normal Mode

  • When you only need a snapshot of multiple ADC channels (e.g., reading several sensors once per second).
  • Applications where data changes slowly, and continuous sampling is unnecessary.
  • Situations where you want to control exactly when conversions happen (e.g., triggered measurements instead of constant monitoring).

ADC Multi-Channel Modes Comparison

ModeHow it WorksAdvantagesLimitationsBest Use Cases
DMA NormalADC samples multiple channels → DMA stores results in memory → stops automatically after one sequence.– Simple and efficient for batch conversions- No buffer overwrite- Low CPU load– DMA must be restarted for next sequence- Not continuousSnapshot readings of multiple sensors, periodic measurements
DMA CircularADC samples continuously → DMA keeps overwriting buffer in a loop.– Continuous, real-time data- Low CPU load- No manual restart needed– Data can be overwritten if CPU doesn’t read fast enough- Harder to capture “exact” snapshotContinuous sensor monitoring, audio processing, streaming data
InterruptADC signals CPU after each conversion → CPU reads data manually.– Easy to implement- Fine control over each conversion– High CPU overhead- Not efficient for multiple channels- Slower response at high sampling ratesSimple applications, single-channel ADC, low-speed sensing

CubeMX Configuration for Multiple Channels (Normal DMA)

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.

STM32F103C8 ADC DMA Configuration

The image below shows the configuration for STM32F103C8 ADC multiple channels using DMA Normal Mode.

STM32F103 ADC Multiple channel  DMA Configuration (Normal Mode)

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 convert.

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 to configure the DMA in the Normal Mode. In this mode the DMA will not transfer the data continuously, rather it will transfer once and then stop.

The STM32F103C8 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).


STM32F446 ADC DMA Configuration

The image below shows the configuration for STM32F446RE ADC multiple channels using DMA Normal Mode.

STM32F4 ADC Multiple channel  DMA Configuration (Normal Mode)

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.

You should set the End of Conversion Selection at the end of all conversions. This ensures that the Conversion Complete interrupt triggers only after all four 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).


STM32H7 ADC DMA Configuration

The image below shows the configuration for STM32F446RE ADC multiple channels using DMA Normal Mode.

STM32H7 ADC Multiple channel  DMA Configuration (Normal Mode)

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 Specific Configuration for ADC with DMA

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.

STM32 CortexM7 MPU Configuration for ADC DMA

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.

STM32 HAL Code for ADC Multiple Channels

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 a 4-element array, with each element 16 bits in size. We use this array to store the converted ADC values. Even when using 10-bit, 12-bit, or 14-bit resolution, ADC_VAL remains defined as a 16-bit variable.

The variable isADCFinished will determine whether the ADC conversion is 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 transferring the converted data from all channels, it triggers an interrupt, which calls the Conversion Complete callback.


ADC 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.

Result of ADC Multiple Channels DMA

The video below shows the ADC values obtained by rotating the potentiometers, in the STM32CubeIDE debugger.

As you can see in the video, the element of the ADC_VAL buffer is changing with the rotation of the respective potentiometer. The data is not updating every 500ms.

The DMA transfers the data when the while loop calls it (every 500 ms), and it works fine even with the cache enabled. There is no issue of the cache coherency, otherwise the data in the ADC_VAL array would not update at all.

Conclusion

In this tutorial, we covered ADC with multiple channels using DMA Normal Mode. While this mode works well for one-time data acquisition, it requires manually restarting the DMA for continuous sampling.

In the next part (Part 4), we will move on to DMA Circular Mode for multiple channels. This mode allows continuous data acquisition without restarting DMA, making it highly efficient for real-time applications such as sensor monitoring, data logging, and control systems.

Next Tutorials in the STM32 ADC Series

1 2

PROJECT DOWNLOAD

Info

You can help with the development by DONATING Below.
To download the project, click the DOWNLOAD button.

FAQs for STM32 ADC Multiple Channels Normal DMA

Subscribe
Notify of

0 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments