Skip to main content
Graduate
November 30, 2025
Solved

Dual Regular Simultaneous Mode ADC1 and ADC2

  • November 30, 2025
  • 2 replies
  • 219 views

Hello,

from recently I have a problem using STM32F407VGT6, moreover, I am unable to get consecutive conversions in the DMA buffer. For example I want to use Dual Regular Simultaneous ADC mode to obtain results from 4 channels (2 in ADC1 and 2 in ADC2). However, when I start my application only the first iteration is obtained, second part of the buffer is never filled(i.e. CHN3 and CHN4 are never read if CHN1 and CHN2 are rank 0 in ADC1 and ADC2).

Setup:
main(void)

#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc1;
ADC_HandleTypeDef hadc2;
DMA_HandleTypeDef hdma_adc1;

TIM_HandleTypeDef htim1;

/* USER CODE BEGIN PV */
uint32_t dma_buffer[2];
uint16_t adc1_val;
uint16_t adc2_val;
uint16_t adc3_val;
uint16_t adc4_val;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_ADC1_Init(void);
static void MX_ADC2_Init(void);
static void MX_TIM1_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
 if (hadc->Instance == ADC1)
 {

 adc1_val = (uint16_t)(dma_buffer[0] & 0xFFFF );
 adc2_val = (uint16_t)((dma_buffer[0] >> 16) & 0xFFFF );
 adc3_val = (uint16_t)(dma_buffer[1] & 0xFFFF );
 adc4_val = (uint16_t)((dma_buffer[1] >> 16) & 0xFFFF );

 
 HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_3);
 }
}
/* USER CODE END 0 */

/**
 * @brief The application entry point.
 * @retval int
 */
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_ADC1_Init();
 MX_ADC2_Init();
 MX_TIM1_Init();
 /* USER CODE BEGIN 2 */

 HAL_ADC_Start(&hadc2);

 HAL_ADCEx_MultiModeStart_DMA(&hadc1, dma_buffer, 2);

 HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
 HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
 /* USER CODE END 2 */

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

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

/**
 * @brief System Clock Configuration
 * @retval None
 */
void SystemClock_Config(void)
{
 RCC_OscInitTypeDef RCC_OscInitStruct = {0};
 RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

 /** Configure the main internal regulator output voltage
 */
 __HAL_RCC_PWR_CLK_ENABLE();
 __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

 /** Initializes the RCC Oscillators according to the specified parameters
 * in the RCC_OscInitTypeDef structure.
 */
 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
 RCC_OscInitStruct.HSEState = RCC_HSE_ON;
 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
 RCC_OscInitStruct.PLL.PLLM = 4;
 RCC_OscInitStruct.PLL.PLLN = 80;
 RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
 RCC_OscInitStruct.PLL.PLLQ = 7;
 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
 {
 Error_Handler();
 }

 /** Initializes the CPU, AHB and APB buses clocks
 */
 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
 |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
 {
 Error_Handler();
 }
}

/**
 * @brief ADC1 Initialization Function
 * @PAram None
 * @retval None
 */
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_DIV4;
 hadc1.Init.Resolution = ADC_RESOLUTION_12B;
 hadc1.Init.ScanConvMode = ENABLE;
 hadc1.Init.ContinuousConvMode = DISABLE;
 hadc1.Init.DiscontinuousConvMode = DISABLE;
 hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_FALLING;
 hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC2;
 hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
 hadc1.Init.NbrOfConversion = 2;
 hadc1.Init.DMAContinuousRequests = ENABLE;
 hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
 if (HAL_ADC_Init(&hadc1) != HAL_OK)
 {
 Error_Handler();
 }

 /** Configure the ADC multi-mode
 */
 multimode.Mode = ADC_DUALMODE_REGSIMULT;
 multimode.DMAAccessMode = ADC_DMAACCESSMODE_2;
 multimode.TwoSamplingDelay = ADC_TWOSAMPLINGDELAY_20CYCLES;
 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_0;
 sConfig.Rank = 1;
 sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES;
 if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != 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 = 2;
 if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
 {
 Error_Handler();
 }
 /* USER CODE BEGIN ADC1_Init 2 */

 /* USER CODE END ADC1_Init 2 */

}

/**
 * @brief ADC2 Initialization Function
 * @PAram 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_DIV4;
 hadc2.Init.Resolution = ADC_RESOLUTION_12B;
 hadc2.Init.ScanConvMode = ENABLE;
 hadc2.Init.ContinuousConvMode = DISABLE;
 hadc2.Init.DiscontinuousConvMode = DISABLE;
 hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
 hadc2.Init.NbrOfConversion = 2;
 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_5;
 sConfig.Rank = 1;
 sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES;
 if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != 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_6;
 sConfig.Rank = 2;
 if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
 {
 Error_Handler();
 }
 /* USER CODE BEGIN ADC2_Init 2 */

 /* USER CODE END ADC2_Init 2 */

}

/**
 * @brief TIM1 Initialization Function
 * @PAram None
 * @retval None
 */
static void MX_TIM1_Init(void)
{

 /* USER CODE BEGIN TIM1_Init 0 */

 /* USER CODE END TIM1_Init 0 */

 TIM_ClockConfigTypeDef sClockSourceConfig = {0};
 TIM_MasterConfigTypeDef sMasterConfig = {0};
 TIM_OC_InitTypeDef sConfigOC = {0};
 TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};

 /* USER CODE BEGIN TIM1_Init 1 */

 /* USER CODE END TIM1_Init 1 */
 htim1.Instance = TIM1;
 htim1.Init.Prescaler = 30000;
 htim1.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED1;
 htim1.Init.Period = 16000;
 htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
 htim1.Init.RepetitionCounter = 0;
 htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
 if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
 {
 Error_Handler();
 }
 sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
 if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
 {
 Error_Handler();
 }
 if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
 {
 Error_Handler();
 }
 sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
 sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
 if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
 {
 Error_Handler();
 }
 sConfigOC.OCMode = TIM_OCMODE_PWM1;
 sConfigOC.Pulse = 8000;
 sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
 sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
 sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
 sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
 sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
 if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
 {
 Error_Handler();
 }
 sConfigOC.Pulse = 15999;
 if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
 {
 Error_Handler();
 }
 sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
 sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
 sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
 sBreakDeadTimeConfig.DeadTime = 0;
 sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
 sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
 sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
 if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
 {
 Error_Handler();
 }
 /* USER CODE BEGIN TIM1_Init 2 */

 /* USER CODE END TIM1_Init 2 */
 HAL_TIM_MspPostInit(&htim1);

}

/**
 * Enable DMA controller clock
 */
static void MX_DMA_Init(void)
{

 /* DMA controller clock enable */
 __HAL_RCC_DMA2_CLK_ENABLE();

 /* DMA interrupt init */
 /* DMA2_Stream0_IRQn interrupt configuration */
 HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
 HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);

}

/**
 * @brief GPIO Initialization Function
 * @PAram None
 * @retval None
 */
static void MX_GPIO_Init(void)
{
 GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */

 /* GPIO Ports Clock Enable */
 __HAL_RCC_GPIOH_CLK_ENABLE();
 __HAL_RCC_GPIOC_CLK_ENABLE();
 __HAL_RCC_GPIOA_CLK_ENABLE();
 __HAL_RCC_GPIOE_CLK_ENABLE();

 /*Configure GPIO pin Output Level */
 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_3, GPIO_PIN_RESET);

 /*Configure GPIO pin : PC3 */
 GPIO_InitStruct.Pin = GPIO_PIN_3;
 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
 HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
 * @brief This function is executed in case of error occurrence.
 * @retval None
 */
void Error_Handler(void)
{
 /* USER CODE BEGIN Error_Handler_Debug */
 /* User can add his own implementation to report the HAL error return state */
 __disable_irq();
 while (1)
 {
 }
 /* USER CODE END Error_Handler_Debug */
}

Config:
Screenshot from 2025-11-30 08-40-28.png
 Screenshot from 2025-11-30 08-41-13.png


I can confirm that Callback is being called on ever Timer1CC2 event but only 2 out of 4 values get filled:
Screenshot from 2025-11-30 08-44-57.png

Any help is more than welcome:)

    This topic has been closed for replies.
    Best answer by TDK

    Data width should be word, not half-word.

    2 replies

    TDKAnswer
    Super User
    November 30, 2025

    Data width should be word, not half-word.

    richiAuthor
    Graduate
    November 30, 2025

    Ohh thank you so much, this resolved my issue.
    Quick note: I am unable to change it in CubeMX I have only Half-Word option, so I did it manually. I don't know if this is a bug or what.

    Many thanks:))

    Super User
    November 30, 2025

    No problems changing it to Word in STM32CubeMX here.

    TDK_0-1764536821032.png