STM32F103C8T6 ADC实战:从零构建LCD1602数字电压表

1. 项目概述与硬件准备

在嵌入式开发领域,ADC(模数转换器)的应用堪称基本功。这次我们选择STM32F103C8T6这款性价比极高的Cortex-M3内核芯片,配合LCD1602显示屏,打造一个实用型数字电压表。不同于简单的理论讲解,我们将从芯片选型、电路设计到代码调试,完整呈现开发全流程。

核心硬件清单

  • STM32F103C8T6最小系统板(或Proteus仿真模型)
  • LCD1602液晶显示屏(兼容HD44780控制器)
  • 10kΩ多圈精密电位器(用于电压调节)
  • 0.1μF去耦电容若干
  • 3.3V稳压电路(若使用实际硬件)

提示:实际硬件搭建时,建议在ADC输入引脚前加入RC低通滤波(如1kΩ电阻串联100nF电容到地),可有效抑制高频干扰。

2. 硬件电路设计要点

2.1 ADC输入电路设计

STM32F103的ADC输入电压范围为0-3.3V,对应12位分辨率(4096个量化等级)。在Proteus仿真中,我们需要特别注意参考电压的设定:

// 电压计算公式
float voltage = (float)adc_value * (3.4 / 4096);  // 注意仿真中使用3.4V而非3.3V

这个看似奇怪的3.4V数值源于Proteus中STM32模型的内部参考电压校准特性。实际测试表明,使用3.4V作为计算基准时,仿真结果更接近预期值。

2.2 LCD1602接口连接

推荐使用4位数据线模式节省IO资源,典型连接方式:

LCD引脚 STM32连接 备注
RS PB0 寄存器选择
RW GND 始终设置为写模式
EN PB1 使能信号
D4-D7 PB12-PB15 数据线高四位
VSS GND 电源地
VDD 5V 注意逻辑电平匹配

注意:虽然STM32 GPIO为3.3V电平,但大多数LCD1602模块在5V供电时仍能可靠识别3.3V信号。若出现显示异常,可考虑添加电平转换电路。

3. 软件架构与关键代码解析

3.1 ADC初始化配置

ADC的配置需要关注几个关键参数:

void ADC1_Init(void) {
    ADC_InitTypeDef ADC_InitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);  // 设置ADC时钟为12MHz(72MHz/6)
    
    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);
    
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5);
    ADC_Cmd(ADC1, ENABLE);
    
    // ADC校准流程(实际硬件必需)
    ADC_ResetCalibration(ADC1);
    while(ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while(ADC_GetCalibrationStatus(ADC1));
    
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}

3.2 电压计算与显示处理

在main循环中,我们需要处理原始ADC值并转换为可显示的电压:

while(1) {
    uint16_t adc_value = ADC_GetConversionValue(ADC1);
    float voltage = (float)adc_value * (3.4 / 4096);  // 关键计算公式
    
    // 电压值分解为整数和小数部分
    uint8_t integer_part = (uint8_t)voltage;
    uint8_t decimal_part = (uint8_t)((voltage - integer_part) * 10);
    
    // LCD显示更新
    LCD_SetCursor(0, 0);
    LCD_WriteString("Voltage:");
    LCD_SetCursor(8, 0);
    LCD_WriteChar(integer_part + '0');
    LCD_WriteChar('.');
    LCD_WriteChar(decimal_part + '0');
    LCD_WriteChar('V');
    
    Delay_ms(200);  // 适当延时防止刷新过快
}

4. Proteus仿真技巧与调试

4.1 仿真环境搭建要点

  1. 元件选择

    • STM32F103C8T6(需加载正确固件)
    • LCD1602(带HD44780控制器)
    • POT-HG(高精度电位器)
  2. 关键连接

    • 电位器中间引脚接PA1(ADC输入)
    • 两端分别接3.3V和GND
    • LCD按前述接线图连接
  3. 调试工具配置

    • 使用Proteus自带的虚拟示波器监控ADC输入
    • 开启STM32寄存器查看窗口观察ADC数据

4.2 常见问题解决方案

问题1:ADC读数不稳定

  • 检查电位器连接是否牢靠
  • 在仿真设置中增加"ANALOGUE SAMPLE TIME"值
  • 代码中增加软件滤波(如移动平均)
#define SAMPLE_COUNT 10
uint32_t filtered_adc = 0;
for(uint8_t i=0; i<SAMPLE_COUNT; i++) {
    filtered_adc += ADC_GetConversionValue(ADC1);
    Delay_ms(5);
}
filtered_adc /= SAMPLE_COUNT;

问题2:LCD显示乱码

  • 检查初始化时序是否满足HD44780规范
  • 确认总线模式设置(4位/8位)与硬件匹配
  • 调整操作间隔时间,特别是EN使能脉冲宽度

5. 性能优化与功能扩展

5.1 精度提升方案

虽然基本设计满足±0.1V精度要求,但通过以下改进可达更高精度:

  1. 参考电压优化

    • 实际硬件中采用外部精密基准源(如TL431)
    • 在代码中动态校准参考电压
  2. 数字滤波算法

    • 中值滤波+移动平均组合
    • 卡尔曼滤波(适合动态测量)
// 简易中值滤波实现
uint16_t median_filter(uint16_t new_value) {
    static uint16_t buffer[5] = {0};
    static uint8_t index = 0;
    
    buffer[index++] = new_value;
    if(index >= 5) index = 0;
    
    // 排序取中值(简化实现)
    uint16_t temp[5];
    memcpy(temp, buffer, sizeof(temp));
    bubble_sort(temp, 5);  // 需实现排序函数
    return temp[2];
}

5.2 功能扩展方向

  1. 多通道测量

    • 配置ADC扫描模式
    • 循环检测多个输入通道
  2. 数据记录功能

    • 添加EEPROM存储历史数据
    • 通过串口输出测量结果
  3. 报警阈值设置

    • 添加按键输入接口
    • 实现电压超限声光报警
// 电压超限检查示例
if(voltage > MAX_VOLTAGE) {
    GPIO_SetBits(GPIOC, GPIO_Pin_13);  // 点亮LED
    Buzzer_On();  // 触发蜂鸣器
} else {
    GPIO_ResetBits(GPIOC, GPIO_Pin_13);
    Buzzer_Off();
}

6. 实际项目经验分享

在实验室测试时发现一个有趣现象:当使用USB供电时,ADC测量结果会出现周期性波动。经过示波器检查,发现是电源纹波导致。解决方法包括:

  1. 在VDDA引脚增加10μF钽电容+0.1μF陶瓷电容组合
  2. 软件上增加50Hz工频滤波算法
  3. 避免将模拟地与数字地单点连接的位置靠近MCU的开关电源引脚

另一个常见问题是LCD在低温环境下响应变慢。针对这种情况可以:

  • 在初始化前增加500ms延时
  • 降低总线通信速率
  • 采用负温度系数热敏电阻进行温度补偿
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐