STM32F103 ADC 实战:测量电位器电压并串口输出

在 STM32 单片机开发中,ADC(模数转换器)是最常用的外设之一。像电位器、光敏电阻、热敏电阻这类输出模拟电压的器件,都需要通过 ADC 把模拟量转换为数字量,才能被单片机处理。

本文以 STM32F103 为例,完成一个非常经典的小实验:使用 ADC 采集电位器输出电压,并通过串口打印采样值和对应电压。这类实验非常适合作为 ADC 入门,也能为后续做传感器采集打基础。

一、实验目标

  • 掌握 STM32F103 ADC 的基本工作原理
  • 学会配置 GPIO 为模拟输入模式
  • 学会配置 ADC 单通道采样
  • 能够读取电位器电压并换算成实际电压值
  • 通过 USART 串口输出结果,便于调试观察

二、硬件连接

实验中使用一个三脚电位器,中间滑动端输出可变电压。

  • 电位器一端接 3.3V
  • 电位器另一端接 GND
  • 中间滑动端接到 STM32F103 的 PA1

这里选择 PA1,对应的 ADC 通道是 ADC_Channel_1

注意:ADC 输入电压不能超过芯片参考电压范围,通常为 0~3.3V,否则可能导致采样异常,严重时还可能损坏芯片。

三、ADC 基本原理

STM32F103 的 ADC 是 12 位逐次逼近型 ADC,也就是说:

  • 输入电压范围:0 ~ 3.3V
  • 转换结果范围:0 ~ 4095
  • 分辨率:3.3 / 4096 ≈ 0.000805V

因此,实际电压可以按下面公式换算:

Voltage = ADC_Value * 3.3 / 4095

比如:

  • ADC 值为 0,对应 0V
  • ADC 值约为 2048,对应约 1.65V
  • ADC 值接近 4095,对应约 3.3V

四、配置思路

整个程序可以分成四步:

  1. 开启 GPIOA 和 ADC1 时钟
  2. 把 PA1 配置为模拟输入
  3. 配置 ADC1 为独立模式、单通道、软件触发
  4. 启动转换并读取结果

如果还要把结果打印出来,就再配置一个串口即可。

五、核心代码实现

1. ADC 初始化

#include "stm32f10x.h"
#include 

void ADC1_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    ADC_InitTypeDef ADC_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE);

    // ADC 时钟不能太高,通常需要分频
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);

    // PA1 配置为模拟输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
    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 = 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);

    ADC_Cmd(ADC1, ENABLE);

    // 复位校准
    ADC_ResetCalibration(ADC1);
    while(ADC_GetResetCalibrationStatus(ADC1));

    // 开始校准
    ADC_StartCalibration(ADC1);
    while(ADC_GetCalibrationStatus(ADC1));
}

2. 读取指定 ADC 通道

uint16_t ADC_GetValue(uint8_t channel)
{
    ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_55Cycles5);

    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);

    return ADC_GetConversionValue(ADC1);
}

3. 主函数测试

下面示例中,循环读取电位器电压,并通过串口打印原始 ADC 值与换算后的电压值。

int main(void)
{
    uint16_t adc_value;
    float voltage;

    USART1_Init();
    ADC1_Init();

    while(1)
    {
        adc_value = ADC_GetValue(ADC_Channel_1);
        voltage = adc_value * 3.3f / 4095.0f;

        printf("ADC = %d, Voltage = %.2fV
", adc_value, voltage);

        Delay_ms(500);
    }
}

六、运行现象

下载程序后,打开串口助手,旋转电位器旋钮,可以看到串口输出的数据不断变化,例如:

ADC = 15, Voltage = 0.01V
ADC = 1024, Voltage = 0.83V
ADC = 2048, Voltage = 1.65V
ADC = 3072, Voltage = 2.48V
ADC = 4090, Voltage = 3.30V

这说明 ADC 已经成功采集到了电位器的模拟电压变化。

七、几个容易出错的点

1. GPIO 没有配置成模拟输入

如果 PA1 没有设置为 GPIO_Mode_AIN,采样值可能不稳定,甚至一直异常。

2. ADC 时钟配置不合适

STM32F1 的 ADC 时钟一般不能超过 14MHz,所以通常要配置分频,例如 RCC_PCLK2_Div6

3. 忘记做校准

ADC 在启用后最好执行复位校准和启动校准,否则测量值可能误差较大。

4. 参考电压不稳定

如果开发板 3.3V 电源波动比较大,那么 ADC 的换算电压也会跟着受影响。

5. 电位器接线错误

中间脚必须接 ADC 输入端,如果接错脚位,读数可能固定不变。

八、拓展方向

完成单通道采样后,还可以继续做这些扩展:

  • 改成 连续转换模式
  • 增加 多通道采样
  • 结合 DMA 自动搬运数据
  • 用 ADC 读取光敏电阻、热敏电阻、摇杆等模拟传感器
  • 加入滤波算法,如均值滤波、中值滤波,提高稳定性

九、总结

这篇实验的重点并不复杂,但非常实用。通过它可以掌握 STM32F103 ADC 的基础使用流程:配置模拟输入 → 初始化 ADC → 启动转换 → 读取结果 → 换算电压

对于刚接触 STM32 的同学来说,电位器测压是一个非常适合练手的项目。等把这个实验吃透后,再去做多通道采样、DMA 采集、传感器融合,就会轻松很多。

如果你也在学习 STM32,建议亲手把这个实验跑一遍,并尝试把串口输出改成 OLED 显示,效果会更直观。

Logo

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

更多推荐