在智能硬件项目中,很多输入信号并不是简单的开关状态,而是连续变化的模拟量。电位器旋钮、光敏电阻、温度传感器、压力传感器这类模块,经常会输出随环境或操作变化而变化的电压信号。对于程序来说,这些变化不能直接变成可计算的数据,需要经过 ADC 模数转换,才能被代码读取、判断和处理。

本实验使用 CanMV K210 开发板通过 I2C 总线连接 PCF8591 模块,读取电位器接入 AIN0 后产生的模拟电压变化。程序会把 PCF8591 读取到的 0~255 原始 ADC 数值转换成百分比、电压估算值和状态等级,并通过串口持续打印。同时,代码还会把读取到的数值写入 PCF8591 的 AOUT,使模拟输出跟随旋钮变化,可用于控制外接 LED 亮度或观察模拟电压输出效果。

学习目标 说明
理解模拟量输入 认识电位器旋转如何产生连续变化的模拟电压
掌握 ADC 采集 使用 PCF8591 将 AIN0 输入电压转换成 0~255 数字量
理解数据换算 将 ADC 原始值转换成百分比、电压估算值和状态等级
掌握 AOUT 输出 使用 PCF8591 的模拟输出端跟随电位器输入变化
建立采集闭环思路 形成“模拟输入采集 → 程序处理 → 模拟输出反馈”的实验链路

这类实验的价值在于建立“真实信号进入程序”的理解方式。电位器旋转时,硬件层产生的是连续变化的电压;PCF8591 将电压转换为数字量;Python 程序再把数字量转换成百分比、电压和状态等级。这个过程非常接近真实传感器项目中的数据采集、数据清洗、状态判断和输出控制逻辑。

理论基础

电位器可以理解成一个可调电阻器。旋转旋钮时,输出端电压会随旋钮位置发生变化。这个变化不是简单的高电平或低电平,而是一段连续变化的模拟电压。普通 GPIO 更适合读取开关量,例如按键是否按下、传感器是否触发;而电位器这种连续变化的信号,需要 ADC 模数转换模块才能被程序稳定读取。

PCF8591 在本实验中承担 ADC 和 DAC 两个角色。AIN0 用于读取电位器输出电压,属于模拟输入;AOUT 用于输出一个跟随 ADC 数值变化的模拟电压,属于模拟输出。程序通过 pot_adc.read(ADC_CHANNEL) 获取 AIN0 的采样值,再通过 pot_adc.write(value) 把采样值写入 AOUT。这样就形成了从模拟输入到数字处理,再从数字值回到模拟输出的简单闭环。

本实验中,CanMV K210 通过软件 I2C 与 PCF8591 通信。物理引脚 6 作为 SCL,物理引脚 7 作为 SDA。电位器输出端接入 PCF8591 的 AIN0,程序读取到的 ADC 原始值通常在 0~255 范围内变化。VREF = 3.3 用于估算电压,表示程序默认把 ADC 最大值 255 对应到 3.3V。如果模块实际使用 5V 供电,电压估算值需要把 VREF 改成 5.0

电位器旋钮
旋转改变输出电压

PCF8591 AIN0
接收模拟输入

ADC 转换
0~255 数字量

I2C 通信
SCL / SDA

CanMV K210
Python 程序读取

数据处理
百分比 / 电压 / 等级

串口输出
显示采样结果

PCF8591 AOUT
模拟输出跟随变化

VCC
参考电压来源

GND
公共参考地

外接 LED 或模拟输出端
观察输出变化

这张流程图展示的是电路和数据之间的关系。电位器负责产生模拟电压,PCF8591 负责完成 ADC 转换,I2C 总线负责传输数据,CanMV K210 程序负责换算和判断,AOUT 再把处理后的数值反馈成模拟输出。理解这条链路后,后续把电位器替换成光敏模块、声音模块或其他模拟传感器时,采集思路基本一致。

模拟量实验中还需要关注抖动问题。电位器旋转时,接触电阻、电源波动和连接线都会让采样值出现轻微跳变。如果每次只读取一次,串口数据可能忽高忽低。代码中使用 SAMPLE_COUNT = 5 做简单平均滤波,通过多次采样再取平均值,让输出结果更稳定。

硬件设施

本实验围绕 CanMV K210、PCF8591 模数转换模块和电位器展开。代码没有使用 LCD、摄像头、按键、蜂鸣器或其他传感器,因此这些模块不作为本节重点。软件侧主要依赖 machine.I2Cfpioa_manager.fmpcf8591time.sleep,它们分别负责 I2C 通信、引脚功能绑定、PCF8591 模块读写和采样延时控制。

接线关系可以先通过下面这张图片建立整体印象。CanMV K210 通过物理引脚 6 和物理引脚 7 连接 PCF8591 的 SCL、SDA,电位器输出端接入 AIN0,AOUT 可以根据实验需要连接外部 LED 或作为模拟输出端观察。

在这里插入图片描述

硬件 / 软件 作用 说明
CanMV K210 开发板 实验运行平台 负责执行 MicroPython 程序,并通过 I2C 总线与 PCF8591 通信
PCF8591 模块 ADC / DAC 转换模块 负责把电位器模拟电压转换成 0~255 的数字量,同时提供 AOUT 模拟输出能力
电位器 模拟输入源 旋转过程中输出连续变化的模拟电压,接入 PCF8591 的 AIN0 通道
AIN0 模拟输入通道 代码中 ADC_CHANNEL = 0,表示读取 PCF8591 第 0 路模拟输入
AOUT 模拟输出通道 程序通过 pot_adc.write(value) 输出与电位器读数对应的模拟量
物理引脚 6 I2C SCL 软件 I2C 的时钟线
物理引脚 7 I2C SDA 软件 I2C 的数据线
machine.I2C I2C 通信模块 用于创建软件 I2C 对象,设置频率、时钟引脚和数据引脚
pcf8591 PCF8591 驱动模块 提供 read()write() 方法,用于读取 ADC 和控制 AOUT
time.sleep 延时模块 用于采样间隔、打印间隔和降低数值抖动

实验中使用的核心模块如下。PCF8591 负责模拟量输入和模拟量输出,电位器负责产生可调模拟电压,CanMV K210 负责读取、计算和输出状态。接线时重点检查 SCL、SDA、AIN0、AOUT、VCC 和 GND,不要只关注信号线而忽略公共地。

PCF8591 模块与电位器实验零件图

接线关系可以根据代码中的 I2C_SCL_PINI2C_SDA_PINADC_CHANNELpot_adc.read() 推导。当前代码没有直接给出模块电源和地线连接,但 PCF8591 正常工作必须连接 VCC 和 GND。电位器信号端接入 AIN0,程序通过 ADC_CHANNEL = 0 读取该通道。AOUT 是否外接 LED 或其他模拟量负载,需要根据实际实验板或外部接线决定。

开发板 / 模块连接 PCF8591 / 外设接口 代码变量 对应功能 说明
物理引脚 6 SCL I2C_SCL_PIN I2C 时钟线 软件 I2C 使用引脚 6 作为时钟信号
物理引脚 7 SDA I2C_SDA_PIN I2C 数据线 软件 I2C 使用引脚 7 作为数据信号
PCF8591 AIN0 电位器输出端 ADC_CHANNEL = 0 模拟输入采集 电位器旋转时,AIN0 读取到不同 ADC 数值
PCF8591 AOUT 外接 LED 或模拟输出端 pot_adc.write(value) 模拟输出控制 AOUT 输出会跟随读取到的电位器数值变化
VCC 模块电源 VREF = 3.3 参考电压 代码默认按 3.3V 估算电压,模块接 5V 时应调整为 5.0
GND 公共地 无单独变量 电路参考地 开发板、PCF8591 和外接负载需要共地,保证信号参考一致

完成接线后的整体状态如下。检查时重点确认电位器信号端是否接入 AIN0,PCF8591 的 SCL / SDA 是否接到代码指定的引脚 6 和引脚 7,AOUT 外接 LED 时是否串联合适限流电阻。AOUT 不适合直接驱动大电流负载,只适合做小信号模拟输出或配合后级电路使用。

CanMV K210 电位器与 PCF8591 实验成品图

实验现象 正常表现 异常提示
程序启动 串口提示 PCF8591 初始化完成 如果没有输出,检查程序运行状态和串口连接
旋转电位器 ADC、百分比、电压和等级随旋钮变化 如果数值不变化,检查电位器输出端是否接入 AIN0
AOUT 输出 外接 LED 亮度或模拟输出随旋钮变化 如果无变化,检查 AOUT 接线和外接电路
数值轻微波动 ADC 读数在小范围内跳动 属于模拟量常见现象,可通过平均滤波减弱
电压显示 串口显示估算电压值 如果模块接 5V,需要把 VREF 改成 5.0
程序停止 捕获停止操作后 AOUT 输出归零 如果外设仍有输出,检查停止处理是否执行

软件代码

本实验代码围绕 PCF8591 的模拟输入采集和模拟输出控制展开。程序先配置软件 I2C 引脚,再创建 PCF8591 对象;循环阶段读取电位器 ADC 原始值,通过平均滤波降低抖动,再计算百分比、电压估算值和状态等级。读取到的数值同时写入 AOUT,使模拟输出跟随旋钮变化。

软件环境 作用 检查重点
CanMV IDE 编辑、运行和调试 K210 程序 能识别开发板串口,并能正常运行基础 print() 测试
CanMV 固件 提供 machine.I2Cfpioa_manager 等模块 固件环境需要支持当前 I2C 与 FPIOA 写法
USB 串口驱动 让电脑识别开发板串口 串口工具中能看到对应端口
pcf8591.py PCF8591 驱动文件 需要与主程序放在同一运行目录下,保证 import pcf8591 正常
串口终端 查看采样结果 旋转电位器时能看到 ADC、百分比、电压和等级变化
外接 AOUT 负载 观察模拟输出 外接 LED 时需要限流电阻,不建议直接驱动大功率负载
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
CanMV K210 电位器实验 Demo

实验现象:
旋转电位器时,串口会显示 ADC 原始值、百分比、电压估算和状态等级。
同时,PCF8591 的 AOUT 会跟随电位器数值变化,可用于控制外接 LED 亮度。

硬件逻辑:
电位器输出的是连续变化的模拟电压。
PCF8591 负责把模拟电压转换成 0~255 的数字量。
程序读取数字量后,再通过 AOUT 输出对应的模拟电压。
"""

from machine import I2C
from fpioa_manager import fm
import pcf8591
from time import sleep


# =========================
# 硬件配置区
# =========================

I2C_SCL_PIN = 6
I2C_SDA_PIN = 7

ADC_CHANNEL = 0          # 电位器连接到 PCF8591 的 AIN0
ADC_MAX_VALUE = 255      # PCF8591 是 8 位 ADC,范围 0~255
VREF = 3.3               # 参考电压,常见为 3.3V;如果模块接 5V,可改成 5.0

SAMPLE_COUNT = 5         # 多次采样次数,用于降低抖动
PRINT_INTERVAL = 0.2     # 串口打印间隔,单位秒


# =========================
# 全局对象
# =========================

pot_adc = None


# =========================
# 初始化函数
# =========================

def setup():
    """初始化 I2C 和 PCF8591 模块"""
    global pot_adc

    i2c = I2C(
        I2C.I2C_SOFT,
        freq=400000,
        scl=I2C_SCL_PIN,
        sda=I2C_SDA_PIN,
        gscl=fm.fpioa.GPIOHS1,
        gsda=fm.fpioa.GPIOHS2
    )

    pot_adc = pcf8591.PCF8591(i2c)

    # AOUT 默认输出 0,避免上电后外接 LED 误亮
    pot_adc.write(0)

    print("PCF8591 初始化完成")
    print("旋转电位器,观察 ADC 数值、百分比、电压和输出变化")


# =========================
# 数据处理函数
# =========================

def read_pot_value():
    """
    读取电位器数值,并做简单平均滤波
    平均滤波可以减少模拟量轻微抖动
    """
    total = 0

    for _ in range(SAMPLE_COUNT):
        total += pot_adc.read(ADC_CHANNEL)
        sleep(0.01)

    return int(total / SAMPLE_COUNT)


def value_to_percent(value):
    """把 0~255 的 ADC 数值转换成百分比"""
    return int(value * 100 / ADC_MAX_VALUE)


def value_to_voltage(value):
    """根据 ADC 数值估算输入电压"""
    return value * VREF / ADC_MAX_VALUE


def get_level(percent):
    """根据百分比判断当前旋钮状态等级"""
    if percent < 20:
        return "LOW"
    elif percent < 50:
        return "MEDIUM"
    elif percent < 80:
        return "HIGH"
    else:
        return "MAX"


def update_output(value):
    """
    将电位器读数写入 AOUT
    AOUT 输出会跟随旋钮变化,可用于控制 LED 亮度
    """
    pot_adc.write(value)


# =========================
# 主循环
# =========================

def loop():
    """循环读取电位器并控制模拟输出"""
    last_value = -1

    while True:
        value = read_pot_value()
        percent = value_to_percent(value)
        voltage = value_to_voltage(value)
        level = get_level(percent)

        update_output(value)

        # 数值变化较小时不重复刷屏,方便观察关键变化
        if abs(value - last_value) >= 2:
            print(
                "ADC: {:3d} | Percent: {:3d}% | Voltage: {:.2f}V | Level: {}".format(
                    value,
                    percent,
                    voltage,
                    level
                )
            )
            last_value = value

        sleep(PRINT_INTERVAL)


# =========================
# 程序入口
# =========================

if __name__ == '__main__':
    try:
        setup()
        loop()
    except KeyboardInterrupt:
        if pot_adc:
            pot_adc.write(0)
        print("程序已停止,AOUT 已关闭")

这段程序可以分成三个层级。底层是 I2C 初始化和 PCF8591 对象创建,负责建立 CanMV K210 与 PCF8591 之间的通信;中间层是 read_pot_value()value_to_percent()value_to_voltage()get_level(),负责把原始 ADC 采样值转换成更容易理解的数据;上层是 loop()update_output(),负责持续采集、打印结果,并让 AOUT 跟随电位器变化。

配置区把 I2C 引脚、ADC 通道、最大采样值、参考电压、采样次数和打印间隔集中放在一起。后续修改接线、调整电压估算或改变采样节奏时,不需要进入业务函数内部查找。VREF 尤其需要结合实际供电确认,模块接 3.3V 时保持 3.3,模块接 5V 时改成 5.0

函数名 功能 对应现象
setup() 初始化软件 I2C 和 PCF8591 对象 串口提示 PCF8591 初始化完成,AOUT 默认关闭
read_pot_value() 多次读取 AIN0 并计算平均值 旋转电位器时获得更稳定的 ADC 数值
value_to_percent(value) 将 ADC 原始值转换成百分比 串口显示旋钮当前位置比例
value_to_voltage(value) 根据参考电压估算输入电压 串口显示近似电压值,例如 1.65V
get_level(percent) 根据百分比划分状态等级 串口显示 LOW、MEDIUM、HIGH 或 MAX
update_output(value) 将 ADC 数值写入 PCF8591 AOUT 外接 LED 亮度或模拟输出随旋钮变化
loop() 持续采集、换算、输出和打印 串口不断显示电位器变化后的数据
KeyboardInterrupt 异常处理 程序停止时关闭 AOUT 手动停止程序后,模拟输出恢复为 0

主循环 loop() 让采集过程持续运行。每一轮循环都会读取电位器数值,计算百分比、电压和状态等级,再调用 update_output(value) 更新 AOUT。last_value 用来记录上一次打印的 ADC 数值,只有当前值与上一次差值大于等于 2 时才打印,避免轻微抖动导致串口刷屏。PRINT_INTERVAL = 0.2 控制输出节奏,既能保持实时性,又不会让日志过于密集。

扩展应用

电位器实验涉及模拟量采集、I2C 通信、参考电压和模拟输出,调试时不能只观察代码是否报错,还要结合串口数据、旋钮变化和 AOUT 输出效果判断问题来源。

问题现象 可能原因 处理思路
串口没有任何输出 程序没有运行、串口未打开、开发板未连接成功 确认脚本已经执行,检查串口工具和连接状态
提示 PCF8591 初始化失败或读取异常 I2C 接线错误、SCL / SDA 接反、模块地址不匹配 核对引脚 6 和 7 是否分别连接到 SCL 和 SDA,确认 PCF8591 模块供电正常
ADC 数值一直不变 电位器没有接到 AIN0、旋钮信号端接线错误、通道配置不一致 检查电位器中间信号端是否连接 AIN0,确认 ADC_CHANNEL = 0 与实际接线一致
ADC 数值跳动明显 模拟信号有噪声、接线松动、采样次数偏少 检查杜邦线连接,适当增大 SAMPLE_COUNT,观察平均滤波后的变化
电压显示不准确 VREF 与 PCF8591 实际供电电压不一致 模块接 3.3V 时保持 VREF = 3.3,模块接 5V 时改为 VREF = 5.0
外接 LED 亮度没有变化 AOUT 没有接入 LED 控制电路、LED 接法不匹配、负载连接异常 确认 AOUT 接线和限流电阻,检查 LED 是否适合模拟输出控制
串口刷屏过快 打印间隔过短或数值变化阈值过小 调大 PRINT_INTERVAL,或提高 abs(value - last_value) 的判断阈值
程序停止后外设仍有输出 异常退出没有执行 AOUT 关闭 保留 KeyboardInterrupt 处理,停止时执行 pot_adc.write(0)
提示找不到 pcf8591 驱动文件没有上传,或文件名不匹配 确认 pcf8591.py 与主程序位于同一运行目录

电位器实验本质上是一次模拟量输入采集实验。程序读取连续变化的电压,把它转换为数字量,再根据数字量进行显示、判断和输出控制。这个模式可以自然迁移到很多智能硬件项目中,例如亮度调节、音量旋钮、阈值设置、速度控制、传感器标定和用户交互参数输入。

应用场景 实现思路 可扩展能力
LED 亮度调节 电位器读数写入 AOUT,让外接 LED 亮度随旋钮变化 可进一步加入 PWM 控制,实现更细腻的亮度变化
参数旋钮输入 将百分比作为程序参数,例如灵敏度、阈值或速度档位 可扩展为菜单参数调节或设备配置入口
传感器采集入门 将电位器替换为光敏、电压检测等模拟传感器 可扩展为环境数据采集课程
状态等级判断 根据百分比划分 LOW、MEDIUM、HIGH、MAX 可用于报警阈值、档位识别和自动控制逻辑
模拟输出控制 使用 PCF8591 AOUT 输出跟随输入变化 可扩展为模拟电压控制或执行器控制实验
数据稳定性教学 通过多次采样和平均滤波观察抖动改善效果 可继续讲解滑动平均、中值滤波和阈值防抖
串口数据监测 将 ADC、百分比、电压和等级打印到串口 后续可扩展 LCD 显示、Web 面板或数据上传

从工程角度看,当前代码已经具备比较完整的数据采集链路。配置区集中管理硬件参数,初始化函数负责建立通信对象,采样函数负责获取原始数据,转换函数负责把底层数值变成业务信息,输出函数负责把处理结果反馈给外设。后续无论替换为其他模拟传感器,还是把串口输出改成屏幕显示、网络上传、AI 控制参数,都可以沿用这套结构。

总结

本实验通过 CanMV K210 和 PCF8591 完成了电位器模拟量采集与 AOUT 跟随输出控制,核心能力包括软件 I2C 通信、PCF8591 初始化、AIN0 模拟输入读取、8 位 ADC 数值处理、平均滤波、百分比换算、电压估算、状态等级判断和模拟输出控制。代码把电位器旋钮的物理变化转化为串口中可观察的数据,也让 AOUT 输出跟随输入变化,形成了从模拟输入到程序处理再到模拟输出的完整闭环。

这类实验非常适合作为传感器采集课程的基础案例。旋钮变化对应连续电压,PCF8591 完成模数转换,Python 程序负责计算和判断,AOUT 再把结果反馈到外部电路。理解这条链路后,后续接入光敏传感器、温度传感器、声音传感器、PWM 调光、LCD 显示、数据上传和自动控制逻辑都会更加自然。硬件编程的重点并不只是读取一个数字,而是让真实世界的变化进入程序,并让程序根据这些变化做出可靠反馈。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐