在智能硬件项目中,声音传感器常用于检测环境变化,例如拍手控制灯光、异常响声提醒、设备运行噪声监测、课堂互动触发和智能家居声控反馈。相比单纯在屏幕中打印变量,声音传感器实验更能体现硬件编程的价值:外部环境中的声音变化会被传感器转换成电信号,再通过 ADC 模块变成程序可以读取的数字。

本实验使用 CanMV K210 开发板通过 I2C 连接 PCF8591 模数转换模块,再由 PCF8591 读取声音传感器的模拟输出值。程序运行后,会自动采集当前环境下的基础噪声值,并以这个基础值作为参考,持续判断声音变化是否超过阈值。当检测到明显声音变化时,程序会记录一次声音事件,并在串口终端输出当前声音值、基础值、差值、声音状态和累计触发次数。

学习目标 说明
理解声音模拟量采集 认识声音传感器如何把环境声音变化转换成模拟电压
掌握 PCF8591 读取方式 通过 PCF8591 将声音传感器 AO 模拟输出转换成数字值
理解 I2C 通信链路 使用 CanMV K210 的软件 I2C 读取外部 ADC 模块数据
掌握环境噪声校准 程序启动后采集基础噪声值,避免固定阈值带来的误判
建立声音事件检测思路 通过差值阈值、触发间隔和次数统计完成声音事件识别

声音传感器实验的重点不是把声音“录下来”,而是判断环境中是否出现了明显声音变化。程序读取到的不是音频波形,而是声音模块输出的模拟电压经过 PCF8591 转换后的数字值。这个数值可以用来做拍手检测、响声触发、噪声变化判断和交互式控制。

理论基础

声音传感器通常由麦克风敏感元件和放大比较电路组成。外部声音变化会让传感器输出端的电压发生变化,如果使用 AO 模拟输出,就可以观察到连续变化的模拟信号。CanMV K210 普通 GPIO 更适合读取高低电平,不适合直接读取连续模拟电压,因此本实验通过 PCF8591 模数转换模块完成模拟量采集。

PCF8591 位于声音传感器和 CanMV K210 之间。声音传感器负责把声音变化转换成电压变化,PCF8591 负责把电压变化转换成 0~255 范围内的数字值,CanMV K210 再通过 I2C 总线读取这个数字值。程序拿到数字后,不是直接判断“声音大不大”,而是先计算当前值与环境基础值之间的差值,再判断是否超过触发阈值。

这个设计比固定阈值更适合教学和实际调试。不同教室、不同桌面、不同供电方式、不同声音传感器模块,安静状态下的基础值可能并不相同。如果直接写死某个 ADC 数值作为触发条件,可能在安静环境中误触发,也可能在嘈杂环境中无法触发。本实验启动后先采集 20 次环境基础噪声,再用差值判断声音事件,这样更符合真实硬件项目的调试思路。

环境声音
拍手 / 敲击 / 说话 / 噪声

声音传感器模块
声音变化转模拟电压

AO 模拟输出
接入 PCF8591 AIN0

PCF8591 ADC
模拟量转数字值

I2C 总线
SCL / SDA

CanMV K210
读取 ADC 数值

环境噪声校准
计算 base_value

差值判断
abs 当前值 - 基础值

声音事件检测
状态分类 / 次数统计

模块供电
VCC

公共地线
GND

这条链路可以从“声音输入到程序事件”的角度理解。声音传感器只负责产生模拟电压变化,PCF8591 负责完成模数转换,CanMV K210 负责读取数字值并执行判断逻辑。最终进入业务程序的不是原始电压,而是“安静”“正常波动”“声音触发”“强声音触发”这类更容易使用的状态。

硬件设施

本实验围绕声音传感器模拟量采集展开,核心硬件由 CanMV K210 开发板、PCF8591 ADC 模块和声音传感器组成。CanMV 负责运行 Python 程序,PCF8591 负责把声音传感器输出的模拟电压转换成数字值,程序通过 I2C 总线读取这些数字值,再根据数值变化判断是否发生声音事件。

接线关系可以先通过下面这张图建立整体印象。CanMV K210 使用物理引脚 6 和 7 与 PCF8591 建立 I2C 通信,声音传感器的 AO 模拟输出接入 PCF8591 的 AIN0 通道。

在这里插入图片描述

硬件 / 软件 作用 说明
CanMV K210 开发板 实验运行平台 负责执行 MicroPython 程序,并通过软件 I2C 读取 PCF8591 数据
声音传感器模块 声音采集对象 将环境声音变化转换成模拟电压信号
PCF8591 ADC 模块 模数转换模块 将声音传感器模拟信号转换成 0~255 范围内的数字值
物理引脚 6 I2C SCL 用于软件 I2C 时钟线,连接 PCF8591 的 SCL
物理引脚 7 I2C SDA 用于软件 I2C 数据线,连接 PCF8591 的 SDA
AIN0 模拟输入通道 接入声音传感器 AO 模拟输出
machine.I2C I2C 通信模块 用于创建软件 I2C 对象,连接 PCF8591
fpioa_manager.fm 引脚功能映射模块 为软件 I2C 提供 GPIOHS 功能绑定
pcf8591 ADC 驱动模块 封装 PCF8591 读取逻辑,通过 read() 获取 ADC 通道值
time 时间控制模块 用于采样延时、触发间隔判断和循环节奏控制

实验中使用到的核心模块如下。声音传感器负责采集环境声音,PCF8591 负责模拟量转换,CanMV K210 负责读取数据并完成状态判断。接线时重点检查声音传感器 AO 是否接到 PCF8591 AIN0,不要误接到 DO 数字输出端。

声音传感器、PCF8591 与连接线实物

接线关系可以从代码中的 I2C_SCL_PINI2C_SDA_PINADC_CHANNELsound_adc.read(ADC_CHANNEL) 推导。当前声音传感器的模拟输出接到 PCF8591 的 AIN0 通道,CanMV 通过物理引脚 6 和 7 与 PCF8591 建立 I2C 通信。

物理引脚 / 接口 功能 代码变量 对应硬件 说明
物理引脚 6 I2C SCL I2C_SCL_PIN PCF8591 SCL 作为软件 I2C 时钟线
物理引脚 7 I2C SDA I2C_SDA_PIN PCF8591 SDA 作为软件 I2C 数据线
AIN0 ADC 输入通道 0 ADC_CHANNEL = 0 声音传感器 AO 声音传感器模拟输出接入 PCF8591 AIN0
GPIOHS1 软件 I2C SCL 功能 gscl=fm.fpioa.GPIOHS1 内部功能映射 用于软件 I2C 时钟功能绑定
GPIOHS2 软件 I2C SDA 功能 gsda=fm.fpioa.GPIOHS2 内部功能映射 用于软件 I2C 数据功能绑定
VCC 模块供电 声音传感器与 PCF8591 根据模块标识连接对应供电端
GND 公共地线 CanMV、PCF8591 与声音传感器 保证通信和模拟量采集具有统一参考电平

完成接线后的整体效果如下。程序运行后,串口会持续输出当前声音值、基础值、差值和声音状态。拍手、敲击桌面或靠近传感器说话时,Diff 数值应明显变化;如果变化超过阈值,程序会记录一次声音事件。

CanMV K210 声音传感器实验成品状态

实验现象 正常表现 异常提示
程序启动 串口提示声音传感器初始化完成,并开始采集基础噪声 没有输出时检查程序运行和串口连接
环境校准 安静状态下采集多次声音值,计算 base_value 校准时有噪声会导致基础值偏离
安静环境 Diff 较小,状态显示“安静”或“正常波动” 安静时频繁触发,说明阈值偏小或环境噪声较大
拍手或敲击 当前值与基础值差距变大,触发声音事件 无明显变化时检查 AO 是否接入 AIN0
连续声音 程序不会无限重复统计同一次声音 触发过密时增大 TRIGGER_DELAY_MS
声音很强 状态可能显示“强声音触发” 若一直强触发,检查传感器灵敏度和环境噪声

软件代码

本实验代码围绕声音传感器模拟值采集和声音事件判断展开。程序先初始化软件 I2C 和 PCF8591 模块,再读取声音传感器的 ADC 数值。为了避免不同环境下固定阈值误判,程序启动后会自动采集 20 次环境基础噪声,并计算平均值作为基准。主循环中持续读取当前声音值,结合差值阈值和触发间隔判断声音事件,同时输出状态信息,方便课堂演示和硬件调试。

软件环境 作用 检查重点
CanMV IDE 编辑、运行和调试 K210 程序 能识别开发板串口,并能运行基础 print() 测试
CanMV 固件 提供 machine.I2Cfpioa_manager 等模块 固件环境需要支持当前 I2C 与 FPIOA 写法
USB 串口驱动 让电脑识别开发板串口 串口工具中能看到对应端口
pcf8591.py PCF8591 驱动文件 需要能正常 import pcf8591
串口终端 查看声音值、基础值、差值和触发次数 拍手或敲击时能看到状态变化
声音传感器 AO 模拟输入来源 应接入 PCF8591 的 AIN0 通道
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
CanMV K210 声音传感器实验 Demo

功能说明:
1. 通过 PCF8591 读取声音传感器模拟值
2. 自动采集环境基础噪声值
3. 根据声音变化判断是否触发声音事件
4. 统计声音触发次数
5. 输出当前声音状态,方便课程演示和调试
"""

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


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

I2C_SCL_PIN = 6
I2C_SDA_PIN = 7

ADC_CHANNEL = 0

SAMPLE_INTERVAL_MS = 200       # 采样间隔
CALIBRATE_COUNT = 20           # 环境噪声采样次数
TRIGGER_DELTA = 25             # 声音变化阈值
TRIGGER_DELAY_MS = 600         # 两次有效触发之间的最小间隔


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

sound_adc = None
base_value = 0
sound_count = 0
last_trigger_time = 0


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

def setup():
    """
    初始化 I2C 和 PCF8591 ADC 模块
    声音传感器的模拟输出接到 PCF8591 的 AIN0
    """
    global sound_adc

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

    sound_adc = pcf8591.PCF8591(i2c)
    print("声音传感器初始化完成")


# =========================
# 读取声音值
# =========================

def read_sound_value():
    """
    读取声音传感器模拟值
    PCF8591 的读取范围通常为 0~255
    """
    return sound_adc.read(ADC_CHANNEL)


# =========================
# 环境噪声校准
# =========================

def calibrate_noise():
    """
    采集当前环境下的基础声音值
    避免固定阈值在不同环境下误判
    """
    global base_value

    total = 0

    print("正在采集环境基础噪声,请保持环境相对安静...")

    for _ in range(CALIBRATE_COUNT):
        value = read_sound_value()
        total += value
        time.sleep_ms(100)

    base_value = total // CALIBRATE_COUNT
    print("环境基础声音值:", base_value)


# =========================
# 声音触发判断
# =========================

def is_sound_triggered(current_value):
    """
    判断是否出现明显声音变化

    部分声音传感器在声音变大时 ADC 数值会下降,
    部分模块则会升高,所以这里使用变化差值判断。
    """
    diff = abs(current_value - base_value)

    if diff >= TRIGGER_DELTA:
        return True

    return False


# =========================
# 状态分析
# =========================

def get_sound_level(current_value):
    """
    根据当前值与基础值的差值,返回声音状态
    """
    diff = abs(current_value - base_value)

    if diff < 10:
        return "安静"
    elif diff < TRIGGER_DELTA:
        return "正常波动"
    elif diff < 60:
        return "声音触发"
    else:
        return "强声音触发"


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

def loop():
    """
    循环读取声音传感器数据
    当检测到明显声音变化时,记录一次声音事件
    """
    global sound_count
    global last_trigger_time

    while True:
        current_value = read_sound_value()
        current_time = time.ticks_ms()

        sound_level = get_sound_level(current_value)
        diff = abs(current_value - base_value)

        print(
            "Sound Value:",
            current_value,
            "| Base:",
            base_value,
            "| Diff:",
            diff,
            "| State:",
            sound_level
        )

        if is_sound_triggered(current_value):
            if time.ticks_diff(current_time, last_trigger_time) > TRIGGER_DELAY_MS:
                sound_count += 1
                last_trigger_time = current_time

                print("检测到声音事件,累计次数:", sound_count)

        time.sleep_ms(SAMPLE_INTERVAL_MS)


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

if __name__ == '__main__':
    setup()
    calibrate_noise()
    loop()

这段程序可以分成采集、校准、判断和统计四个层级。采集层由 setup()read_sound_value() 完成,负责通过 I2C 和 PCF8591 读取 AIN0 模拟量;校准层由 calibrate_noise() 完成,负责采集当前环境基础值;判断层由 is_sound_triggered()get_sound_level() 完成,负责判断声音变化是否明显;统计层由 loop() 完成,负责持续输出状态并记录触发次数。

参数配置区是实验调试的重点。CALIBRATE_COUNT 决定基础噪声采样次数,TRIGGER_DELTA 决定声音触发灵敏度,TRIGGER_DELAY_MS 决定两次声音事件之间的最小间隔,SAMPLE_INTERVAL_MS 决定主循环采样频率。安静环境中频繁触发时,优先增大 TRIGGER_DELTA;拍手没有触发时,可以适当减小 TRIGGER_DELTA 或调整声音传感器模块灵敏度。

函数名 功能 对应现象
setup() 初始化软件 I2C 和 PCF8591 串口输出初始化完成,程序具备读取 ADC 数据的能力
read_sound_value() 读取声音传感器模拟值 获取 PCF8591 AIN0 通道的当前数字值
calibrate_noise() 采集环境基础噪声 程序启动后统计当前安静环境下的基础声音值
is_sound_triggered(current_value) 判断声音是否触发 当前值与基础值差距超过阈值时返回触发状态
get_sound_level(current_value) 分析声音状态 根据差值返回安静、正常波动、声音触发或强声音触发
loop() 持续采样和事件统计 串口持续输出声音值、基础值、差值、状态和累计次数

主循环 loop() 每隔 SAMPLE_INTERVAL_MS 毫秒读取一次声音值,并通过 get_sound_level() 得到当前声音状态。程序还使用 time.ticks_ms()time.ticks_diff() 控制两次有效触发之间的最小间隔,避免一次拍手或一次响声在短时间内被重复统计。TRIGGER_DELAY_MS = 600 表示两次有效声音事件至少间隔 600 毫秒,这种设计在硬件项目中通常可以理解为事件防抖。

扩展应用

声音传感器实验容易受到接线、环境噪声、模块灵敏度和阈值设置影响。排查时应围绕 I2C 通信、PCF8591 通道、基础噪声值、差值阈值和采样间隔进行判断。

问题现象 可能原因 处理思路
程序初始化后无法读取声音值 I2C 接线错误、PCF8591 未正确连接、SCL/SDA 引脚不匹配 核对物理引脚 6 和 7 是否分别连接 PCF8591 的 SCL 与 SDA,并确认 PCF8591 供电正常
程序提示无法导入 pcf8591 驱动文件没有上传,或不在当前运行目录 检查 pcf8591.py 是否已经保存到开发板对应目录
声音值一直不变化 声音传感器 AO 未接入 AIN0、模块供电异常、传感器灵敏度过低 检查声音传感器模拟输出是否接到 PCF8591 AIN0,适当调整模块电位器
安静环境中频繁触发 环境噪声较大、TRIGGER_DELTA 设置过小、校准时环境不安静 增大 TRIGGER_DELTA,并在校准阶段保持环境相对安静
拍手或说话没有触发 阈值过高、声音距离传感器太远、模块灵敏度较低 降低 TRIGGER_DELTA,靠近传感器测试,并调整声音模块灵敏度
一次声音被统计多次 触发间隔太短、声音持续时间较长 增大 TRIGGER_DELAY_MS,例如从 600 调整到 1000
状态一直显示正常波动 当前差值没有达到触发阈值 观察串口中的 Diff 数值,再根据实际波动范围调整 TRIGGER_DELTA
基础值明显不稳定 校准期间有噪声干扰或传感器输出抖动较大 增加 CALIBRATE_COUNT,让基础值采样更充分
声音值变化方向与预期相反 不同模块在声音变大时可能表现为数值升高或下降 当前代码使用绝对差值判断,通常不需要修改方向逻辑

声音传感器实验的价值不只是读取一个 ADC 数字,而是把环境声音变化变成程序可以处理的事件。当前代码已经具备数据采样、环境校准、阈值判断、状态分类和次数统计能力,因此很适合扩展到声控交互、异常提醒、课堂演示和设备状态监测等场景。对于硬件课程而言,这类实验能把“传感器输入—程序判断—状态输出”这条链路讲得非常直观。

应用场景 实现思路 可扩展能力
拍手触发控制 通过声音变化判断一次拍手事件 后续可扩展 LED 或继电器,实现拍手开关效果
教学互动统计 统计课堂中拍手、敲击或响声触发次数 可结合串口输出或屏幕显示,形成互动实验演示
环境噪声监测 持续读取声音值并计算与基础噪声的差值 可增加数据记录功能,形成简易噪声变化曲线
异常声音提醒 当声音差值超过较大阈值时判断为强声音触发 后续可扩展蜂鸣器、LED 或网络消息提醒
设备运行监听 观察机器运行时声音变化是否明显偏离基础值 可结合时间统计,判断设备是否出现异常噪声
智能家居入口实验 将声音事件作为控制逻辑的输入条件 后续可接入灯光、舵机、显示屏或无线通信模块
传感器数据课程案例 用声音传感器讲解 ADC、阈值、校准和防抖 适合延伸到光敏、电位器、温度等模拟量采集实验

从工程角度看,当前程序已经具备比较完整的输入型硬件实验结构。setup() 负责硬件初始化,read_sound_value() 负责数据读取,calibrate_noise() 负责环境适配,is_sound_triggered() 负责事件判断,loop() 负责持续调度。后续增加 LED 提示、蜂鸣器报警、LCD 显示或网络上传时,不需要推翻原有代码,只需要在声音事件触发的位置追加对应的输出动作。

总结

本实验通过 CanMV K210、PCF8591 和声音传感器完成了环境声音采集与声音事件检测,核心能力包括软件 I2C 通信、ADC 模拟量读取、环境基础值校准、差值阈值判断、事件防抖、状态分类和触发次数统计。代码没有把声音判断写成固定数值比较,而是先采集环境基础噪声,再根据当前值与基础值的差异判断声音变化,这种写法更符合真实硬件环境中的调试思路。

这类实验适合作为传感器课程的入门案例。程序中的数字变化来自真实环境,串口输出的状态可以直接反映声音强弱变化,基础噪声校准和触发间隔控制也能让初学者理解硬件输入并不总是稳定可靠。后续课程可以在这个基础上继续扩展 LED 状态提示、蜂鸣器报警、LCD 数据显示、传感器数据记录、网络上传和 AI 视觉联动等方向,把单一传感器读取逐步升级成完整的智能硬件交互系统。

Logo

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

更多推荐