欢迎光临
我们一直在努力

STM32驱动声音检测模块+OLED显示项目完整讲解(外部中断捕获拍手声音)

前言

本篇博文基于 STM32F103C8T6 最小系统板,搭配数字声音检测模块、I2C‑OLED 屏幕,实现声音触发检测、拍手识别、触发次数统计,并且把状态和计数实时显示在 OLED 上。 项目使用PA0 引脚外部中断 EXTI0捕获短促的声音脉冲,同时搭配 GPIO 轮询读取电平,解决单纯轮询容易丢失短脉冲的问题,适合 STM32 外部中断入门教学实验。

视频讲解:STM32驱动声音检测模块+OLED显示项目完整讲解(外部中断捕获拍手声音)http://STM32驱动声音检测模块+OLED显示项目完整讲解(外部中断捕获拍手声音)

注意:本项目使用的是数字声音检测模块 DO/OUT 输出,不能测量分贝、声音频率,仅判断声音是否超过设定阈值。

一、项目实现功能与工作流程

演示目标

拍手、敲击等声音被麦克风采集,声音检测模块将声信号转为数字电平信号;STM32 捕获电平跳变,记录声音触发事件与累计触发次数,最终在 OLED 屏幕展示声音检测状态和计数。

整体工作流程:

  • 拍手 / 敲击产生声音
  • 麦克风将声音振动转化为微弱电信号
  • 模块内部放大电路放大信号,和电位器设置的灵敏度阈值做对比
  • 声音超过阈值,OUT 引脚输出低电平(本模块低电平有效)
  • STM32 的 PA0 引脚检测到下降沿,外部中断 EXTI0 被触发
  • 程序记录声音事件、累计触发次数
  • OLED 刷新显示当前状态与触发计数
  • 通俗概括:声音检测模块把声音转换成数字电平,STM32 捕获电平变化,将声音状态和触发次数输出到 OLED 屏幕。

    二、硬件接线说明

    ⚠️重点提醒:所有器件 GND 必须共地,否则会出现乱触发、检测失效问题!

    模块引脚STM32F103C8T6 引脚功能说明
    声音模块 OUT PA0 数字输出,接 GPIO 输入 + EXTI0 外部中断
    声音模块 VCC 5V 声音检测模块供电
    声音模块 GND GND 电源共地
    OLED SCL PB8 软件 I2C 时钟线
    OLED SDA PB9 软件 I2C 数据线
    OLED VCC 3.3V OLED 屏幕供电
    OLED GND GND OLED 电源地

    硬件注意事项

  • OUT 电平安全 不同声音检测模块输出电路有差异。模块接 5V 供电前,需要确认 OUT 引脚高电平不能超过 STM32 引脚耐压。如果 OUT 输出接近 5V,需要增加分压或者电平转换保护。 本工程 PA0 配置为上拉输入,模块断开或者未触发时,引脚维持高电平,避免悬空带来误触发。
  • AO 和 OUT (DO) 引脚区分 很多声音检测模块同时存在 AO、DO 两个输出:
    • AO:模拟输出,输出随声音变化的模拟电压,需要 ADC 采集,可以获取声音强弱;
    • DO/OUT:数字比较器输出,只有高低电平,仅代表声音是否超过阈值;
    • 电位器:调节比较器参考阈值,也就是声音触发灵敏度。

    本项目只用数字 OUT 引脚,只能判断 “有没有触发”,无法获取真实响度、分贝。

    三、声音检测模块硬件原理

    数字声音检测模块由麦克风、信号放大电路、电压比较器、灵敏度调节电位器四部分组成。

    声音本质是空气压力波动,声波传到麦克风,麦克风振膜发生振动,生成微弱交流电信号;微弱信号送入板载放大电路放大,之后送入电压比较器。 电位器输出一个参考电压阈值,放大后的声音信号和参考阈值做比较。

    本项目使用模块为低电平有效逻辑:

    环境状态OUT 输出电平程序内部状态
    声音未超过阈值 高电平 1 安静状态
    声音超过阈值 低电平 0 检测到声音

    声音触发瞬间,OUT 会从高电平跳转到低电平,产生下降沿,STM32 依靠这个下降沿触发外部中断。

    不同厂商模块有效电平可能相反!如果你的模块发声输出高电平,需要修改代码的判断逻辑和中断触发边沿。

    四、STM32 程序检测原理:GPIO 轮询 + EXTI 外部中断

    项目同时使用两种检测方式,取长补短。

    1. GPIO 轮询读取电平

    直接调用库函数读取 PA0 引脚当前电平,判断高低,识别声音状态。 但是轮询存在短板:主循环每 20ms 才读取一次引脚,如果声音产生的低电平脉冲持续时间小于 20ms,轮询就会直接漏掉这个声音事件。

    2. EXTI0 外部中断捕获短脉冲

    PA0 映射到 EXTI0 外部中断,配置为下降沿触发。 当 OUT 引脚高变低产生下降沿,硬件自动置位中断标志,CPU 暂停主循环,跳转到中断服务函数。

    中断服务函数里面只做两件事:标记声音待处理事件、触发计数自增,清除中断挂起标志。 ❗重要原则:中断服务函数不做 OLED 屏幕刷新,OLED 刷新属于耗时操作,放在中断里面会拉长中断执行时间,影响系统实时性。

    3. 为什么轮询和中断一起用

    • 外部中断:专门捕捉非常短促的下降沿脉冲,防止短声音事件被漏掉;
    • GPIO 轮询:读取引脚实时电平,可以识别持续时间比较长的声音触发。

    两者结合,既可以捕捉短促拍手,也可以识别长时间持续触发状态。

    4. 共享变量与临界区问题

    中断服务函数会修改事件标志、触发计数,主循环也会读取这两个全局变量。 如果主循环读取一半,突然进入中断改写变量,会拿到错乱的数据。 解决方案:访问这两个共享变量的时候,短暂关闭全局中断,读取 / 修改完成再恢复中断。 同时变量加上volatile关键字,告诉编译器:变量会被中断异步修改,不要做编译器优化,每次都从内存读取真实值。

    5. OLED 显示保持逻辑

    声音触发脉冲持续时间很短,如果检测到就立刻显示 YES,会一闪而过人眼看不清。 程序设置显示保持周期:每一轮主循环 20ms,保持 25 个周期,20ms × 25 = 500ms。 触发之后,就算声音已经消失,屏幕仍然维持 500ms 的 SOUND:YES 显示。

    这个 500ms 仅仅是显示效果,并不是代表声音持续了 500ms。

    OLED 屏幕输出示例:

    SOUND CHECK
    OUT: PA0
    SOUND: YES
    COUNT:0000000001

    显示字段释义:

    显示内容含义
    OUT: PA0 声音模块输出接 PA0 引脚
    SOUND: YES 检测到声音,或者 500ms 显示保持还没结束
    SOUND: NO 无声音触发,保持时间结束
    COUNT 外部中断捕获的下降沿总次数

    五、源代码

    main.c、SoundCheck.c、SoundCheck.h相关驱动代码。

    main.c代码

    /* Sound detector and OLED teaching demonstration for STM32F103C8T6. */
    #include "delay.h"
    #include "oled.h"
    #include "SoundCheck.h"

    #define SAMPLE_PERIOD_MS 20U
    #define SOUND_HOLD_CYCLES 25U

    static void SoundCheck_ShowState(u8 detected, u32 trigger_count);

    int main(void)
    {
    u8 detected;
    u8 last_detected;
    u8 hold_cycles;
    u32 trigger_count;
    u32 last_trigger_count;

    delay_init();
    SoundCheck_Init();
    OLED_Init();
    OLED_ColorTurn(0);
    OLED_DisplayTurn(0);

    last_detected = 0xFFU;
    last_trigger_count = 0xFFFFFFFFUL;
    hold_cycles = 0U;

    while(1)
    {
    /* EXTI0 keeps short sound pulses from being missed between samples. */
    if(SoundCheck_FetchEvent() != 0U)
    {
    hold_cycles = SOUND_HOLD_CYCLES;
    }

    if(SoundCheck_IsSoundDetected() != 0U)
    {
    hold_cycles = SOUND_HOLD_CYCLES;
    }

    if(hold_cycles > 0U)
    {
    detected = 1U;
    hold_cycles–;
    }
    else
    {
    detected = 0U;
    }

    trigger_count = SoundCheck_GetTriggerCount();
    if((detected != last_detected) ||
    (trigger_count != last_trigger_count))
    {
    SoundCheck_ShowState(detected, trigger_count);
    last_detected = detected;
    last_trigger_count = trigger_count;
    }

    delay_ms(SAMPLE_PERIOD_MS);
    }
    }

    static void SoundCheck_ShowState(u8 detected, u32 trigger_count)
    {
    OLED_Clear();
    OLED_ShowString(0, 0, (u8 *)"SOUND CHECK", 16);
    OLED_ShowString(0, 16, (u8 *)"OUT: PA0", 16);

    if(detected != 0U)
    {
    OLED_ShowString(0, 32, (u8 *)"SOUND: YES", 16);
    }
    else
    {
    OLED_ShowString(0, 32, (u8 *)"SOUND: NO", 16);
    }

    OLED_ShowString(0, 50, (u8 *)"COUNT:", 12);
    OLED_ShowNum(42, 50, trigger_count, 10, 12);
    OLED_Refresh();
    }

    SoundCheck.c代码

    #include "SoundCheck.h"

    #include "stm32f10x.h"
    #include "stm32f10x_exti.h"
    #include "stm32f10x_gpio.h"
    #include "stm32f10x_rcc.h"
    #include "misc.h"

    /* 声音检测模块固定连接到 PA0,并使用 EXTI0 产生下降沿中断。 */
    #define SOUND_CHECK_GPIO_PORT GPIOA
    #define SOUND_CHECK_GPIO_PIN GPIO_Pin_0
    #define SOUND_CHECK_GPIO_CLOCK RCC_APB2Periph_GPIOA
    #define SOUND_CHECK_EXTI_LINE EXTI_Line0

    /* 对外公开的当前电平、声音状态及累计触发次数。 */
    uint8_t SoundCheck_Level = SOUND_CHECK_LEVEL_HIGH;
    uint8_t SoundCheck_Value = SOUND_CHECK_STATE_QUIET;
    volatile uint32_t SoundCheck_TriggerCount = 0U;

    /* 中断置位的事件标志,以及模块初始化状态。 */
    static volatile uint8_t SoundCheck_EventPending = 0U;
    static uint8_t SoundCheck_Initialized = 0U;

    /*
    * 进入临界区并返回进入前的全局中断状态。
    * 保存 PRIMASK 可避免在原本已经关中断的环境中错误地重新开中断。
    */
    static uint32_t SoundCheck_EnterCritical(void)
    {
    uint32_t primask;

    primask = __get_PRIMASK();
    __disable_irq();
    return primask;
    }

    /* 按进入临界区前的状态恢复全局中断。 */
    static void SoundCheck_ExitCritical(uint32_t primask)
    {
    if(primask == 0U)
    {
    __enable_irq();
    }
    }

    /* 读取 PA0,并按照“低电平有效”规则同步公开状态变量。 */
    static void SoundCheck_UpdateState(void)
    {
    if(GPIO_ReadInputDataBit(SOUND_CHECK_GPIO_PORT,
    SOUND_CHECK_GPIO_PIN) == Bit_SET)
    {
    SoundCheck_Level = SOUND_CHECK_LEVEL_HIGH;
    SoundCheck_Value = SOUND_CHECK_STATE_QUIET;
    }
    else
    {
    SoundCheck_Level = SOUND_CHECK_LEVEL_LOW;
    SoundCheck_Value = SOUND_CHECK_STATE_DETECTED;
    }
    }

    /* 初始化声音检测所需的输入引脚、外部中断和中断控制器。 */
    void SoundCheck_Init(void)
    {
    GPIO_InitTypeDef gpio_init;
    EXTI_InitTypeDef exti_init;
    NVIC_InitTypeDef nvic_init;
    uint32_t primask;

    SoundCheck_Initialized = 0U;

    primask = SoundCheck_EnterCritical();
    SoundCheck_EventPending = 0U;
    SoundCheck_TriggerCount = 0U;
    SoundCheck_ExitCritical(primask);

    RCC_APB2PeriphClockCmd(SOUND_CHECK_GPIO_CLOCK | RCC_APB2Periph_AFIO,
    ENABLE);

    /* 使用内部上拉,使模块未触发或断开连接时 PA0 保持为高电平。 */
    gpio_init.GPIO_Pin = SOUND_CHECK_GPIO_PIN;
    gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
    gpio_init.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(SOUND_CHECK_GPIO_PORT, &gpio_init);

    /* 将 PA0 映射到 EXTI0,仅在有效的下降沿到来时触发中断。 */
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);

    exti_init.EXTI_Line = SOUND_CHECK_EXTI_LINE;
    exti_init.EXTI_Mode = EXTI_Mode_Interrupt;
    exti_init.EXTI_Trigger = EXTI_Trigger_Falling;
    exti_init.EXTI_LineCmd = ENABLE;
    EXTI_Init(&exti_init);
    EXTI_ClearITPendingBit(SOUND_CHECK_EXTI_LINE);

    /* 配置 EXTI0 的抢占优先级和响应优先级,并使能其中断通道。 */
    nvic_init.NVIC_IRQChannel = EXTI0_IRQn;
    nvic_init.NVIC_IRQChannelPreemptionPriority = 2U;
    nvic_init.NVIC_IRQChannelSubPriority = 0U;
    nvic_init.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&nvic_init);

    /* 初始化完成前先同步一次引脚状态,避免首次读取返回默认值。 */
    SoundCheck_UpdateState();
    SoundCheck_Initialized = 1U;
    }

    /* 兼容旧工程命名的初始化接口。 */
    void SoundCheck_IO_Init(void)
    {
    SoundCheck_Init();
    }

    /* 返回初始化状态;非零表示已经初始化。 */
    uint8_t SoundCheck_IsInitialized(void)
    {
    return SoundCheck_Initialized;
    }

    /* 读取 OUT 引脚原始电平,并在需要时自动初始化模块。 */
    uint8_t SoundCheck_ReadOutLevel(void)
    {
    if(SoundCheck_Initialized == 0U)
    {
    SoundCheck_Init();
    }

    SoundCheck_UpdateState();
    return SoundCheck_Level;
    }

    /* 读取经过有效电平转换后的声音状态。 */
    uint8_t SoundCheck_ReadState(void)
    {
    (void)SoundCheck_ReadOutLevel();
    return SoundCheck_Value;
    }

    /* 将声音状态转换为便于条件判断的布尔返回值。 */
    uint8_t SoundCheck_IsSoundDetected(void)
    {
    return (SoundCheck_ReadState() == SOUND_CHECK_STATE_DETECTED) ? 1U : 0U;
    }

    /* 兼容旧工程命名的声音检测接口。 */
    uint8_t SoundCheck_Read(void)
    {
    return SoundCheck_IsSoundDetected();
    }

    /* 记录一次有效边沿;计数达到 uint32_t 上限后保持饱和。 */
    void SoundCheck_NotifyActiveEdge(void)
    {
    SoundCheck_EventPending = 1U;

    if(SoundCheck_TriggerCount < 0xFFFFFFFFUL)
    {
    SoundCheck_TriggerCount++;
    }
    }

    /* 原子地取出事件标志,并将其清零,防止主程序与中断并发访问。 */
    uint8_t SoundCheck_FetchEvent(void)
    {
    uint32_t primask;
    uint8_t event_pending;

    primask = SoundCheck_EnterCritical();
    event_pending = SoundCheck_EventPending;
    SoundCheck_EventPending = 0U;
    SoundCheck_ExitCritical(primask);

    return event_pending;
    }

    /* 查询事件标志但不消费事件。 */
    uint8_t SoundCheck_PeekEvent(void)
    {
    return SoundCheck_EventPending;
    }

    /* 通过取出事件的方式清除待处理标志。 */
    void SoundCheck_ClearEvent(void)
    {
    (void)SoundCheck_FetchEvent();
    }

    /* 在临界区内读取 32 位触发计数,保证读取结果一致。 */
    uint32_t SoundCheck_GetTriggerCount(void)
    {
    uint32_t primask;
    uint32_t trigger_count;

    primask = SoundCheck_EnterCritical();
    trigger_count = SoundCheck_TriggerCount;
    SoundCheck_ExitCritical(primask);

    return trigger_count;
    }

    /* 原子清零触发计数和待处理事件。 */
    void SoundCheck_ResetTriggerCount(void)
    {
    uint32_t primask;

    primask = SoundCheck_EnterCritical();
    SoundCheck_TriggerCount = 0U;
    SoundCheck_EventPending = 0U;
    SoundCheck_ExitCritical(primask);
    }

    /* 获取模块完整状态快照;空指针会返回参数错误。 */
    uint8_t SoundCheck_ReadData(SoundCheck_Data *data)
    {
    uint32_t primask;

    if(data == 0)
    {
    return SOUND_CHECK_ERROR_ARGUMENT;
    }

    (void)SoundCheck_ReadOutLevel();
    data->level = SoundCheck_Level;
    data->state = SoundCheck_Value;

    primask = SoundCheck_EnterCritical();
    data->event_pending = SoundCheck_EventPending;
    data->trigger_count = SoundCheck_TriggerCount;
    SoundCheck_ExitCritical(primask);

    return SOUND_CHECK_OK;
    }

    /* 处理 EXTI0 中断:登记有效边沿,并清除硬件中断挂起位。 */
    void SoundCheck_EXTI_IRQHandler(void)
    {
    if(EXTI_GetITStatus(SOUND_CHECK_EXTI_LINE) != RESET)
    {
    SoundCheck_NotifyActiveEdge();
    EXTI_ClearITPendingBit(SOUND_CHECK_EXTI_LINE);
    }
    }

    /* EXTI0 中断入口。 */
    void EXTI0_IRQHandler(void)
    {
    SoundCheck_EXTI_IRQHandler();
    }

    SoundCheck.h代码

    #ifndef __SOUND_CHECK_H
    #define __SOUND_CHECK_H

    #include <stdint.h>

    /*
    * STM32F103C8T6 声音检测模块接线:
    * VCC -> 5V
    * OUT -> PA0(外部中断 EXTI0)
    * GND -> GND
    *
    * 本项目使用的数字声音检测模块为低电平有效:当声音强度超过模块上
    * 电位器设定的阈值时,OUT 引脚输出低电平。
    */
    /* OUT 引脚的原始电平。 */
    #define SOUND_CHECK_LEVEL_LOW 0U
    #define SOUND_CHECK_LEVEL_HIGH 1U

    /* 将原始电平转换后得到的声音检测状态。 */
    #define SOUND_CHECK_STATE_QUIET 0U
    #define SOUND_CHECK_STATE_DETECTED 1U

    /* 接口返回值。 */
    #define SOUND_CHECK_OK 0U
    #define SOUND_CHECK_ERROR_ARGUMENT 1U

    /* 声音检测模块的完整状态快照。 */
    typedef struct
    {
    uint8_t level; /* OUT 引脚当前电平。 */
    uint8_t state; /* 当前声音状态:安静或检测到声音。 */
    uint8_t event_pending; /* 是否存在尚未处理的触发事件。 */
    uint32_t trigger_count; /* 自初始化或复位计数以来的触发次数。 */
    } SoundCheck_Data;

    /* 为简单教学及调试示例保留的兼容变量。 */
    extern uint8_t SoundCheck_Level;
    extern uint8_t SoundCheck_Value;
    extern volatile uint32_t SoundCheck_TriggerCount;

    /* 初始化 GPIO、EXTI0 和 NVIC,并清除事件及触发计数。 */
    void SoundCheck_Init(void);
    /* 兼容旧代码的初始化接口,内部直接调用 SoundCheck_Init。 */
    void SoundCheck_IO_Init(void);
    /* 查询模块是否已经初始化。 */
    uint8_t SoundCheck_IsInitialized(void);

    /* 读取 OUT 引脚原始电平;未初始化时会自动完成初始化。 */
    uint8_t SoundCheck_ReadOutLevel(void);
    /* 读取转换后的声音状态。 */
    uint8_t SoundCheck_ReadState(void);
    /* 检测当前是否有声音,检测到声音时返回 1,否则返回 0。 */
    uint8_t SoundCheck_IsSoundDetected(void);
    /* 兼容旧代码的声音检测接口。 */
    uint8_t SoundCheck_Read(void);

    /* 读取并清除待处理事件;有事件时返回 1。 */
    uint8_t SoundCheck_FetchEvent(void);
    /* 只查询待处理事件,不清除事件标志。 */
    uint8_t SoundCheck_PeekEvent(void);
    /* 清除待处理事件。 */
    void SoundCheck_ClearEvent(void);

    /* 原子读取累计触发次数。 */
    uint32_t SoundCheck_GetTriggerCount(void);
    /* 原子清零累计触发次数,并同时清除待处理事件。 */
    void SoundCheck_ResetTriggerCount(void);
    /* 记录一次有效触发边沿,通常由中断服务程序调用。 */
    void SoundCheck_NotifyActiveEdge(void);
    /* 将当前电平、状态、事件标志和触发次数复制到 data。 */
    uint8_t SoundCheck_ReadData(SoundCheck_Data *data);

    /* 由 EXTI0_IRQHandler 调用;与其他中断服务程序整合时也可直接调用。 */
    void SoundCheck_EXTI_IRQHandler(void);

    #endif

    核心 API 函数说明

    文件函数名功能描述
    SoundCheck.c SoundCheck_Init() 初始化 PA0 上拉输入、EXTI0 外部中断、NVIC 中断优先级
    SoundCheck.c SoundCheck_ReadOutLevel() 读取 PA0 原始 GPIO 高低电平
    SoundCheck.c SoundCheck_IsSoundDetected() 根据电平判断是否检测到声音
    SoundCheck.c SoundCheck_FetchEvent() 读取并且清除待处理声音事件标志
    SoundCheck.c SoundCheck_GetTriggerCount() 原子操作读取下降沿累计计数
    SoundCheck.c SoundCheck_EXTI_IRQHandler() EXTI0 中断服务函数,记录事件,清除中断标志
    main.c SoundCheck_ShowState() OLED 屏幕打印声音状态与计数值

    六、实物演示章节

    6.1 实物器材清单

    • STM32F103C8T6 最小系统板 ×1
    • 数字声音检测模块(麦克风 + 比较器) ×1
    • 0.96 寸 I2C OLED 显示屏 ×1
    • 杜邦线若干
    • USB 下载器、供电电源

    6.2 实物演示操作步骤

  • 按照上面硬件接线表完成接线,确认所有 GND 共地,检查 VCC 供电不要接反。
  • 把编译完成程序下载进 STM32 开发板,上电。
  • 灵敏度电位器调节 ①上电保持环境安静,观察 OLED 屏幕; ②缓慢旋转声音模块电位器,安静环境下保证 OLED 稳定显示SOUND: NO; ③距离模块合适距离拍手,观察屏幕切换成SOUND: YES,同时 COUNT 计数数字增加; ④如果安静的时候计数自己疯狂上涨,说明灵敏度太高,旋转电位器降低灵敏度;大声拍手不触发,则调高灵敏度。

    不同模块电位器旋转方向效果不一样,不要死记顺时针 / 逆时针,以实际屏幕现象为准。

  • 6.3 演示过程现象说明

  • 拍手一瞬间屏幕变成SOUND: YES,维持大约 500ms 变回 NO,属于程序显示保持,是正常现象;
  • COUNT 数字代表捕获的下降沿次数,一次拍手有可能计数增加 2‑3 次,这是因为声音信号抖动,多次跨过阈值产生多个下降沿,不是程序 bug;
  • 演示可以对比现象:如果注释掉外部中断,只保留 20ms 轮询,快速短促拍手,会出现计数丢失,以此体现外部中断优势。
  • 6.4 常见实物故障排查

    现象可能原因排查方式
    OLED 一直 SOUND: YES 灵敏度过高、OUT 持续低电平、接线错误 调节电位器,万用表测量 OUT 引脚电平
    永远 SOUND: NO,拍手无反应 模块没供电、没有共地、OUT 接线断开、阈值调太高 检查 VCC/GND,确认 PA0 接线,调高灵敏度
    安静环境下计数不停上涨 电源干扰、环境噪音、电位器处在阈值临界点 远离开关电源,重新微调电位器阈值
    拍一次手,计数跳多次 信号抖动反复触发比较器输出边沿 硬件增加迟滞电路,或者软件增加消抖延时

    七、项目局限性以及改进拓展思路

    数字声音检测模块只能做简单的声音有无触发,存在不少局限:

  • OUT 数字输出仅代表是否超过阈值,不能获取声音分贝、音量大小;
  • COUNT 统计是电平下降沿次数,不等于拍手次数,一次拍手会产生多个边沿;
  • 电位器阈值受电源电压、环境噪声影响;
  • 无法区分拍手、说话、环境噪音,只要声音超过阈值就触发;
  • 拓展升级方案

  • 使用模块 AO 模拟输出,接到 STM32 ADC 引脚,采集模拟波形,做包络检测、滤波,获取声音相对响度;
  • 软件增加消抖、延时过滤,解决一次拍手多次计数;
  • 如果要真正测量分贝,需要标定麦克风、稳定模拟放大电路,声压校准,不能直接用本模块数字输出;
  • 拓展功能:声音触发继电器、蜂鸣器报警、串口上报声音事件到电脑。
  • 八、总结

    本实验通过声音检测模块,把现实世界的声音振动转换成单片机能够识别的数字电平信号。 利用 STM32 的 EXTI 外部中断捕获转瞬即逝的下降沿脉冲,弥补单纯轮询检测丢失短事件的缺陷;再通过事件标志、计数变量、500ms 显示保持,将肉眼很难捕捉的短促声音事件直观展示在 OLED 屏幕。 适合学习 STM32 外部中断、GPIO 输入、临界区共享变量处理的入门实验。

    赞(0)
    未经允许不得转载:171主机测评 » STM32驱动声音检测模块+OLED显示项目完整讲解(外部中断捕获拍手声音)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址