Skip to main content
Explorer
June 24, 2026
Question

STM32F405,ADC_DUALMODE_REGSIMULT ADC1 and ADC2 acquisition with DMA. Phase keeps changing for each acquisition

  • June 24, 2026
  • 2 replies
  • 104 views

Implementation: STM32F405, using Timer 2 to synchronously trigger ADC1 and ADC2 acquisition with DMA.
Problem: The sampling frequencies of ADC1 and ADC2 are correct, but the phase difference keeps changing for each acquisition – they are not sampling synchronously, and the captured waveform does not match what is observed on the oscilloscope.
Question: 

1,How can I debug and resolve this issue?

2, I found that I must call HAL_ADC_Start(&hadc2) before HAL_ADCEx_MultiModeStart_DMA(&hadc1, ...);  Is this correct?

     HAL_ADC_Start(&hadc2);
     HAL_ADCEx_MultiModeStart_DMA(&hadc1,(uint32_t*)adc_dma_buf,POINTS_PER_CH);
Specific code: 

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2026 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f4xx_hal_adc.h"
#include "stm32f4xx_hal_dma.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
#include <stdio.h>
#include "arm_math.h"
#include "atc.h"                     // AT 命令库
/* 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 */
#define CHANNEL_NUM      2
#define POINTS_PER_CH    4096
#define DMA_BUFF_SIZE    (CHANNEL_NUM * POINTS_PER_CH)
#define ULTRASONIC_DURATION_MS   12
#define SAMPLING_FREQ            25000.0f
#define SOUND_SPEED              1500.0f
#define TRANSDUCER_FREQ          500000.0f
#define FFT_SIZE                 POINTS_PER_CH
#define FFT_HALF_SIZE            (FFT_SIZE / 2)
#define ULTRASONIC_FREQ_HZ       1000000
/* USER CODE END PM */

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

TIM_HandleTypeDef htim2;

UART_HandleTypeDef huart1;
DMA_HandleTypeDef hdma_usart1_rx;
DMA_HandleTypeDef hdma_usart1_tx;

/* USER CODE BEGIN PV */
//__attribute__((aligned(32))) uint16_t adc_dma_buf[DMA_BUFF_SIZE];
//__attribute__((aligned(32))) uint16_t adc_backup_buf[DMA_BUFF_SIZE];

__attribute__((aligned(32))) uint32_t adc_dma_buf[POINTS_PER_CH];
__attribute__((aligned(32))) uint32_t adc_backup_buf[POINTS_PER_CH];
volatile uint8_t new_data_ready = 0;

static float32_t fft_input[FFT_SIZE * 2];
static float32_t fft_output[FFT_SIZE * 2];
static float32_t power_spectrum[FFT_HALF_SIZE];
static float32_t window[FFT_SIZE];
static arm_cfft_instance_f32 fft_inst;

/* AT 命令控制标志 */
static volatile uint8_t measurement_pending = 0;
static volatile uint8_t measurement_busy = 0;

/* ATC 句柄 */
ATC_HandleTypeDef hAtc;
/* 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_TIM2_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_ADC2_Init(void);
/* USER CODE BEGIN PFP */
static void compute_flow_velocity(void);
static void generate_window(void);
static float32_t modified_rife(float32_t *power, int peak_index, float32_t fs, int fft_len);
static void cmd_start(const char* args, char* response);
static void cmd_stop(const char* args, char* response);
static void cmd_status(const char* args, char* response);
static void cmd_help(const char* args, char* response);
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

#if 1
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif

PUTCHAR_PROTOTYPE
{
    HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, 2);
      //HAL_UART_Transmit_DMA(&huart1, (uint8_t*)&ch, 1);
    return ch;
}
#endif
/* ADC DMA 完成回调 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
    if (hadc->Instance == ADC1) {  
              HAL_TIM_Base_Stop(&htim2);
              __HAL_TIM_SET_COUNTER(&htim2, 0);
              #if 0
        // ---------- DMA 状态 ----------
        uint16_t ndtr = hdma_adc1.Instance->NDTR;      // 剩余传输次数
        uint32_t lisr = DMA2->LISR;                    // 低中断状态
        uint32_t hisr = DMA2->HISR;                    // 高中断状态
        uint32_t cr = hdma_adc1.Instance->CR;          // 流控制寄存器
        uint32_t dma_psar = hdma_adc1.Instance->PAR;   // 外设地址 (应指向 ADC_CDR)
        uint32_t dma_m0ar = hdma_adc1.Instance->M0AR;  // 内存地址

        // ---------- ADC1 状态 ----------
        uint32_t adc1_cr1 = hadc1.Instance->CR1;
        uint32_t adc1_cr2 = hadc1.Instance->CR2;       // 含 DMA 使能位
        uint32_t adc1_sr = hadc1.Instance->SR;         // 状态寄存器

        // ---------- ADC2 状态 ----------
        uint32_t adc2_cr1 = hadc2.Instance->CR1;
        uint32_t adc2_cr2 = hadc2.Instance->CR2;
        uint32_t adc2_sr = hadc2.Instance->SR;

        // ---------- 定时器状态 ----------
        uint32_t tim2_cnt = __HAL_TIM_GET_COUNTER(&htim2);
        uint32_t tim2_cr1 = TIM2->CR1;                // 控制寄存器,查看 CEN 位

        // ---------- 打印 ----------
        printf("\r\n=== Debug Info ===\r\n");
        printf("DMA: NDTR=%d LISR=0x%08X HISR=0x%08X CR=0x%08X\r\n", ndtr, lisr, hisr, cr);
        printf("DMA PAR=0x%08X M0AR=0x%08X\r\n", dma_psar, dma_m0ar);
        printf("ADC1: CR1=0x%08X CR2=0x%08X SR=0x%08X\r\n", adc1_cr1, adc1_cr2, adc1_sr);
        printf("ADC2: CR1=0x%08X CR2=0x%08X SR=0x%08X\r\n", adc2_cr1, adc2_cr2, adc2_sr);
        printf("TIM2: CNT=%lu CR1=0x%08X\r\n", tim2_cnt, tim2_cr1);
        printf("====================\r\n");   
        for (int i=0; i<10; i++) {
          printf("buf[%d] = 0x%08X\r\n", i, adc_dma_buf[i]);
        }
                printf("CDR = 0x%08lX\r\n", ADC->CDR);
                #endif
        memcpy(adc_backup_buf, adc_dma_buf, POINTS_PER_CH * sizeof(uint32_t));
        new_data_ready = 1;
    }
}

/* AT 命令回调函数 */
static void cmd_start(const char* args, char* response) {
    if (measurement_busy) { strcpy(response, "ERROR: busy"); return; }
    
    strcpy(response, "startOK");
}
static void cmd_stop(const char* args, char* response) {
   
    strcpy(response, "OK");
}
static void cmd_status(const char* args, char* response) {
    sprintf(response, "Pending:%d Busy:%d", measurement_pending, measurement_busy);
}
static void cmd_help(const char* args, char* response) {
    strcpy(response, "AT+START\r\nAT+STOP\r\nAT+STATUS\r\nAT+HELP");
}

ATC_CmdTypeDef at_commands[] = {
    {"AT+START", cmd_start},
    {"AT+STOP",  cmd_stop},
    {"AT+STATUS",cmd_status},
    {"AT+HELP",  cmd_help},
    {NULL, NULL}
};
/* 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_USART1_UART_Init();
    MX_DMA_Init();

    MX_ADC1_Init();
  MX_ADC2_Init();
  MX_TIM2_Init();

    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);

  /* USER CODE BEGIN 2 */

    generate_window();
    arm_cfft_init_f32(&fft_inst, FFT_SIZE);
    printf("Doppler Flow Meter Started.\r\n");

    // 初始化 AT 命令库(缓冲区 256 字节)
    if (!ATC_Init(&hAtc, &huart1, 256, "Doppler")) {
            printf("ATC_Init failed!\r\n");
            while(1);
    }
    ATC_SetCommands(&hAtc, at_commands);
    printf("AT commands ready. Send AT+HELP\r\n");
    /* 启动第一次采集: 发射超声波 -> ADC */
    // 启动 1MHz 连续方波输出
    # if 0
    HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);
    // 持续发射指定毫秒数
    HAL_Delay(ULTRASONIC_DURATION_MS);
    // 停止发射
    HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_2);
    // 启动 ADC 采集
    
    #else
    
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET);
  HAL_ADC_Start(&hadc2);
  HAL_ADCEx_MultiModeStart_DMA(&hadc1,(uint32_t*)adc_dma_buf,POINTS_PER_CH);
    HAL_TIM_Base_Start(&htim2);
    HAL_Delay(ULTRASONIC_DURATION_MS);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET);
    #endif

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
    __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
  while (1)
  {
        //printf("CDR = 0x%08lX\r\n", ADC->CDR);
        //printf("ADC2 enabled, CR2 = 0x%08X\n", ADC2->CR2);
        ATC_Loop(&hAtc);   // 必须频繁调用
        
        #if 1
        if (new_data_ready)
        {
                new_data_ready = 0;
        #if 1
                printf("--- CH5 (ADC1_IN5)\r\n");
                for (int i = 0; i < POINTS_PER_CH; i++) {
                        uint16_t adc1 = (uint16_t)(adc_backup_buf[i] & 0xFFFF);
                        printf("%d ", adc1);
                }
                printf("\r\n--- CH6 (ADC1_IN6)\r\n");
                for (int i = 0; i < POINTS_PER_CH; i++) {
                        uint16_t adc2 = (uint16_t)(adc_backup_buf[i] >> 16);
                        printf("%d ", adc2);
                }
                printf("\r\n=== End of block ===\r\n");
        #endif
                /* 计算并输出流速 */
                //compute_flow_velocity();

                /* 延时指定秒数(例如 10 ms)后,重新开始下一轮采集 */
                HAL_Delay(1000);   // 延时

                
                # if 0
                HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);
                // 持续发射指定毫秒数
                HAL_Delay(ULTRASONIC_DURATION_MS);
                // 停止发射
                HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_2);
                // 启动 ADC 采集
                
                #else
                
                HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET);
                HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);
        HAL_ADC_Start(&hadc2);
        HAL_ADCEx_MultiModeStart_DMA(&hadc1,(uint32_t*)adc_dma_buf,POINTS_PER_CH);
          HAL_TIM_Base_Start(&htim2);
                HAL_Delay(ULTRASONIC_DURATION_MS);
                HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET);
                HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET);
                #endif
        }
        else
        {
                HAL_Delay(10);
        }
        #endif
    /* 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 = 8;
  RCC_OscInitStruct.PLL.PLLN = 144;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  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_DIV1;

  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_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_T2_TRGO;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 1;
  hadc1.Init.DMAContinuousRequests = ENABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_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_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_15CYCLES;
  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_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;
  hadc2.Init.ExternalTrigConv = ADC_SOFTWARE_START;        // 从机必须为软件触发
  hadc2.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  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_6;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES;
  if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC2_Init 2 */
   
  /* USER CODE END ADC2_Init 2 */

}

/**
  * @brief TIM2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM2_Init(void)
{

  /* USER CODE BEGIN TIM2_Init 0 */

  /* USER CODE END TIM2_Init 0 */

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

  /* USER CODE BEGIN TIM2_Init 1 */

  /* USER CODE END TIM2_Init 1 */
  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 0;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  //htim2.Init.Period =  5759; //5759  25k
    //htim2.Init.Period = 8999; //8999  8k
    htim2.Init.Period = 359; //359;200k
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM2_Init 2 */

  /* USER CODE END TIM2_Init 2 */

}

/**
  * @brief USART1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}

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

  /* DMA controller clock enable */
  __HAL_RCC_DMA2_CLK_ENABLE();
#if 0
  /* ============ 修改:配置 DMA2_Stream0 给 ADC1(主) ============ */
  hdma_adc1.Instance = DMA2_Stream0;           // ADC1 固定映射 Stream0
  hdma_adc1.Init.Channel = DMA_CHANNEL_0;      // ADC 专用通道
  hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
  hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
  hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
  hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;   // 外设 32-bit
  hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;      // 内存 32-bit
  hdma_adc1.Init.Mode = DMA_NORMAL;          // 循环模式
  hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
  HAL_DMA_Init(&hdma_adc1);
  __HAL_DMA_DISABLE_IT(&hdma_adc1, DMA_IT_HT);
  /* 将 DMA 句柄链接到 ADC1 句柄(关键) */
  __HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);

  /* 中断配置(DMA2_Stream0 中断) */
  HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
    /* DMA2_Stream2_IRQn interrupt configuration */
  //HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, 0, 0);
  //HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn);
#else
  /* DMA2_Stream0_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);

#endif
  /* DMA2_Stream5_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream5_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream5_IRQn);
  /* DMA2_Stream7_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream7_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream7_IRQn);
    

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  /* 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_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /* USER CODE BEGIN MX_GPIO_Init_2 */
  /* USER CODE BEGIN MX_GPIO_Init_2 */
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  // 配置 PA7 和 PA15 为推挽输出(用于控制发射脉冲)
  GPIO_InitStruct.Pin = GPIO_PIN_7;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  // 配置 PA8 为推挽输出(用于指示状态)
  GPIO_InitStruct.Pin = GPIO_PIN_8;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  // 配置 PA15 为推挽输出(用于指示状态)
  GPIO_InitStruct.Pin = GPIO_PIN_15;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  /* 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 */
}
#ifdef USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

 

2 replies

mƎALLEm
ST Technical Moderator
June 24, 2026

Hello ​@suker  and welcome to the ST community,

1- In next time please use insert code button to share your code:

2- This post is more related to the ADC configuration with HAL it needs to be posted in STM32 Embedded software.

I’m editing your post and moving it.

Thank you for your understanding.

To give better visibility on the answered topics, please click "Best answer" on the reply which solved your issue or answered your question.
ST Technical Moderator
June 26, 2026

Hello ​@suker 

2, I found that I must call HAL_ADC_Start(&hadc2) before HAL_ADCEx_MultiModeStart_DMA(&hadc1, ...);  Is this correct?

Yes, this is the correct usage of these functions. 

In order to give better visibility on the answered topics, please click on 'Best answer' on the reply which solved your issue or answered your question. Saket_Om