1. STM32G431多通道ADC采集实战指南
在嵌入式系统开发中,模拟信号采集是常见需求。STM32G431作为STMicroelectronics推出的高性能微控制器,其内置的ADC模块支持多通道采集,能够满足大多数应用场景。本文将详细介绍如何使用STM32G431实现多通道ADC采集,包括硬件配置、软件实现以及常见问题处理。
2. 硬件配置与CubeMX设置
2.1 引脚配置与ADC通道映射
STM32G431的ADC模块支持多达19个外部通道,我们选择ADC1的通道1(PA0)、通道2(PA1)和通道3(PA2)作为采集通道。在CubeMX中的配置步骤如下:
- 在Pinout & Configuration标签页中,找到ADC1模块
- 启用IN1、IN2和IN3通道(对应PA0、PA1、PA2引脚)
- 将这三个GPIO配置为模拟输入模式(Analog)
注意:STM32的ADC输入引脚必须配置为模拟模式,否则无法正确采集信号。如果配置为其他模式(如输入或输出),可能导致采集值不准确或完全无效。
2.2 ADC参数配置
在ADC1的Parameter Settings选项卡中,关键配置如下:
- Clock Prescaler: ADC clock asynchronous prescaler选择Div4
- Resolution: 12位分辨率(ADC_RESOLUTION_12B)
- Data Alignment: 右对齐(ADC_DATAALIGN_RIGHT)
- Scan Conversion Mode: 启用(ENABLE)
- Continuous Conversion Mode: 启用(ENABLE)
- Number Of Conversion: 设置为3(对应我们使用的3个通道)
- External Trigger Conversion Source: 软件触发(ADC_SOFTWARE_START)
对于每个通道的具体配置:
- Channel 1 (PA0): Rank=1, Sampling Time=47.5 cycles
- Channel 2 (PA1): Rank=2, Sampling Time=2.5 cycles
- Channel 3 (PA2): Rank=3, Sampling Time=2.5 cycles
采样时间的选择需要权衡转换速度和精度。较长的采样时间可以提高精度但降低转换速率。对于高阻抗信号源,建议使用更长的采样时间。
2.3 DMA配置
为实现高效的数据传输,我们使用DMA将ADC转换结果直接搬运到内存:
- 在DMA Settings选项卡中添加DMA通道
- 配置方向为Peripheral To Memory
- 外设地址不递增,内存地址递增
- 数据宽度均为Half Word(16位)
- 模式选择Circular(循环模式)
DMA配置的关键点:
c复制hdma_adc1.Instance = DMA1_Channel1;
hdma_adc1.Init.Request = DMA_REQUEST_ADC1;
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_HALFWORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc1.Init.Mode = DMA_CIRCULAR;
hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
3. 软件实现与代码解析
3.1 ADC初始化与校准
在main函数中,初始化ADC后需要进行校准:
c复制HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
校准是ADC精度保证的关键步骤,可以消除偏移误差和增益误差。未执行校准可能导致采集值存在系统性偏差。
3.2 多通道采集实现
我们实现了ADC_Multich_Sample函数来处理多通道采集:
c复制void ADC_Multich_Sample(void)
{
static uint32_t ADC_Tick1;
if(uwTick - ADC_Tick1 < 5) return; // 函数减速,防止阻塞
ADC_Tick1 = uwTick;
adcValue.value1 = 0;
adcValue.value2 = 0;
adcValue.value3 = 0;
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)My_adcData, adc_max);
// 数据处理
adcValue.value1 = (My_adcData[0]>>4)*3300/4096;
adcValue.value2 = (My_adcData[4]>>4)*3300/4096;
adcValue.value3 = (My_adcData[8]>>4)*3300/4096;
// 消除微小干扰
if(adcValue.value1<5) adcValue.value1=0;
if(adcValue.value2<5) adcValue.value2=0;
if(adcValue.value3<5) adcValue.value3=0;
}
数据处理说明:
- 右移4位是因为12位ADC结果存储在16位变量中(右对齐)
- 乘以3300/4096将ADC值转换为毫伏电压(假设参考电压为3.3V)
- 阈值过滤消除了小于5mV的噪声干扰
3.3 数据缓冲区设计
我们使用双缓冲区来存储ADC数据:
c复制#define adc_max 40
uint16_t My_adcData[adc_max] = {0};
缓冲区大小设置为40,为3个通道提供足够的存储空间。DMA将以循环模式填充这个缓冲区。
4. 常见问题与解决方案
4.1 采集值不准确
可能原因及解决方法:
- 未执行校准:确保在初始化后调用HAL_ADCEx_Calibration_Start
- 采样时间不足:对于高阻抗信号源,增加采样时间
- 参考电压不稳定:检查VDDA和VSSA引脚的去耦电容
- GPIO模式错误:确认ADC引脚配置为模拟模式
4.2 DMA传输问题
DMA配置常见错误:
- 数据宽度不匹配:确保ADC、DMA和缓冲区使用相同的数据宽度
- 地址递增设置错误:外设地址不应递增,内存地址通常需要递增
- 缓冲区溢出:确保缓冲区足够大,特别是多通道情况下
4.3 多通道采集顺序混乱
如果通道采集顺序不符合预期:
- 检查CubeMX中每个通道的Rank设置
- 确认ScanConvMode已启用
- 确保NbrOfConversion设置为正确的通道数
5. 性能优化技巧
5.1 过采样技术
STM32G431的ADC支持硬件过采样,可显著提高有效分辨率:
c复制hadc1.Init.OversamplingMode = ENABLE;
hadc1.Init.Oversampling.Ratio = ADC_OVERSAMPLING_RATIO_128;
hadc1.Init.Oversampling.RightBitShift = ADC_RIGHTBITSHIFT_3;
hadc1.Init.Oversampling.TriggeredMode = ADC_TRIGGEREDMODE_SINGLE_TRIGGER;
过采样128倍可将12位ADC的有效分辨率提高到约15位。
5.2 定时器触发
替代软件触发,使用定时器触发ADC转换可实现精确的采样间隔:
- 配置一个定时器(如TIM2)
- 在ADC配置中选择定时器作为触发源
- 设置定时器频率决定采样率
这种方法消除了软件延迟带来的时序不确定性。
5.3 数据滤波算法
除了简单的阈值滤波,还可以实现更复杂的滤波算法:
c复制#define FILTER_SAMPLES 8
uint16_t filterBuffer[FILTER_SAMPLES];
uint16_t medianFilter(uint16_t newSample) {
// 移位旧数据
for(int i=0; i<FILTER_SAMPLES-1; i++) {
filterBuffer[i] = filterBuffer[i+1];
}
filterBuffer[FILTER_SAMPLES-1] = newSample;
// 计算中值
uint16_t temp[FILTER_SAMPLES];
memcpy(temp, filterBuffer, sizeof(temp));
// 排序并返回中值
// ... 排序实现省略
return temp[FILTER_SAMPLES/2];
}
6. 实测结果与分析
在我们的测试中:
- PA0连接3.3V,测得电压3295mV(误差约0.15%)
- PA1悬空,测得电压14mV(合理的噪声水平)
- PA2悬空,测得电压0mV(被阈值过滤)
结果表明:
- ADC线性度良好,在满量程处误差很小
- 悬空引脚的噪声处于合理范围
- 阈值滤波有效消除了微小噪声
7. 扩展应用
7.1 温度传感器读取
STM32G431内置温度传感器,连接到ADC1的通道17:
c复制sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
sConfig.Rank = ADC_REGULAR_RANK_4;
sConfig.SamplingTime = ADC_SAMPLETIME_247CYCLES_5;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
温度计算公式:
c复制float temperature = ((float)adcValue * 3300 / 4096 - 760) / 2.5 + 25;
7.2 多ADC协同工作
STM32G431支持多个ADC同时工作,可实现:
- 同步采样(需要精确时序的应用)
- 交替采样(提高总体采样率)
- 交叉校验(高可靠性系统)
8. 调试技巧
8.1 printf调试
我们实现了通过串口输出调试信息:
c复制int fputc(int ch, FILE *f) {
HAL_UART_Transmit(&huart4, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
使用示例:
c复制printf("ADC value: %d, %dmV\r\n", rawValue, voltage);
8.2 断点调试
在ADC转换完成回调函数中设置断点,检查DMA传输是否正确:
c复制void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
if (hadc->Instance == ADC1) {
adc_conversion_complete = 1; // 此处设断点
}
}
8.3 信号发生器测试
使用信号发生器产生已知波形(如正弦波),验证ADC的线性度和频率响应。
9. 电源管理与噪声抑制
9.1 电源去耦
ADC精度受电源噪声影响较大,建议:
- 在VDDA和VSSA引脚附近放置1μF和100nF陶瓷电容
- 使用独立的LDO为模拟部分供电
- 避免数字和模拟部分共用地线
9.2 低功耗模式
在不需要采集时,可以关闭ADC以节省功耗:
c复制HAL_ADC_Stop(&hadc1);
HAL_ADC_DeInit(&hadc1);
需要时重新初始化和校准。
10. 项目移植与适配
10.1 更换MCU型号
本方案可适配其他STM32系列,主要注意:
- ADC和DMA通道可能不同
- 校准方法可能有差异
- 时钟配置需要调整
10.2 增加通道数量
要增加采集通道:
- 在CubeMX中添加更多ADC通道
- 增大数据缓冲区
- 调整NbrOfConversion参数
- 更新数据处理代码
10.3 提高采样率
如需更高采样率:
- 提高ADC时钟(但不超过规格限制)
- 减少采样时间(可能影响精度)
- 使用交替模式或多ADC并行
在实际项目中,我发现在STM32G431上实现稳定可靠的多通道ADC采集,关键在于仔细的硬件设计和正确的软件配置。特别是DMA配置和ADC校准这两个环节,常常是新手容易出错的地方。通过合理的过采样和滤波算法,可以显著提高采集质量。