【CanMV K210】传感器实验 电位器模拟量采集与输出控制
在智能硬件项目中,很多输入信号并不是简单的开关状态,而是连续变化的模拟量。电位器旋钮、光敏电阻、温度传感器、压力传感器这类模块,经常会输出随环境或操作变化而变化的电压信号。对于程序来说,这些变化不能直接变成可计算的数据,需要经过 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 负责完成 ADC 转换,I2C 总线负责传输数据,CanMV K210 程序负责换算和判断,AOUT 再把处理后的数值反馈成模拟输出。理解这条链路后,后续把电位器替换成光敏模块、声音模块或其他模拟传感器时,采集思路基本一致。
模拟量实验中还需要关注抖动问题。电位器旋转时,接触电阻、电源波动和连接线都会让采样值出现轻微跳变。如果每次只读取一次,串口数据可能忽高忽低。代码中使用 SAMPLE_COUNT = 5 做简单平均滤波,通过多次采样再取平均值,让输出结果更稳定。
硬件设施
本实验围绕 CanMV K210、PCF8591 模数转换模块和电位器展开。代码没有使用 LCD、摄像头、按键、蜂鸣器或其他传感器,因此这些模块不作为本节重点。软件侧主要依赖 machine.I2C、fpioa_manager.fm、pcf8591 和 time.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,不要只关注信号线而忽略公共地。

接线关系可以根据代码中的 I2C_SCL_PIN、I2C_SDA_PIN、ADC_CHANNEL 和 pot_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 不适合直接驱动大电流负载,只适合做小信号模拟输出或配合后级电路使用。

| 实验现象 | 正常表现 | 异常提示 |
|---|---|---|
| 程序启动 | 串口提示 PCF8591 初始化完成 | 如果没有输出,检查程序运行状态和串口连接 |
| 旋转电位器 | ADC、百分比、电压和等级随旋钮变化 | 如果数值不变化,检查电位器输出端是否接入 AIN0 |
| AOUT 输出 | 外接 LED 亮度或模拟输出随旋钮变化 | 如果无变化,检查 AOUT 接线和外接电路 |
| 数值轻微波动 | ADC 读数在小范围内跳动 | 属于模拟量常见现象,可通过平均滤波减弱 |
| 电压显示 | 串口显示估算电压值 | 如果模块接 5V,需要把 VREF 改成 5.0 |
| 程序停止 | 捕获停止操作后 AOUT 输出归零 | 如果外设仍有输出,检查停止处理是否执行 |
软件代码
本实验代码围绕 PCF8591 的模拟输入采集和模拟输出控制展开。程序先配置软件 I2C 引脚,再创建 PCF8591 对象;循环阶段读取电位器 ADC 原始值,通过平均滤波降低抖动,再计算百分比、电压估算值和状态等级。读取到的数值同时写入 AOUT,使模拟输出跟随旋钮变化。
| 软件环境 | 作用 | 检查重点 |
|---|---|---|
| CanMV IDE | 编辑、运行和调试 K210 程序 | 能识别开发板串口,并能正常运行基础 print() 测试 |
| CanMV 固件 | 提供 machine.I2C、fpioa_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 显示、数据上传和自动控制逻辑都会更加自然。硬件编程的重点并不只是读取一个数字,而是让真实世界的变化进入程序,并让程序根据这些变化做出可靠反馈。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)