别再只会用万用表了!用STM32的ADC做个简易电压表(附OLED显示代码)

张开发
2026/4/15 17:45:13 15 分钟阅读

分享文章

别再只会用万用表了!用STM32的ADC做个简易电压表(附OLED显示代码)
用STM32打造高精度数字电压表从ADC原理到OLED显示实战在电子制作和嵌入式开发中测量电压是最基础却又最频繁的需求之一。虽然万用表是工程师工具箱里的标配但当我们希望将电压测量功能集成到自己的项目中时基于STM32的ADC模数转换器方案就显示出独特优势。本文将带你从零开始利用STM32F103C8T6开发板和0.96寸OLED屏幕构建一个具有专业显示效果的数字电压表。这个项目不仅能让你的开发板变身实用测量工具更能深入理解ADC的采样精度、参考电压校准等关键概念。1. 硬件准备与电路设计1.1 元器件选型指南制作电压表的核心器件包括主控芯片STM32F103C8T6Blue Pill开发板内置12位精度ADC显示模块SSD1306驱动的0.96寸OLED128x64分辨率电压分压电路1%精度的金属膜电阻10kΩ和3.3kΩ组成分压网络参考电压源TL431精密电压基准可选用于提高测量精度对于测量范围扩展建议采用以下电阻配置测量范围R1值R2值分压比0-3.3V直接连接无1:10-10V10kΩ3.3kΩ约1:4// 电压分压计算函数示例 float calculate_voltage(uint16_t adc_value, float vref, float divider_ratio) { return (adc_value * vref / 4095.0f) * divider_ratio; }1.2 硬件连接示意图正确连接是项目成功的基础请按以下方式接线ADC输入将待测电压接入PA0ADC1_IN0OLED接口SCL → PB6I2C1_SCLSDA → PB7I2C1_SDA电源部分确保开发板3.3V稳压输出稳定注意测量超过3.3V的电压时必须使用分压电路否则可能损坏ADC引脚2. ADC模块深度配置2.1 关键参数优化设置STM32的ADC性能很大程度上取决于几个核心参数的配置ADC_InitTypeDef ADC_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);采样时间对测量结果有显著影响不同时钟周期下的噪声表现采样周期转换时间适用场景1.5周期1.17μs高速但精度低7.5周期2.25μs平衡模式55.5周期5.43μs高精度测量2.2 校准与参考电压处理出厂校准是提升精度的关键步骤ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1));对于需要更高精度的场合可以外接基准电压源到VREF引脚。实测发现使用TL431作为2.5V基准时温度漂移可降低至±50ppm/℃。3. 软件架构与核心算法3.1 多阶滤波算法实现原始ADC数据往往带有噪声采用复合滤波策略效果更佳硬件层面在ADC输入端添加0.1μF去耦电容软件层面实现移动平均中值滤波#define FILTER_WINDOW 16 uint16_t adc_filter(uint16_t new_value) { static uint16_t buffer[FILTER_WINDOW]; static uint8_t index 0; static uint32_t sum 0; sum - buffer[index]; buffer[index] new_value; sum new_value; index (index 1) % FILTER_WINDOW; return sum / FILTER_WINDOW; }3.2 电压转换与显示优化将ADC原始值转换为实际电压时需要注意浮点运算效率void update_display(uint16_t raw) { static char buf[8]; float voltage raw * 3.3f / 4095.0f; // 使用定点数运算优化性能 uint16_t integer (uint16_t)voltage; uint16_t decimal (uint16_t)((voltage - integer) * 100); OLED_ShowNum(2, 8, integer, 1); OLED_ShowNum(2, 10, decimal, 2); }4. 高级功能扩展4.1 多量程自动切换通过继电器或模拟开关实现量程自动切换void auto_range_switch(float voltage) { if(voltage 3.3f current_range RANGE_LOW) { set_high_range(); // 切换到高压量程 HAL_Delay(100); // 等待电路稳定 } else if(voltage 3.0f current_range RANGE_HIGH) { set_low_range(); // 切回低压量程 } }4.2 数据记录与导出添加SD卡模块实现测量数据存储void log_measurement(float voltage) { static FIL file; static char line[32]; sprintf(line, %.3f\n, voltage); f_write(file, line, strlen(line), NULL); }5. 性能优化与故障排查5.1 常见问题解决方案读数跳变严重检查电源稳定性增加采样周期优化滤波算法参数测量值偏置重新校准ADC检查分压电阻精度测量实际VREF电压5.2 低功耗设计技巧当需要电池供电时void enter_low_power_mode() { ADC_SoftwareStartConvCmd(ADC1, DISABLE); HAL_ADC_Stop(hadc1); __HAL_ADC_DISABLE(hadc1); HAL_I2C_DeInit(hi2c1); }在最近的一个智能电池监测项目中这种电压测量方案成功将静态功耗控制在150μA以下使设备续航时间延长至6个月。

更多文章