最近在做一个基于 STM32 的无人机遥控器项目,摇杆的 ADC 数据一直不太稳定,存在抖动和噪声。为了解决这个问题,我对 STM32 摇杆 ADC 数据分析进行了深入的研究和实践,并总结了一些经验分享给大家。
问题场景重现
项目中使用的是 STM32F103C8T6,摇杆通过两个模拟输入引脚连接到 ADC1 的两个通道。在没有操作摇杆的情况下,ADC 读取的数据应该保持在一个稳定的值附近。然而,实际情况是,ADC 数据在小范围内不停地波动,导致控制指令不精确,影响无人机的飞行体验。
底层原理深度剖析
ADC (模数转换器) 的作用是将模拟信号转换为数字信号。STM32 的 ADC 属于逐次逼近型 ADC,其转换精度受到多种因素的影响,包括:
- 参考电压的稳定性: 稳定的参考电压是保证 ADC 转换精度的基础。通常使用外部高精度基准源,或者使用 STM32 内部的参考电压,但需要校准。
- 输入信号的噪声: 电源噪声、环境电磁干扰等都会影响 ADC 的输入信号,导致数据波动。
- 采样频率: 采样频率过低可能导致信号失真,采样频率过高则会增加功耗,并可能引入更多的噪声。
- ADC 分辨率: STM32F103C8T6 的 ADC 分辨率为 12 位,理论精度为 1/4096。实际精度会受到上述因素的影响。
- 软件滤波算法: 滤波算法的选择和参数设置会直接影响数据的平滑程度和响应速度。常见的滤波算法有:均值滤波、中值滤波、卡尔曼滤波等。
具体的代码/配置解决方案
针对以上问题,我采取了以下措施来优化 ADC 数据:
硬件优化:

- 电源滤波: 在 STM32 的电源输入端增加 LC 滤波电路,滤除电源噪声。同时,在摇杆的模拟信号输入端增加一个小的电容 (例如 0.1uF) 进行滤波。
- 屏蔽: 使用屏蔽线连接摇杆和 STM32,减少电磁干扰。
- 合理布局: PCB 设计时,将模拟电路和数字电路分开布局,避免相互干扰。
软件优化:
- ADC 校准: 在程序中进行 ADC 校准,消除 ADC 的零点漂移和增益误差。
// ADC 初始化 ADC_InitTypeDef ADC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; // PA0, PA1 作为 ADC 输入 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &GPIO_InitStructure); ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = DISABLE; // 单通道模式 ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // 连续转换模式 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init(ADC1, &ADC_InitStructure); RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 设置 ADC 时钟,不能超过 14MHz ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1)); ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 读取 ADC 值 uint16_t get_adc_value(uint8_t channel) { ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_55Cycles5); return ADC_GetConversionValue(ADC1); }- 软件滤波: 使用均值滤波算法对 ADC 数据进行平滑处理。
#define FILTER_SIZE 5 // 均值滤波窗口大小 uint16_t adc_buffer[FILTER_SIZE]; uint8_t adc_index = 0; uint16_t filter_adc_value(uint16_t new_value) { adc_buffer[adc_index] = new_value; adc_index = (adc_index + 1) % FILTER_SIZE; uint32_t sum = 0; for (int i = 0; i < FILTER_SIZE; i++) { sum += adc_buffer[i]; } return sum / FILTER_SIZE; } // 使用示例 uint16_t raw_adc = get_adc_value(ADC_Channel_0); uint16_t filtered_adc = filter_adc_value(raw_adc);- 死区处理: 在摇杆中心位置附近设置一个死区,当 ADC 数据在死区范围内波动时,忽略这些波动,避免误操作。
#define DEAD_ZONE 50 // 死区范围 #define CENTER_VALUE 2048 // ADC 中间值 (12 位 ADC) int16_t process_adc_value(uint16_t adc_value) { int16_t diff = adc_value - CENTER_VALUE; if (abs(diff) < DEAD_ZONE) { return 0; // 在死区内,返回 0 } else { return diff; // 不在死区内,返回偏移量 } } // 使用示例 uint16_t raw_adc = get_adc_value(ADC_Channel_0); uint16_t filtered_adc = filter_adc_value(raw_adc); int16_t processed_adc = process_adc_value(filtered_adc);
实战避坑经验总结
- ADC 时钟配置: ADC 时钟频率必须合理配置,过高可能导致转换错误,过低会影响采样速率。建议使用 RCC_PCLK2_Div6 或 RCC_PCLK2_Div8。
- 采样时间: 采样时间会影响 ADC 的转换精度。如果输入信号变化较快,需要选择较长的采样时间。反之,可以选择较短的采样时间,提高采样速率。
- 中断方式读取: 可以使用 ADC 中断方式读取数据,避免 CPU 轮询,提高系统效率。但是,需要注意中断处理函数的执行时间,避免影响其他任务的执行。
- 调试工具: 使用调试工具 (例如 ST-Link) 观察 ADC 的原始数据和滤波后的数据,可以帮助分析问题并优化参数。
通过以上方法,可以有效地提高 STM32 摇杆 ADC 数据的稳定性和精度,提升控制系统的性能。希望这些经验能帮助到大家。
冠军资讯
HelloWorld狂魔