如何提升STM32 ADC指数移动平均的整数运算精度?
STM32 16位ADC整数EMA平滑的偏差修正方案
你的问题核心是整数EMA的截断误差累积导致的系统性向下偏差:原始代码用右移实现整数除法时,会直接截断小数部分,每次更新的向下误差被指数加权后长期累积,最终让稳定值低于真实均值。以下是几种高效的整数运算修正方案,适配中断服务程序的快速执行要求:
偏差根源解析
你用的EMA公式本质是:
ema_new = ema_old * (2^factor - 1)/2^factor + adc * 1/2^factor
整数右移相当于截断除法,每次计算会丢弃(ema_old*(2^factor-1) + adc) % 2^factor的小数部分,平均每次引入-0.5的误差。当factor越大(新值权重越小),误差累积越明显,最终稳定值偏离真实均值。
优化方案
方案1:64位中间运算实现精确四舍五入
STM32 Cortex-M3及以上MCU支持32×32位乘法得到64位结果,利用这一点可以避免截断误差:
uint32_t ema = 0; const unsigned factor = 8; const uint64_t m = (1ULL << factor) - 1; const uint64_t round_offset = 1ULL << (factor - 1); // 中断内更新逻辑 uint64_t temp = (uint64_t)ema * m + adc[0]; temp += round_offset; // 四舍五入偏移 ema = (uint32_t)(temp >> factor);
这个方法通过64位中间值完整保留乘法结果,加偏移后再右移实现真正的四舍五入,每次更新的误差控制在±0.5,长期累积的期望偏差为0,稳定值会贴合真实均值。
方案2:轻量偏差补偿(无需64位运算)
如果MCU资源受限,可通过添加固定补偿项抵消截断误差:
uint32_t ema = 0; const unsigned factor = 8; const unsigned m = (1 << factor) - 1; const unsigned bias = 1; // 补偿截断的向下误差 // 中断内更新逻辑 ema = (ema * m + adc[0] + bias) >> factor;
原理是主动给每次计算加1,抵消平均-0.5的截断误差。若factor较小(如≤4),可适当增大bias(比如设为2),能快速让稳定值回归真实均值区间。
方案3:高精度定点数存储
用64位变量存储EMA的整数+小数部分,彻底避免精度丢失:
uint64_t ema = 0; // 高32位为整数部分,低factor位为小数部分 const unsigned factor = 8; const uint64_t m = (1ULL << factor) - 1; // 中断内更新逻辑 ema = (ema * m + (uint64_t)adc[0] << factor) >> factor; // 读取整数结果 uint32_t ema_result = (uint32_t)(ema >> factor);
该方法把ADC值左移factor位后参与运算,保留了EMA的小数精度,读取时仅需右移取整数部分,稳定后结果会精确贴合真实均值(若均值为整数),无系统性偏差。
方案选择建议
- 优先选方案1:实现简单,偏差最小,适配绝大多数STM32 MCU;
- 资源极度受限选方案2:代码量最小,运算最快;
- 追求最高精度选方案3:完全消除截断误差,适合对读数精度要求极高的场景。
内容的提问来源于stack exchange,提问作者Stefan
相关产品推荐
相关产品推荐

