引言:为什么选择 PY32F003 做多路 ADC 采集?​

在嵌入式开发中,精准高效的模拟信号采集是许多项目的核心需求。PY32F003 作为一款高性价比的 32 位微控制器,凭借其丰富的外设资源和优异的性能,成为多路 ADC 采集场景的理想选择。​

本文将重点介绍如何利用 PY32F003 的DMA(直接存储器访问) 功能实现多路 ADC 数据的高效采集。相比传统的 CPU 轮询方式,DMA 方式能显著降低处理器负载,提高数据采集的实时性和连续性,特别适合需要同时监测多个模拟信号的应用场景。​

一、硬件基础:PY32F003 的 ADC 与 DMA 资源​

1.1 PY32F003 芯片简介​

PY32F003 是一款基于 ARM Cortex-M0 + 内核的微控制器,主频高达 48MHz,拥有丰富的外设资源:​

  • 12 位精度 ADC,支持多通道采集​
  • 1 个 DMA 控制器,支持多种外设数据传输​
  • 丰富的 GPIO 接口,可灵活配置为 ADC 输入引脚​

1.2 ADC 与 DMA 的协同优势​

传统 ADC 采集流程中,CPU 需要不断查询转换完成标志并读取数据,这会占用大量处理器时间。而DMA 与 ADC 的组合能实现:​

  • 硬件自动完成数据传输,无需 CPU 干预​
  • 连续不间断的多通道数据采集​
  • 避免因 CPU 响应延迟导致的数据丢失​

二、开发环境搭建​

2.1 软件准备​

  • 安装 PY32F003 专用开发工具链(如 Keil MDK 或 GCC)​ 。。,
  • 本文使用的是基于官方SDK修改的问题

2.2 硬件搭建​

  • PY32F003 最小系统板​
  • 模拟信号源(如电位器、传感器模块)​
  • 面包板及杜邦线​
  • USB 转串口模块(用于程序下载和调试)
  • ​仿真调试器

三、DMA 多路 ADC 采集原理​

3.1 基本工作流程​

DMA 多路 ADC 采集的核心原理是通过 DMA 控制器自动将 ADC 转换结果搬运到指定的内存缓冲区,整个过程无需 CPU 参与:​

  1. 配置 ADC 为扫描模式,依次对多个通道进行转换​
  1. 启用 ADC 的 DMA 请求功能​
  1. 配置 DMA 通道,设置数据传输方向(从 ADC 到内存)​
  1. 启动 ADC 和 DMA 后,硬件自动完成多通道数据的连续采集和存储​

3.2 关键技术点​

  • 循环缓冲区:确保数据采集的连续性,避免缓冲区溢出​
  • 通道扫描顺序:按预设顺序循环采集各通道信号​
  • 数据对齐方式:12 位 ADC 数据在 16 位存储单元中的排列方式​
  • 采样率控制:根据应用需求配置合适的转换速度​

四、软件实现框架​

4.1 初始化配置详解​

核心初始化代码​

4.2 初始化配置详解​

核心初始化代码​

五、实战案例:多通道电压监测系统​

5.1 硬件连接​

以采集 5 路模拟信号为例:​

  • 通道 0:可调电源(0-3.3V 可调)​
  • 通道 1:连接温度传感器(如 LM35)​
  • 通道 2:连接光照传感器(如光敏电阻模块)​

5.2 代码实现与解析​

核心初始化代码​

# 初始化ADC和DMA​

/********************************************************************************************************
**函数信息 :void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
**功能描述 :初始化ADC相关MSP
**输入参数 :
**输出参数 :
**    备注 :
********************************************************************************************************/
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{
    GPIO_InitTypeDef          GPIO_InitStruct;
    __HAL_RCC_SYSCFG_CLK_ENABLE();                              //SYSCFG时钟使能
    __HAL_RCC_DMA_CLK_ENABLE();                                 //DMA时钟使能
    __HAL_RCC_GPIOA_CLK_ENABLE();                               //使能GPIOA时钟
    __HAL_RCC_ADC_CLK_ENABLE();                                 //使能ADC时钟

    //----------------
    //ADC通道配置PA0
    //----------------
    GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    HAL_SYSCFG_DMA_Req(0);                                      //DMA1_MAP选择为ADC
    //------------
    //DMA配置
    //------------
    HdmaCh1.Instance                 = DMA1_Channel1;           //选择DMA通道1
    HdmaCh1.Init.Direction           = DMA_PERIPH_TO_MEMORY;    //方向为从外设到存储器
    HdmaCh1.Init.PeriphInc           = DMA_PINC_DISABLE;        //禁止外设地址增量
    HdmaCh1.Init.MemInc              = DMA_MINC_ENABLE;         //使能存储器地址增量
	  HdmaCh1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; 	//外设数据宽度为32位
	  HdmaCh1.Init.MemDataAlignment    = DMA_MDATAALIGN_WORD; 	//存储器数据宽度位32位
    HdmaCh1.Init.Mode                = DMA_CIRCULAR;            //循环模式
    HdmaCh1.Init.Priority            = DMA_PRIORITY_VERY_HIGH;  //通道优先级为很高

    HAL_DMA_DeInit(&HdmaCh1);                                   //DMA反初始化
    HAL_DMA_Init(&HdmaCh1);                                     //初始化DMA通道1
    __HAL_LINKDMA(hadc, DMA_Handle, HdmaCh1);                   //连接DMA句柄
    
//     HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 1, 0);          //普通应用不用中断
//     HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}

# 主循环中读取数据​

# 读取各通道电压值​

# 计算平均值(减少噪声影响)​

/**
  ******************************************************************************
  * @file    main.c
  * @author  MCU Application Team
  * @Version V1.0.0
  * @Date    2020-10-19
  * @brief   main function
  ******************************************************************************
  */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "string.h"
/* Private define ------------------------------------------------------------*/
#define ADC_BUFFER_SIZE         128         //缓存数据数组的大小
#define CHANNEL_NUM	            5           //ADC通道数量
#define VREF                1200        // 参考电压1.200V
#define V_OFFSET            0.014f      //补偿电压 
#define ADC_RESOLUTION      4095     // 12位ADC分辨率
/* Private variables ---------------------------------------------------------*/
typedef enum {
    ADC_SINGLE_MODE,
    ADC_DMA_MODE,
} ADC_ChannelEnum;
typedef struct {
    uint32_t average;
    float voltage;
} ADC_Channel;

typedef struct {
    uint32_t adc_data[CHANNEL_NUM * ADC_BUFFER_SIZE]; 
    ADC_Channel channels[CHANNEL_NUM];
	uint32_t vref;
    uint32_t vcc;
    uint32_t temp_val;
    uint32_t temp;
    uint32_t index;
    volatile uint8_t data_ready;
} ADC_DataManager;

typedef struct {
	software_timer_t timer_adc_convert;
    software_timer_t timer_printf;
}timer_t;

ADC_HandleTypeDef           AdcHandle;
ADC_ChannelConfTypeDef      sConfig;
TIM_HandleTypeDef           TimHandle;
TIM_OC_InitTypeDef          OCConfig;
TIM_MasterConfigTypeDef     sMasterConfig;
ADC_DataManager             adc = {0};

/* Private user code ---------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
void Error_Handler(void);
void Timer_Init(void);
void ADCConfig(ADC_DataManager *adc_manager);
void SystemClock_Config(void);
float Get_Voltage(uint32_t vcc ,uint32_t adc_value);
uint32_t Min(uint32_t a, uint32_t b);
uint32_t Max(uint32_t a, uint32_t b);
void Process_ADC_Data(ADC_DataManager *adc_manager);
void Deal_ADC_Data(ADC_DataManager *adc_manager);
void Sample_VREF_Temp(ADC_DataManager *adc_manager);
void adc_mult_channel_set(ADC_DataManager *adc_manager);
/********************************************************************************************************
**函数信息 :void main(void)
**功能描述 :执行函数
**输入参数 :
**输出参数 :
**    备注 :
********************************************************************************************************/
int main(void)
{
 
    HAL_Init();                                            //初始化systick
    SystemClock_Config();                                  //系统时钟配置
    DEBUG_USART_Config();                                  //初始化uart
    Timer_Init();                                          //TImer1初始化
    ADCConfig(&adc);                                           //ADC初始化
    while (1) {
        Process_ADC_Data(&adc);
    }
}


void Process_ADC_Data(ADC_DataManager *adc_manager) 
{
    if(__HAL_DMA_GET_FLAG(DMA1->ISR, DMA_ISR_TCIF1)) {   //DMA通道1传输完成
        // 处理缓冲区数据
        Deal_ADC_Data(adc_manager);
        __HAL_DMA_CLEAR_FLAG(DMA1->ISR, DMA_IFCR_CTCIF1);

    }
}

void Deal_ADC_Data(ADC_DataManager *adc_manager) 
{   
    uint64_t sum = 0;
    uint32_t max = 0;
    uint32_t min = 4095;
    uint32_t val;
    // 遍历所有ADC通道
    for (int ch = 0; ch < CHANNEL_NUM; ch++) {
        // 遍历缓冲区计算sum/max/min
        for (int i = 0; i < ADC_BUFFER_SIZE; i++) {
            val = adc_manager->adc_data[CHANNEL_NUM * i + ch];
            sum += val;
            max = Max(max, val);
            min = Min(min, val);
        }
        // 计算平均值(去掉最大最小值)
#if (ADC_BUFFER_SIZE < 10)
		adc_manager->channels[ch].average = sum/ADC_BUFFER_SIZE;
#else 		
		adc_manager->channels[ch].average = (sum - min - max) / (ADC_BUFFER_SIZE - 2);
#endif  
		
		adc_manager->channels[ch].voltage = Get_Voltage(adc_manager->vcc,adc_manager->channels[ch].average);
        sum = 0;
        max = 0;
        min = 4095;
    }
#if 1
    printf("ADC_VAL:");
    for (int i = 0; i < CHANNEL_NUM; i++) {
        printf("%.4f,",adc_manager ->channels[i].voltage);
        //printf("%d,",  adc_manager->channels[i].average);
    }
    printf("%d,",adc_manager->vref);
    printf("%d,",adc_manager->vcc);
    printf("%d,",adc_manager->temp_val);
    printf("%d,",adc_manager->temp);
    printf("\r\n");
#endif
}

/**
 * @brief 求两无符号整数中较小值
 * @param a 数a
 * @param b 数b
 * @return 较小值
 */
uint32_t Min(uint32_t a, uint32_t b)
{
    return a <= b ? a : b;
}

/**
 * @brief 求两无符号整数中较大值
 * @param a 数a
 * @param b 数b
 * @return 较大值
 */
uint32_t Max(uint32_t a, uint32_t b)
{
    return a >= b ? a : b;
}

/**
 * @brief 将ADC原始值转换为电压值(带舍入处理的整数运算)
 * @param adc_value ADC原始采样值(0-4095)
 * @return 计算得到的电压值(单位:V)
 * @note 使用整数运算提高效率,通过以下步骤实现:
 *       1. 将3.28V放大1000倍变为3280整型
 *       2. 先乘后除减少精度损失
 *       3. 添加2047(=4095/2)实现四舍五入
 *       4. 最后除以1000还原实际电压值
 * 对于M0系列没有FPU的计算是提升,但是对于M4/M7系列有FPU的,没必要做这个处理,
 * 直接用浮点计算也可以,但是效率会低一些,但相对误差精度会高一点。
 * 用精度换取运算速度。
 */
float Get_Voltage(uint32_t vcc ,uint32_t adc_value) 
{
    if(vcc == 0){
        vcc = 3300;
    }
    uint32_t temp = (adc_value * vcc + 2047) / 4095; // 添加舍入处理
    float v = temp / 1000.0f ; 
    return v; 
}

/********************************************************************************************************
**函数信息 :void SystemClock_Config(void)
**功能描述 :系统时钟配置
**输入参数 :
**输出参数 :
**    备注 :
********************************************************************************************************/
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /*配置时钟源HSE/HSI/LSE/LSI*/
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE | RCC_OSCILLATORTYPE_HSI | RCC_OSCILLATORTYPE_LSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;                                                      //开启HSI
  //RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_4MHz;                            //配置HSI输出时钟为4MHz
  //RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_8MHz;                            //配置HSI输出时钟为8MHz
  //RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_16MHz;                           //配置HSI输出时钟为16MHz
  //RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_22p12MHz;                        //配置HSI输出时钟为22.12MHz
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_24MHz;                             //配置HSI输出时钟为24MHz
  RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV1;                                                      //HSI不分频
  RCC_OscInitStruct.HSEState = RCC_HSE_OFF;                                                      //关闭HSE
  RCC_OscInitStruct.HSEFreq = RCC_HSE_16_32MHz;                                                 //HSE工作频率范围16M~32M
  RCC_OscInitStruct.LSIState = RCC_LSI_OFF;                                                      //关闭LSI


  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)                                          //RCC振荡器初始化
  {
    Error_Handler();
  }

  //初始化CPU,AHB,APB总线时钟
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1; //RCC系统时钟类型
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;                                        //SYSCLK的源选择为HSI
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;                                            //APH时钟不分频
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;                                              //APB时钟不分频

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)                        //初始化RCC系统时钟
  {
    Error_Handler();
  }
}

/********************************************************************************************************
**函数信息 :void ADCConfig(void)
**功能描述 :ADC初始化
**输入参数 :
**输出参数 :
**    备注 :
********************************************************************************************************/
void ADCConfig(ADC_DataManager *adc_manager)
{
    __HAL_RCC_ADC_FORCE_RESET();
    __HAL_RCC_ADC_RELEASE_RESET();
    __HAL_RCC_ADC_CLK_ENABLE();

    /*ADC校准*/
    AdcHandle.Instance = ADC1;

    if (HAL_ADCEx_Calibration_Start(&AdcHandle) != HAL_OK) {                 //AD校准
        Error_Handler();
    }                                                    //ADC时钟使能
    AdcHandle.Instance                   = ADC1;                                    //ADC
    AdcHandle.Init.ClockPrescaler        = ADC_CLOCK_SYNC_PCLK_DIV4;                //模拟ADC时钟源为PCLK /2 = 12M_ADC时钟
    AdcHandle.Init.Resolution            = ADC_RESOLUTION_12B;                      //转换分辨率12bit
    AdcHandle.Init.DataAlign             = ADC_DATAALIGN_RIGHT;                     //数据右对齐
    AdcHandle.Init.ScanConvMode          = ADC_SCAN_DIRECTION_FORWARD;             //扫描序列方向上
    AdcHandle.Init.LowPowerAutoWait      = DISABLE;                                  //等待转换模式开启
    AdcHandle.Init.ContinuousConvMode    = ENABLE;                                 //单次转换模式
    AdcHandle.Init.DiscontinuousConvMode = DISABLE;                                 //使能非连续模式
    
    AdcHandle.Init.ExternalTrigConv      = ADC_SOFTWARE_START;                      //ADC 无外部事件
    AdcHandle.Init.ExternalTrigConvEdge  = ADC_EXTERNALTRIGCONVEDGE_NONE;           //上升沿和下降沿进行外部驱动
    AdcHandle.Init.DMAContinuousRequests = ENABLE;                                  //DMA循环模式选择
    AdcHandle.Init.Overrun               = ADC_OVR_DATA_OVERWRITTEN;                //当过载发生时,ADC_DR保留久值
    AdcHandle.Init.SamplingTimeCommon    = ADC_SAMPLETIME_239CYCLES_5;               //通道采样时间为28.5ADC时钟周期
    /*ADC初始化*/
    if (HAL_ADC_Init(&AdcHandle) != HAL_OK) {
        Error_Handler();
    }
  
    Sample_VREF_Temp(adc_manager);
}
void adc_mult_channel_set(ADC_DataManager *adc_manager)
{
   //====================
   //输入通道0选择配置
   //====================
    sConfig.Rank         = ADC_RANK_NONE;
    sConfig.Channel      = ADC_CHANNEL_TEMPSENSOR;
    if (HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK)  {                   //清除内部温度通道
        Error_Handler();
    }

    sConfig.Rank         = ADC_RANK_NONE;
    sConfig.Channel      = ADC_CHANNEL_VREFINT;
    if (HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK)  {                   //清除内部参考电压通道ADC_RANK_NONE
        Error_Handler();
    }

    sConfig.Rank         = ADC_RANK_CHANNEL_NUMBER;
    sConfig.Channel      = ADC_CHANNEL_0;
    if (HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK)  {                   //通道1配置
        Error_Handler();
    }
    
    sConfig.Rank         = ADC_RANK_CHANNEL_NUMBER;
    sConfig.Channel      = ADC_CHANNEL_1;
    if (HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK)  {                   //通道1配置
        Error_Handler();
    }

    sConfig.Rank         = ADC_RANK_CHANNEL_NUMBER;
    sConfig.Channel      = ADC_CHANNEL_2;
    if (HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK)  {                   //通道1配置
        Error_Handler();
    }

    sConfig.Rank         = ADC_RANK_CHANNEL_NUMBER;
    sConfig.Channel      = ADC_CHANNEL_3;
    if (HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK)  {                   //通道1配置
        Error_Handler();
    }

    sConfig.Rank         = ADC_RANK_CHANNEL_NUMBER;
    sConfig.Channel      = ADC_CHANNEL_4;
    if (HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK)  {                   //通道4配置
        Error_Handler();
    }
    if (HAL_ADC_Start_DMA(&AdcHandle,adc_manager->adc_data, CHANNEL_NUM*ADC_BUFFER_SIZE) != HAL_OK) {           //ADC_DMA模式开启
        Error_Handler();
    }
   
}
void Sample_VREF_Temp(ADC_DataManager *adc_manager) 
{
#if 1
    HAL_ADC_Stop(&AdcHandle); // 先停止ADC
    HAL_ADC_Stop_DMA(&AdcHandle);
    // adc初始化设置为单独采样模
     AdcHandle.Instance                   = ADC1;                                    //ADC
    AdcHandle.Init.ClockPrescaler        = ADC_CLOCK_SYNC_PCLK_DIV4;                //模拟ADC时钟源为PCLK /2 = 12M_ADC时钟
    AdcHandle.Init.Resolution            = ADC_RESOLUTION_12B;                      //转换分辨率12bit
    AdcHandle.Init.DataAlign             = ADC_DATAALIGN_RIGHT;                     //数据右对齐
    AdcHandle.Init.ScanConvMode          = ADC_SCAN_DIRECTION_FORWARD;             //扫描序列方向上
    AdcHandle.Init.LowPowerAutoWait      = DISABLE;                                  //等待转换模式开启
    AdcHandle.Init.ContinuousConvMode    = DISABLE;                                 //单次转换模式
    AdcHandle.Init.DiscontinuousConvMode = ENABLE;                                 //使能非连续模式
    
    AdcHandle.Init.ExternalTrigConv      = ADC_SOFTWARE_START;                      //ADC 无外部事件
    AdcHandle.Init.ExternalTrigConvEdge  = ADC_EXTERNALTRIGCONVEDGE_NONE;           //上升沿和下降沿进行外部驱动
    AdcHandle.Init.DMAContinuousRequests = ENABLE;                                  //DMA循环模式选择
    AdcHandle.Init.Overrun               = ADC_OVR_DATA_OVERWRITTEN;                //当过载发生时,ADC_DR保留久值
    AdcHandle.Init.SamplingTimeCommon    = ADC_SAMPLETIME_239CYCLES_5;               //通道采样时间为28.5ADC时钟周期
    if (HAL_ADC_Init(&AdcHandle) != HAL_OK) {
        Error_Handler();
    }

    // 采样VREFINT
    sConfig.Channel = ADC_CHANNEL_VREFINT;
    HAL_ADC_ConfigChannel(&AdcHandle, &sConfig);
    HAL_ADC_Start(&AdcHandle);
    HAL_ADC_PollForConversion(&AdcHandle, 10);
    adc_manager->vref = HAL_ADC_GetValue(&AdcHandle);
	adc_manager->vcc = (ADC_RESOLUTION * VREF)/ adc_manager->vref;      //计算VCC电压
    
    // 采样温度传感器
    sConfig.Channel = ADC_CHANNEL_TEMPSENSOR; 
    HAL_ADC_ConfigChannel(&AdcHandle, &sConfig);
    HAL_ADC_Start(&AdcHandle);
    HAL_ADC_PollForConversion(&AdcHandle, 10);
    adc_manager->temp_val = HAL_ADC_GetValue(&AdcHandle);
    adc_manager->temp = __LL_ADC_CALC_TEMPERATURE(adc_manager->vcc, adc_manager->temp_val, LL_ADC_RESOLUTION_12B);

    HAL_ADC_Stop(&AdcHandle); // 先停止ADC
    HAL_ADC_Stop_DMA(&AdcHandle);
    // adc初始化设置为连续采样模式
    AdcHandle.Instance                   = ADC1;                                    //ADC
    AdcHandle.Init.ClockPrescaler        = ADC_CLOCK_SYNC_PCLK_DIV4;                //模拟ADC时钟源为PCLK /2 = 12M_ADC时钟
    AdcHandle.Init.Resolution            = ADC_RESOLUTION_12B;                      //转换分辨率12bit
    AdcHandle.Init.DataAlign             = ADC_DATAALIGN_RIGHT;                     //数据右对齐
    AdcHandle.Init.ScanConvMode          = ADC_SCAN_DIRECTION_FORWARD;             //扫描序列方向上
    AdcHandle.Init.LowPowerAutoWait      = DISABLE;                                  //等待转换模式开启
    AdcHandle.Init.ContinuousConvMode    = ENABLE;                                 //单次转换模式
    AdcHandle.Init.DiscontinuousConvMode = DISABLE;                                 //使能非连续模式
    
    AdcHandle.Init.ExternalTrigConv      = ADC_SOFTWARE_START;                      //ADC 无外部事件
    AdcHandle.Init.ExternalTrigConvEdge  = ADC_EXTERNALTRIGCONVEDGE_NONE;           //上升沿和下降沿进行外部驱动
    AdcHandle.Init.DMAContinuousRequests = ENABLE;                                  //DMA循环模式选择
    AdcHandle.Init.Overrun               = ADC_OVR_DATA_OVERWRITTEN;                //当过载发生时,ADC_DR保留久值
    AdcHandle.Init.SamplingTimeCommon    = ADC_SAMPLETIME_239CYCLES_5;               //通道采样时间为28.5ADC时钟周期
    if (HAL_ADC_Init(&AdcHandle) != HAL_OK) {
        Error_Handler();
    }
#endif 
    adc_mult_channel_set(adc_manager);

}

/********************************************************************************************************
**函数信息 :void Timer_Init(void)
**功能描述 :Timer1初始化
**输入参数 :
**输出参数 :
**    备注 :TIM1每100ms产生更新事件
********************************************************************************************************/
 void Timer_Init(void)
 {

//   __HAL_RCC_TIM1_CLK_ENABLE();                                        //TIM1时钟使能
//   TimHandle.Instance = TIM1;                                          //TIM1
//   TimHandle.Init.Period            = 24000 - 1;                        //TIM1重装载值位8000-1
//   TimHandle.Init.Prescaler         = 1000 - 1;                         //预分频为100-1
//   TimHandle.Init.ClockDivision     = TIM_CLOCKDIVISION_DIV1;          //时钟不分配   
//   TimHandle.Init.CounterMode       = TIM_COUNTERMODE_UP;              //向上计数
//   TimHandle.Init.RepetitionCounter = 0;                               //不重复
//   TimHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;  //自动重装载寄存器没有缓冲
//   if (HAL_TIM_Base_Init(&TimHandle) != HAL_OK) {                       //初始化TIM1
//     Error_Handler();
//   }
//   /*配置TIM1为主机模式*/
//   sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;                // 选择更新事件作为触发源
//   sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;        //主/从模式无作用
//   HAL_TIMEx_MasterConfigSynchronization(&TimHandle, &sMasterConfig);  //配置TIM1
//   if (HAL_TIM_Base_Start(&TimHandle) != HAL_OK)                       //TIM1时钟启动
//   {
//     Error_Handler();
//   }
}
 
/********************************************************************************************************
**函数信息 :Error_Handler(void)
**功能描述 :错误执行函数
**输入参数 :
**输出参数 :
**    备注 :
********************************************************************************************************/
void Error_Handler(void)
{
  while (1)
  {
  }
}
#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 can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
}
#endif /* USE_FULL_ASSERT */


/* Private function -------------------------------------------------------*/



六、常见问题与优化技巧​

6.1 采集精度问题​

  • 电源噪声抑制:为 ADC 参考电压添加滤波电容​
  • 通道间干扰:避免高电压通道与低电压通道相邻布置​
  • 校准方法:通过标准电压源进行系统校准​

6.2 性能优化​

  • 缓冲区大小调整:平衡内存占用和数据连续性​
  • 采样率匹配:根据信号变化速度设置合理采样率​
  • 中断配合:使用 DMA 半传输中断处理实时数据​

6.3 调试技巧​

  • 使用示波器监测 ADC 输入信号​
  • 检查 DMA 缓冲区数据完整性​
  • 通过 LED 指示灯判断系统运行状态​

6.4 使用vofa+动态查看ADC变化

5个通数数据外加开机采集的内部1.2V电压ADC和开机采集的温度ADC。

七、总结与扩展应用​

通过本文介绍的方法,我们成功实现了基于 PY32F003 的 DMA 多路 ADC 采集系统。该方案不仅能高效完成多通道模拟信号采集,还能显著降低 CPU 负载,为复杂嵌入式系统设计提供了可靠的信号采集解决方案。​

扩展应用方向:​

  • 多参数环境监测系统(温度、湿度、光照等)​
  • 电池电量监测与均衡管理​
  • 音频信号采集与分析​
  • 工业传感器数据采集终端​

希望本文能为你的 PY32F003 开发提供实用参考,如有任何问题欢迎在评论区交流讨论!

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐