STM32 ADC实战:用PA0引脚做个简易电压表(OLED显示,附完整代码)
2026/5/3 0:42:09 网站建设 项目流程

STM32 ADC实战:用PA0引脚制作OLED电压表

最近在调试一个传感器项目时,发现需要实时监测电路板上某点的电压变化。翻遍实验室没找到万用表,突然想到手边的STM32开发板自带ADC功能——这不就是个现成的数字电压表吗?花了两小时捣鼓出来的这个方案,不仅解决了燃眉之急,还让我对STM32的ADC有了更直观的认识。下面就把这个用PA0引脚和OLED屏搭建的简易电压表实现过程完整分享出来,特别适合已经掌握ADC基础但想实战应用的开发者。

1. 硬件设计与连接

1.1 核心元件选型

这次项目用到的关键部件都在常见开发套件里:

  • STM32F103C8T6最小系统板(俗称"蓝 pill")
  • 0.96寸I2C OLED显示屏(SSD1306驱动)
  • 10kΩ多圈电位器(用于模拟电压变化)
  • 杜邦线若干

选择PA0引脚(ADC1通道0)作为电压检测口有两个原因:一是这个引脚在大多数STM32板子上都引出了;二是它没有复用功能冲突。OLED屏则通过标准的I2C接口连接,节省GPIO资源。

1.2 电路连接示意图

实际接线时特别注意三点:

  1. 电位器中间引脚接PA0,两侧分别接3.3V和GND
  2. OLED的VCC接3.3V,切勿错接5V
  3. 所有GND共地连接
[电位器] [STM32] VCC ---- 3.3V GND ---- GND OUT ---- PA0 [OLED] [STM32] VCC ---- 3.3V GND ---- GND SCL ---- PB6 SDA ---- PB7

提示:若使用其他型号开发板,请根据手册确认ADC通道对应的引脚。F1系列通常PA0-PA7对应ADC1的通道0-7。

2. 软件环境配置

2.1 开发工具准备

推荐使用以下工具组合:

  • Keil MDKSTM32CubeIDE作为开发环境
  • STM32CubeMX生成初始化代码
  • OLED驱动库(通常供应商会提供)

我在项目中用的是Keil + Standard Peripheral Library的组合,这样代码更透明利于学习。如果追求开发效率,可以用HAL库配合CubeMX图形化配置。

2.2 关键外设初始化

ADC和OLED都需要正确配置时钟和接口:

// ADC时钟配置示例(72MHz主频下) RCC_ADCCLKConfig(RCC_PCLK2_Div6); // ADC时钟=12MHz RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); // GPIO初始化代码 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; // 模拟输入模式 GPIO_Init(GPIOA, &GPIO_InitStructure);

OLED的I2C初始化更简单,但要注意时序配置。实测发现STM32的I2C在标准模式下(100kHz)最稳定。

3. ADC采集核心实现

3.1 单次转换模式配置

这个电压表采用单次转换而非连续转换,主要考虑:

  • 降低功耗(适合电池供电场景)
  • 避免不必要的转换影响系统实时性
  • 简化代码逻辑

关键配置参数如下表:

参数配置值说明
ADC_ModeADC_Mode_Independent独立模式
DataAlignADC_DataAlign_Right数据右对齐
ExternalTrigConvADC_ExternalTrigConv_None软件触发
SampleTimeADC_SampleTime_55Cycles555.5周期采样时间

对应的初始化代码:

ADC_InitTypeDef ADC_InitStructure; ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init(ADC1, &ADC_InitStructure);

3.2 电压值计算与滤波

直接从ADC读取的是0-4095的原始值,需要转换为实际电压:

float voltage = (float)adc_value / 4095 * 3.3f;

但实际测试会发现数值跳动明显,这时可以加入简单的滑动平均滤波:

#define SAMPLE_SIZE 10 uint16_t samples[SAMPLE_SIZE]; uint8_t index = 0; // 在循环中采集 samples[index] = AD_GetValue(); index = (index + 1) % SAMPLE_SIZE; // 计算平均值 uint32_t sum = 0; for(int i=0; i<SAMPLE_SIZE; i++) sum += samples[i]; uint16_t avg = sum / SAMPLE_SIZE;

4. OLED显示优化技巧

4.1 界面布局设计

有限的空间(128x64像素)需要合理利用:

  • 顶部显示原始ADC值(直观反映采集状态)
  • 中间大号字体显示电压值(重点突出)
  • 底部留作状态提示区
// 显示示例代码 OLED_ShowString(1, 1, "ADC:"); OLED_ShowNum(1, 5, adc_value, 4); OLED_SetFontSize(16); OLED_ShowString(3, 1, "Volt:"); OLED_ShowFloat(3, 40, voltage, 2); OLED_SetFontSize(8); OLED_ShowString(5, 1, "PA0 Voltage Monitor");

4.2 刷新策略优化

频繁全屏刷新会导致闪烁,采用局部刷新技术:

  1. 只在数值变化时更新对应区域
  2. 使用反色效果突出变化
  3. 添加简单的动画效果(如进度条)

实测将刷新间隔控制在200ms左右,既能保证实时性又不会影响显示效果。

5. 完整代码实现

5.1 工程文件结构

建议采用模块化组织:

/Project ├── CMSIS ├── Libraries ├── User │ ├── main.c │ ├── adc.c │ ├── oled.c │ └── delay.c └── Output

5.2 核心代码片段

主循环处理逻辑:

while(1) { // 采集电压 adc_value = get_filtered_adc(); voltage = (float)adc_value / 4095 * 3.3f; // 更新显示 if(adc_value != last_adc) { update_display(adc_value, voltage); last_adc = adc_value; } // 状态检测 if(voltage > 3.0) show_warning(); Delay_ms(200); }

ADC驱动关键函数:

uint16_t AD_GetValue(void) { ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); ADC_SoftwareStartConvCmd(ADC1, ENABLE); while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); return ADC_GetConversionValue(ADC1); }

6. 常见问题排查

调试时遇到的几个典型问题及解决方案:

  1. ADC读数不稳定

    • 检查电源是否干净(可并联0.1μF电容)
    • 适当增加采样时间(如239.5周期)
    • 确保模拟地与数字地单点连接
  2. OLED显示异常

    • 确认I2C上拉电阻(通常4.7kΩ)
    • 检查地址是否正确(0x3C或0x3D)
    • 降低I2C时钟频率测试
  3. 电压测量偏差

    • 校准参考电压(有些板载LDO实际输出3.28V)
    • 用万用表测量实际电压对比
    • 检查分压电阻精度(如果使用外部分压)

这个项目最让我惊喜的是,用最基础的硬件实现了实用功能。后来在调试其他电路时,这个自制的电压表成了我的得力助手——毕竟它不仅能显示当前值,还能通过代码扩展记录历史最大值、最小值等实用功能。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询