你是不是也遇到过:STM32 采一个电位器,串口打印出来的 ADC 值一直跳。明明手没碰,数据却在 2040、2058、2033 之间来回晃。
然后你开始怀疑:ADC 配置错了?HAL 库有问题?DMA 出 bug 了?代码改了一晚上,最后发现——还是抖。
说实话,ADC 这东西最坑初学者的地方就在这里:它看起来是软件问题,实际经常是硬件、电源、采样参数一起在搞你。
先记住一句话:ADC 不是在测一个绝对电压,它是在拿输入电压和参考电压做比较。
如果你的 3.3V 本身不稳,ADC 结果一定跟着跳。项目里很常见,继电器一吸合、电机一启动、WiFi 模块一发射,3.3V 掉一下,ADC 数值马上变。
这时候你在代码里疯狂平均,意义不大。因为根上是电源在抖。
我之前调过一个板子,ADC 测电池电压,静态很稳,一开电机就乱跳。最后不是 ADC 配置问题,而是电机电流把地线拉动了。ADC 看到的不是电池电压,是整块板子的噪声。
第二个常见坑:采样时间太短。
很多人配置 ADC 时,采样周期随手选最小。看起来转换速度快,实际很容易翻车。
比如你用 100K、100K 电阻分压测电池。万用表看着很稳,ADC 读出来却飘。原因很简单:输入阻抗太高,ADC 内部采样电容还没充满,就开始转换了。
低速信号别贪快。电池电压、温度、电位器,这些东西根本不需要特别快。采样时间适当加长,反而更稳。
第三个坑:输入脚悬、线太长、旁边有 PWM。
ADC 引脚很敏感。传感器线从电机线旁边走,PWM 线贴着模拟信号线跑,数据不抖才怪。
正确做法很朴素:ADC 输入脚附近加个小电容,比如 0.01uF 到 0.1uF。响应速度要求不高的场景,这招非常管用。
但注意,电容不是越大越好。你测旋钮可以大一点,测快速变化信号就不能乱加。
软件上,最常用的不是复杂算法,而是多次采样取平均。
uint16_tADC_ReadOnce(void){HAL_ADC_Start(&hadc1);HAL_ADC_PollForConversion(&hadc1,10);uint16_tvalue=HAL_ADC_GetValue(&hadc1);HAL_ADC_Stop(&hadc1);returnvalue;}uint16_tADC_ReadAverage(uint8_ttimes){uint32_tsum=0;for(uint8_ti=0;i<times;i++){sum+=ADC_ReadOnce();HAL_Delay(1);}returnsum/times;}比如你主循环里这样用:
uint16_tadc_raw=ADC_ReadAverage(16);floatvoltage=adc_raw*3.3f/4095.0f;16 次平均,对电池电压、温度检测、电位器读取这类场景,已经够用了。
如果你想让显示更平滑,可以再加一个简单的一阶滤波:
uint16_tADC_Filter(uint16_tnew_value){staticuint16_told_value=0;if(old_value==0)old_value=new_value;old_value=(old_value*7+new_value)/8;returnold_value;}使用方式:
uint16_tadc_avg=ADC_ReadAverage(16);uint16_tadc_show=ADC_Filter(adc_avg);这段代码的效果很直接:数据不会突然乱跳,显示也更舒服。
但我要提醒一句:滤波只能让数据好看,不能替你解决硬件问题。
如果 ADC 接 GND 都还在乱跳,那先查配置、供电、地线、参考电压。
如果接 GND 稳,接 3.3V 也稳,接传感器才抖,那就重点查传感器输出、分压电阻、线长、干扰和输入阻抗。
我自己排查 ADC 问题,一般按这个顺序来:
先把 ADC 脚接 GND,看是不是接近 0。
再接 3.3V,看是不是接近 4095。
再接一个稳定分压点,看抖动范围。
最后才接传感器。
这一步很关键。它能快速判断:到底是 ADC 本身的问题,还是外部电路的问题。
很多初学者最大的问题,就是一上来盯着代码改。ADC 不是纯软件模块,它站在硬件和软件中间。电源不干净、参考电压不稳、采样时间太短、输入阻抗太高、走线被干扰,最后都会变成你串口里的“数值乱跳”。
所以,ADC 采样不稳,别急着怀疑 HAL 库。
先把电源稳住,把采样时间加长,把输入阻抗降下来,再谈滤波。
真正做项目的人都知道:ADC 稳不稳,代码只占一半,另一半藏在电源、地线和那根你没注意的模拟信号线上。
如果你也被 ADC 数值乱跳折磨过,建议收藏这篇。下次项目翻车时,按这个顺序排查,能少走很多弯路。