Skip to main content
Visitor II
October 28, 2025
Question

Setting up ADC on Regular Triple Simultaneous mode with DMA on STM32F429

  • October 28, 2025
  • 6 replies
  • 761 views

Hi, I'm having really a hard time setting up my ADCs to work with DMA using the STM32F429I-DISC1. My objective:

 

have a clock source from a Timer at a given frequency as trigger to the ADCs, each ADC would then read a specific pin (let's say ADC1-> pin_x; ADC2->pin_y and ADC3->pin_z). Since nothing is working regardless of the configuration I set, I'm trying with very big time intervals to avoid busy bus and so on. So I've set the TIMER8 to have a period of 5s and tested it individually with the interrupt enabled to make sure it is triggering and having the correct interval. Now the idea is to trigger the ADCs to perform a few conversions of the pins in order to do some oversampling. 

Here is my ADCs configurations:

image.png

 

image.png

 

 

image.png

 

 

image.png

 

The ADC3 is identical to ADC2. Timer8 Trigger Event Selection is set to "Update Event".

 

Here is my main function:

int main(void)
{

 /* USER CODE BEGIN 1 */

 /* USER CODE END 1 */

 /* MCU Configuration--------------------------------------------------------*/

 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
 HAL_Init();

 /* USER CODE BEGIN Init */

 /* USER CODE END Init */

 /* Configure the system clock */
 SystemClock_Config();

 /* USER CODE BEGIN SysInit */

 /* USER CODE END SysInit */

 /* Initialize all configured peripherals */
 MX_GPIO_Init();
 MX_DMA_Init();
 MX_CRC_Init();
 MX_I2C3_Init();
 MX_SPI5_Init();
 MX_FMC_Init();
 MX_LTDC_Init();
 MX_DMA2D_Init();
 MX_ADC2_Init();
 MX_ADC3_Init();
 MX_ADC1_Init();
 MX_USB_OTG_HS_HCD_Init();
 MX_TIM8_Init();
 MX_TouchGFX_Init();
 /* Call PreOsInit function */
 MX_TouchGFX_PreOSInit();
 /* USER CODE BEGIN 2 */

 /* USER CODE END 2 */

 /* Init scheduler */
 osKernelInitialize();

 /* USER CODE BEGIN RTOS_MUTEX */
 /* add mutexes, ... */
 /* USER CODE END RTOS_MUTEX */

 /* USER CODE BEGIN RTOS_SEMAPHORES */
 /* add semaphores, ... */
 /* USER CODE END RTOS_SEMAPHORES */

 /* USER CODE BEGIN RTOS_TIMERS */
 /* start timers, add new ones, ... */
 /* USER CODE END RTOS_TIMERS */

 /* USER CODE BEGIN RTOS_QUEUES */
 /* add queues, ... */
 /* USER CODE END RTOS_QUEUES */

 /* Create the thread(s) */
 /* creation of defaultTask */
 defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);

 /* creation of GUI_Task */
 GUI_TaskHandle = osThreadNew(TouchGFX_Task, NULL, &GUI_Task_attributes);

 /* USER CODE BEGIN RTOS_THREADS */
 /* add threads, ... */
 /* USER CODE END RTOS_THREADS */

 /* USER CODE BEGIN RTOS_EVENTS */
 /* add events, ... */
 /* USER CODE END RTOS_EVENTS */

 /* Start scheduler */
 osKernelStart();

 /* We should never get here as control is now taken by the scheduler */

 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
 /* USER CODE END WHILE */

 /* USER CODE BEGIN 3 */
 }
 /* USER CODE END 3 */
}

 

My ADC initializing functions:

static void MX_ADC1_Init(void)
{

 /* USER CODE BEGIN ADC1_Init 0 */

 /* USER CODE END ADC1_Init 0 */

 ADC_MultiModeTypeDef multimode = {0};
 ADC_ChannelConfTypeDef sConfig = {0};

 /* USER CODE BEGIN ADC1_Init 1 */

 /* USER CODE END ADC1_Init 1 */

 /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
 */
 hadc1.Instance = ADC1;
 hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
 hadc1.Init.Resolution = ADC_RESOLUTION_12B;
 hadc1.Init.ScanConvMode = DISABLE;
 hadc1.Init.ContinuousConvMode = DISABLE;
 hadc1.Init.DiscontinuousConvMode = DISABLE;
 hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
 hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T8_TRGO;
 hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
 hadc1.Init.NbrOfConversion = 1;
 hadc1.Init.DMAContinuousRequests = DISABLE;
 hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
 if (HAL_ADC_Init(&hadc1) != HAL_OK)
 {
 Error_Handler();
 }

 /** Configure the ADC multi-mode
 */
 multimode.Mode = ADC_TRIPLEMODE_REGSIMULT;
 multimode.DMAAccessMode = ADC_DMAACCESSMODE_1;
 multimode.TwoSamplingDelay = ADC_TWOSAMPLINGDELAY_5CYCLES;
 if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
 {
 Error_Handler();
 }

 /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
 */
 sConfig.Channel = ADC_CHANNEL_1;
 sConfig.Rank = 1;
 sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
 if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
 {
 Error_Handler();
 }
 /* USER CODE BEGIN ADC1_Init 2 */
 if(HAL_ADCEx_MultiModeStart_DMA(&hadc1, test, 3) != HAL_OK)
 //if(HAL_ADCEx_MultiModeStart_DMA(&hadc1, test, 5) != HAL_OK)
 //if(HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&(adc_L.val), 1) != HAL_OK)
 {
 /* Start Conversation Error */
 Error_Handler();
 }
 /* USER CODE END ADC1_Init 2 */

}

/**
 * @brief ADC2 Initialization Function
 * None
 * @retval None
 */
static void MX_ADC2_Init(void)
{

 /* USER CODE BEGIN ADC2_Init 0 */

 /* USER CODE END ADC2_Init 0 */

 ADC_ChannelConfTypeDef sConfig = {0};

 /* USER CODE BEGIN ADC2_Init 1 */

 /* USER CODE END ADC2_Init 1 */

 /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
 */
 hadc2.Instance = ADC2;
 hadc2.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
 hadc2.Init.Resolution = ADC_RESOLUTION_12B;
 hadc2.Init.ScanConvMode = DISABLE;
 hadc2.Init.ContinuousConvMode = DISABLE;
 hadc2.Init.DiscontinuousConvMode = DISABLE;
 hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
 hadc2.Init.NbrOfConversion = 1;
 hadc2.Init.DMAContinuousRequests = DISABLE;
 hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
 if (HAL_ADC_Init(&hadc2) != HAL_OK)
 {
 Error_Handler();
 }

 /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
 */
 sConfig.Channel = ADC_CHANNEL_2;
 sConfig.Rank = 1;
 sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
 if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
 {
 Error_Handler();
 }
 /* USER CODE BEGIN ADC2_Init 2 */
 //if(HAL_ADC_Start_DMA(&hadc2, (uint32_t*)&(adc_R.val), 1) != HAL_OK)
 if(HAL_ADC_Start(&hadc2) != HAL_OK)
 {
 /* Start Conversation Error */
 Error_Handler();
 }
 /* USER CODE END ADC2_Init 2 */

}

/**
 * @brief ADC3 Initialization Function
 * None
 * @retval None
 */
static void MX_ADC3_Init(void)
{

 /* USER CODE BEGIN ADC3_Init 0 */

 /* USER CODE END ADC3_Init 0 */

 ADC_ChannelConfTypeDef sConfig = {0};

 /* USER CODE BEGIN ADC3_Init 1 */

 /* USER CODE END ADC3_Init 1 */

 /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
 */
 hadc3.Instance = ADC3;
 hadc3.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
 hadc3.Init.Resolution = ADC_RESOLUTION_12B;
 hadc3.Init.ScanConvMode = DISABLE;
 hadc3.Init.ContinuousConvMode = DISABLE;
 hadc3.Init.DiscontinuousConvMode = DISABLE;
 hadc3.Init.DataAlign = ADC_DATAALIGN_RIGHT;
 hadc3.Init.NbrOfConversion = 1;
 hadc3.Init.DMAContinuousRequests = DISABLE;
 hadc3.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
 if (HAL_ADC_Init(&hadc3) != HAL_OK)
 {
 Error_Handler();
 }

 /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
 */
 sConfig.Channel = ADC_CHANNEL_4;
 sConfig.Rank = 1;
 sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
 if (HAL_ADC_ConfigChannel(&hadc3, &sConfig) != HAL_OK)
 {
 Error_Handler();
 }
 /* USER CODE BEGIN ADC3_Init 2 */
 //if(HAL_ADC_Start_DMA(&hadc3, (uint32_t*)&(adc_M.val), 1) != HAL_OK)
 if(HAL_ADC_Start(&hadc3) != HAL_OK)
 {
 /* Start Conversation Error */
 Error_Handler();
 }
 /* USER CODE END ADC3_Init 2 */

}

 

Timer8 initializing function:

static void MX_TIM8_Init(void)
{

 /* USER CODE BEGIN TIM8_Init 0 */

 /* USER CODE END TIM8_Init 0 */

 TIM_ClockConfigTypeDef sClockSourceConfig = {0};
 TIM_MasterConfigTypeDef sMasterConfig = {0};

 /* USER CODE BEGIN TIM8_Init 1 */

 /* USER CODE END TIM8_Init 1 */
 htim8.Instance = TIM8;
 htim8.Init.Prescaler = 14399;
 htim8.Init.CounterMode = TIM_COUNTERMODE_UP;
 htim8.Init.Period = 49999;
 htim8.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
 htim8.Init.RepetitionCounter = 0;
 htim8.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
 if (HAL_TIM_Base_Init(&htim8) != HAL_OK)
 {
 Error_Handler();
 }
 sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
 if (HAL_TIM_ConfigClockSource(&htim8, &sClockSourceConfig) != HAL_OK)
 {
 Error_Handler();
 }
 sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
 sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
 if (HAL_TIMEx_MasterConfigSynchronization(&htim8, &sMasterConfig) != HAL_OK)
 {
 Error_Handler();
 }
 /* USER CODE BEGIN TIM8_Init 2 */
 HAL_TIM_Base_Start(&htim8);
 /* USER CODE END TIM8_Init 2 */

}

 

Problem is: it only fires HAL_ADC_ConvCpltCallback once at the beginning of the debugging and not anymore. My "test" variable is defined as "uint32_t test[3];".

 

I've already tried almost all combinations possible... tried changing EOC flag to "end of all conversions", tried circular and normal DMA modes, tried also some other ADC modes (continuous, injected, scan etc) but I only get one of the following results:

- either the callback keeps getting inifintely called blocking the rest of the application and leading to ADC overrun or;

- it triggers once and never more

Also pausing the debug session shows me that the results stored on the test variable are the ones from the beginning of the session, regardless if I change the voltage level ate the ADC pins. What am I missing here?

    This topic has been closed for replies.

    6 replies

    Super User
    October 28, 2025

    > either the callback keeps getting inifintely called blocking the rest of the application and leading to ADC overrun

    With circular mode enabled, you will need to slow down the rate of conversions so the CPU can keep up. Increase the sampling time and/or use a larger buffer for storage so the interrupts get called less often.

    Visitor II
    October 28, 2025

    That was the initial problem, using DMA Continuous Requests as Enabled with Circular DMA, was causing it to infinitely trigger, regardless of the ADC conversion rate. Now that the ADC is triggered by a Timer with a very large period (multiple seconds), my expectation was that the ADCs perform the conversion very fast due to its ADC_CLK being very high, and that the DMA should transfer this result to the memory. If that would work, I could start thinking about increasing the sampling rate of the ADC but even with this current one, it is not working. Definitely not because the CPU is not keeping up, since we are talking about seconds. Or have I misunderstood your point?

    Graduate II
    November 2, 2025

    Have you found a solution for this? I am having this exact problem, with these exact two outcomes (either my adc callback fires once and never again, or it fires continuously and floods things so that no other code can run). I too have tried almost every permutation of settings I can think to try, and both ChatGPT and Claude are unable to offer insight into what might be wrong.

     

    thanks!

    Graduate II
    November 3, 2025

    (I don't mean to hijack this thread - it seems similar enough to what I am trying to do that I'd like to amplify it, but not sure if that means making a new post about it). 

    I think the sticky bit here is that:

    • We want to trigger the ADC periodically with a timer. So we start the ADC once, then let the timer trigger it periodically
    • If DMA is in circular mode, the 'trigger' part of things becomes irrelevant - once the DMA is started, it just fires the DMA complete interrupt constantly once the buffer is full. It doesn't seem to "convert once, then stop and wait for the next trigger". This is consistent with the way DMA behaves in other contexts, I guess, but isn't quite what we want here. But:
    • If DMA is in normal mode, it 'kicks off' once one ADC read is complete. The timer trigger does not 'restart' it, and (to my knowledge) there's no "access point" for restarting the DMA automatically at the timer trigger point. As far as I can tell, we have to:
      • Set the timer trigger up
      • start DMA in normal mode
      • allow the trigger to fire the ADC, which in turn triggers the DMA read (?)
      • In/after the DMA complete interrupt, we read the results, then need to re-start the DMA
      • Wait for the next trigger moment?

    This all seems a little convoluted compared to how the triggering machinery works outside of the DMA context...so convoluted that I'm quite sure I am misunderstanding something. 

    Explorer
    November 3, 2025

    > If DMA is in normal mode, it 'kicks off' once one ADC read is complete. The timer trigger does not 'restart' it, and (to my knowledge) there's no "access point" for restarting the DMA automatically at the timer trigger point.

    Which is not the way things work, as I understand it.
    You would need to trigger the ADC to get this chain going, not DMA.

    Assuming the F429 is not too dissimiliar from the F407, here a snapshot from the reference manual of the latter:

    Ozone_0-1762179071060.png

    I have always used DMA in circular mode for this purpose, although with a larger number of transfers at once.
    This should work as well in dual and triple mode.

    IMHO dual and triple modes only make sense if you need a set of ADC channel data in shorter intervals than possible by one ADC alone. I never used it.

    Super User
    November 3, 2025

    Perhaps get the examples working, then use those as a base:

    STM32CubeF4/Projects/STM32446E_EVAL/Examples/ADC/ADC_TripleModeInterleaved/Src/main.c at 34f54dd3b7ce267453350d1bb0fe5b35d623774d · STMicroelectronics/STM32CubeF4

    Quite a few differences between those and the posted code.

    Visitor II
    November 3, 2025

    The whole topic is for me too confusing for multiple reasons.

     

    Some parts of the documentation seems to say that DMA in Circular mode is used when you do not want to set up the DMA every time manually after each triggered conversion, which was exactly my initial purpose. Also, this is different than having the ADC mode with "Continuous Mode: Enabled", which would trigger infinitely conversions after the last one is done (which is NOT what I want). So it seemed clear that Continuous Mode: Disabled with DMA Mode: Circular is exactly what I need. But it seems this circular mode keeps generating requests even if no new conversion is done

    Setting the the DMA Mode to Normal, on the other hand, would seem to solve the problem as long as I keep activating the DMA manually after every trigger. I did try this option as well but it did not work, it only triggered once. Here I must admit, perhaps I've activated the DMA in a wrong way after the trigger or forgot to clear/set important flags, not sure.

    In the end, responding to your question @sb_st , no I did not find a solution so I just gave up this freaking DMA usage for this case. I found the whole documentation quite poor on covering the multiple mode of the ADCs. Also following the steps presented on document UM1725 did not help at all. Now I am running on Multimode with interrupt only, reading manually the values from the ADCs, which was also kind of cumbersom. The documentation of the ADC Extended driver seems to be the one with all MultiMode HAL functions, but guess what? No "HAL_ADCEx_MultiModeStart_IT()" exists, even though it seems possible to use Multimode with DMA Disabled.

    At the User Manual itself if you go to ADC->Multimode ADC->Regular Simultaneous Mode->Triple mode sub-chapter, it is explicitely only mentioning DMA procedure, as if it would not be possible to use it without DMA.

    Furthermore, at the ADC_CDR register explanation:

    feliperibas_0-1762182601150.png

    it seems the upper 2 bytes holds one conversion value whereas the lower 2 bytes, another converted value, on Multimode, alternating. But on the Multimode Chapter itself:

    feliperibas_1-1762182674600.png

    it leads me to believe the ADC_CDR is completely overwritten by a single result every time. Perhaps I just did not get anything about the whole concept of this ADC but as for now, the documentation is just ***. So I've set up manually the registers to work in a kind of Multimode Simultaneous Regular conversions with interrupt at the end and that's it.

     

    Explorer
    November 4, 2025

    > ... it leads me to believe the ADC_CDR is completely overwritten by a single result every time.

    Yes, it is.
    Which is exactly the same behavior as with ADC_DR and multi-channel conversion sequences.
    You need to pick up the result immediately, or it will be overwritten.
    ST has the ADC designed this way, and assumes you use DMA for more complex and time-consuming setups.

    Just as a counterexample, the LPC14xx and LPC17xx devices I dealt with have individual result registers for each channel.

    Visitor II
    November 4, 2025

    How is that? I mean, on one explanation it says the 4 bytes are split, being the top 2 one value and the bottom 2 another value. On the second explanation it says ADC_CDR[31:0] = ADCx_DR[15:0]. To be coherent with the first explanation, shouldnt it be ADC_CDR[15:0] = ACDx_DR[15:0] (besides maybe explicitely mentioning ADC_CDR[31:16] = ADC_CDR[15:0])? The way it is written seems it overwrites the whole 32bit ADC_CDR with the new ADCx_DR, reseting the upper 32:16 bits.

    Visitor II
    November 23, 2025

    So, I had given up using DMA and was just using Regular Simultaneous Triple Mode but manually starting ADC2 and ADC3 with HAL_ADC_Start() and ADC1 after them with HAL_ADC_Start_IT() and having one callback for ADC1 where I would read all ADCs at once. ADC1 was being triggered by a Timer. This used to work until i had some unexpected behavior: pulling ADC3 pin to 3v crashed my application. I noticed that both ADC1 and ADC2 behaved like open-drain without pull-up or pull-down and I could connect to anything between 0v and 3v without problems. ADC3 however, despite all the same configuration (no pull-ups or downs, open-drain, analog mode etc), was behaving as if it would have a pull-down, giving always 0 when not connected and measuring 3v difference to the 3v pin. When pulling it to 3v, the ADC would capture this 3v for a few miliseconds and then the application would break or exhibit some unexpected behavior. I thought it was a HW problem but I think it was due to the fact i was accessing ADC3 registers when using Triple Simultaneous mode, where the user is supposed to read stuff from the common register. So I went back to the Regular Simultaneous Triple Mode with DMA, and now it works! Finally. So here is what I figured it out:

     

    complementing to some stuff pointer out by @sb_st :

    - Continuous conversion mode is an ADC setting which decouples the ADC from the timmer, starting the next conversion immediately after the last one. As I was using the timer to make sure my intervals were constant, this must be disabled;

    - DMA Continuous requests: this is also an ADC setting and it tells the ADC module to keep sending DMA requests uninterruptly, which is in my case also not desired as I only want to transfer the data once at end of conversion and wait for the next timer trigger. So also disabled

    - DMA Circular mode: this one fills up the number of conversions to be made after finishing the last sequence. I left it disabled.

    Now the important things:

    1) quote from the manual RM0090 Page 403 talking about DMA Continuous Requests: "... the DMA bit is
    not cleared by hardware. It must be written to 0, then to 1 to start a new transfer.".  I am not sure if this is the DMA bit the manual is talking about but I put these two lines on my ADC Conversion Callback:

    	ADC123_COMMON->CCR &= 0xFFFF3FFF;
    	ADC123_COMMON->CCR |= 0x00004000;

    Which disables and enables DMA Mode from Common ADC Register. Commenting out these lines makes my code not work again.

    2) Besides re-setting the DMA mode on the common ADC register, I also appended the following lines afterwards (still inside the ADC conv callback):

    	DMA2_Stream0->CR |= 0x10;
    	DMA2_Stream0->CR |= 0x01;

    Which re-enables the DMA Interrupt on my stream as well as enabling it again.

    3) I enabled the ADCs global interrupts (NVIC) so the auto-generated code from STM32CubeIDE/MX calls the ADC interrupt handlers which check for OVR flag. In case overrun occurs, this also blocks further readings. This can be done directly on the registers as well but I used the callbacks so I only go through the handlers if an overrun occrus.

    4) I've set up my global variable as an array of 3 elements of uint32_t. Then DMA set as "word" for peripheral and memory, only increment on memory. This setting is also probably better using "half-word" with different parameters, since ADC results are 16 bit but I decided to use 32bit as ADC_CDR is 32bit and it is working, so I havent tested with "half-word" yet.

    Now my code works as expected, but I confess I still dont understand who re-fills my DMA->NDTR, since i am clearly not doing that manually after triggering the conversions during intialization (only once). But it does work somehow.

     

    Here is the rest of my code.

    Initialization:

    int main(void)
    {
    ...
    MX_GPIO_Init();
     MX_DMA_Init();
    ...
     MX_ADC2_Init();
     MX_ADC3_Init();
     MX_ADC1_Init();
     MX_TIM8_Init(); // <- used for trigger
    ...
    }
    void MX_ADC2_Init(void)
    {
    
     /* USER CODE BEGIN ADC2_Init 0 */
    
     /* USER CODE END ADC2_Init 0 */
    
     ADC_ChannelConfTypeDef sConfig = {0};
    
     /* USER CODE BEGIN ADC2_Init 1 */
    
     /* USER CODE END ADC2_Init 1 */
    
     /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
     */
     hadc2.Instance = ADC2;
     hadc2.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
     hadc2.Init.Resolution = ADC_RESOLUTION_12B;
     hadc2.Init.ScanConvMode = DISABLE;
     hadc2.Init.ContinuousConvMode = DISABLE;
     hadc2.Init.DiscontinuousConvMode = DISABLE;
     hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
     hadc2.Init.NbrOfConversion = 1;
     hadc2.Init.DMAContinuousRequests = DISABLE;
     hadc2.Init.EOCSelection = ADC_EOC_SEQ_CONV;
     if (HAL_ADC_Init(&hadc2) != HAL_OK)
     {
     Error_Handler();
     }
    
     /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
     */
     sConfig.Channel = ADC_CHANNEL_7;
     sConfig.Rank = 1;
     sConfig.SamplingTime = ADC_SAMPLETIME_144CYCLES;
     if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
     {
     Error_Handler();
     }
     /* USER CODE BEGIN ADC2_Init 2 */
     if(HAL_ADC_Start(&hadc2) != HAL_OK)
     {
     /* Start Conversation Error */
     Error_Handler();
     }
     __HAL_ADC_ENABLE_IT(&hadc2, ADC_IT_OVR);
     /* USER CODE END ADC2_Init 2 */
    
    }
    void MX_ADC3_Init(void)
    {
    
     /* USER CODE BEGIN ADC3_Init 0 */
    
     /* USER CODE END ADC3_Init 0 */
    
     ADC_ChannelConfTypeDef sConfig = {0};
    
     /* USER CODE BEGIN ADC3_Init 1 */
    
     /* USER CODE END ADC3_Init 1 */
    
     /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
     */
     hadc3.Instance = ADC3;
     hadc3.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
     hadc3.Init.Resolution = ADC_RESOLUTION_12B;
     hadc3.Init.ScanConvMode = DISABLE;
     hadc3.Init.ContinuousConvMode = DISABLE;
     hadc3.Init.DiscontinuousConvMode = DISABLE;
     hadc3.Init.DataAlign = ADC_DATAALIGN_RIGHT;
     hadc3.Init.NbrOfConversion = 1;
     hadc3.Init.DMAContinuousRequests = DISABLE;
     hadc3.Init.EOCSelection = ADC_EOC_SEQ_CONV;
     if (HAL_ADC_Init(&hadc3) != HAL_OK)
     {
     Error_Handler();
     }
    
     /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
     */
     sConfig.Channel = ADC_CHANNEL_2;
     sConfig.Rank = 1;
     sConfig.SamplingTime = ADC_SAMPLETIME_144CYCLES;
     if (HAL_ADC_ConfigChannel(&hadc3, &sConfig) != HAL_OK)
     {
     Error_Handler();
     }
     /* USER CODE BEGIN ADC3_Init 2 */
     if(HAL_ADC_Start(&hadc3) != HAL_OK)
     {
     /* Start Conversation Error */
     Error_Handler();
     }
     __HAL_ADC_ENABLE_IT(&hadc3, ADC_IT_OVR);
     /* USER CODE END ADC3_Init 2 */
    
    }
    void MX_ADC1_Init(void)
    {
    
     /* USER CODE BEGIN ADC1_Init 0 */
    
     /* USER CODE END ADC1_Init 0 */
    
     ADC_MultiModeTypeDef multimode = {0};
     ADC_ChannelConfTypeDef sConfig = {0};
    
     /* USER CODE BEGIN ADC1_Init 1 */
    
     /* USER CODE END ADC1_Init 1 */
    
     /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
     */
     hadc1.Instance = ADC1;
     hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
     hadc1.Init.Resolution = ADC_RESOLUTION_12B;
     hadc1.Init.ScanConvMode = DISABLE;
     hadc1.Init.ContinuousConvMode = DISABLE;
     hadc1.Init.DiscontinuousConvMode = DISABLE;
     hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
     hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T8_TRGO;
     hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
     hadc1.Init.NbrOfConversion = 1;
     hadc1.Init.DMAContinuousRequests = DISABLE;
     hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
     if (HAL_ADC_Init(&hadc1) != HAL_OK)
     {
     Error_Handler();
     }
    
     /** Configure the ADC multi-mode
     */
     multimode.Mode = ADC_TRIPLEMODE_REGSIMULT;
     multimode.DMAAccessMode = ADC_DMAACCESSMODE_1;
     multimode.TwoSamplingDelay = ADC_TWOSAMPLINGDELAY_5CYCLES;
     if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
     {
     Error_Handler();
     }
    
     /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
     */
     sConfig.Channel = ADC_CHANNEL_5;
     sConfig.Rank = 1;
     sConfig.SamplingTime = ADC_SAMPLETIME_144CYCLES;
     if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
     {
     Error_Handler();
     }
     /* USER CODE BEGIN ADC1_Init 2 */
     if(HAL_ADCEx_MultiModeStart_DMA(&hadc1, adc_val, 3) != HAL_OK)
     {
     /* Start Conversation Error */
     Error_Handler();
     }
     /* USER CODE END ADC1_Init 2 */
    
    }

     

    uint32_t adc_val[3];
    void HAL_ADC_ConvCpltCallback (ADC_HandleTypeDef * hadc)
    {
    ...
    //USE adc_val[3] HERE
    ...
    
    	ADC123_COMMON->CCR &= 0xFFFF3FFF;
    	ADC123_COMMON->CCR |= 0x00004000;
    
    	DMA2_Stream0->CR |= 0x10;
    	DMA2_Stream0->CR |= 0x01;
    }

     

    I hope this helps, but I would still be glad if someone could explain who is filling up my NDTR every round, as I havent found this info in the manual.

    Cheers