基于PY32F003的DMA多路ADC采集
PY32F003 是一款基于 ARM Cortex-M0 + 内核的微控制器,主频高达 48MHz,拥有丰富的外设资源:12 位精度 ADC,支持多通道采集1 个 DMA 控制器,支持多种外设数据传输丰富的 GPIO 接口,可灵活配置为 ADC 输入引脚。
引言:为什么选择 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 参与:
- 配置 ADC 为扫描模式,依次对多个通道进行转换
- 启用 ADC 的 DMA 请求功能
- 配置 DMA 通道,设置数据传输方向(从 ADC 到内存)
- 启动 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 开发提供实用参考,如有任何问题欢迎在评论区交流讨论!
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)