蜂鸣器为什么会响:有源、无源和低电平触发

水箱里的水慢慢上涨。水位还没有到警戒线时,系统安安静静地做自己的事;水一超过警戒线,旁边突然响起“滴”的一声。人不用盯着刻度,也知道该去处理了。

蜂鸣器,就是这样一个用声音把消息送到人的耳朵里的器件。

小车也是一样。传感器发现黑线丢了、前方检测到异常,或者某个数值超过了允许范围,程序可以让蜂鸣器响一下。传感器负责发现,蜂鸣器负责提醒,它们之间只隔着一句很普通的判断:

if (need_alarm)
{
    Buzzer_Set(1);
}
else
{
    Buzzer_Set(0);
}

need_alarm 是 1 还是 0,由前面的判断决定;Buzzer_Set() 只管把声音打开或关掉。把两件事分开以后,报警的理由变了,蜂鸣器的控制代码不需要跟着重写。

有源蜂鸣器:给它开关,它自己会叫

有源蜂鸣器内部带着一套让自己持续振动的电路。供电正常、控制脚处于有效状态时,它就会发出固定的声音;控制脚回到无效状态时,它停止。

所以 STM32 不需要自己去制造那个音调,只需要像按开关一样控制它:

GPIO 有效电平 -> 蜂鸣器内部电路工作 -> 发出声音
GPIO 无效电平 -> 蜂鸣器内部电路停止 -> 没有声音

若每隔一秒开一次、关一次,听到的是“响一秒,停一秒”。把开关节奏改快,提示声的节奏会变快;但基本音调仍由蜂鸣器内部决定,不会因为程序把间隔从 1000ms 改成 200ms 就变成另一个音符。

这里的“有源”不是说它不需要供电。任何蜂鸣器都要供电;有源和无源的区别是,让它持续发声的振荡电路是在模块里面,还是要由外部提供。

无源蜂鸣器:还差一串不停变化的电平

无源蜂鸣器没有内部振荡电路。若只给它一个稳定的高电平或低电平,它不会像有源蜂鸣器那样持续鸣叫。它需要 STM32 不断把控制脚在高、低电平之间切换,形成周期性的方波。

有源蜂鸣器:有效电平 ---------> 自己发出固定音调

无源蜂鸣器:高低高低高低…… -> 按这个变化的频率发声

方波变化得越快,声音通常越尖;变化得越慢,声音通常越低。定时器产生的 PWM 就很适合做这件事:它能稳定地输出连续变化的电平,程序改变频率,就能改变无源蜂鸣器的音调。

因此,有源蜂鸣器适合做“提示一下”“故障报警”这类简单声音;无源蜂鸣器能演奏不同音调,也就能做出更丰富的提示声。两者外形有时很像,不能只凭长相决定接法。

三根线里,哪一根在发命令

常见的有源蜂鸣器模块有三根引脚:VCC、GND 和 IN。

引脚 它负责的事
VCC 给模块供电
GND 和 STM32 共用电压参考点
IN 接收 STM32 GPIO 发出的开关命令

GPIO 接的是 IN,不是用 GPIO 给蜂鸣器本体供电。VCC 应接模块资料要求的电压,GND 必须和 STM32 的 GND 连在一起;没有共同的参考点,IN 上的高低电平就失去了明确的含义。

模块的 IN 也不是天然“高电平就响”。有的模块高电平有效,有的模块低电平有效,还有些模块要求的供电电压或输入电平与手上的最小系统板并不相同。丝印、模块说明和原理图比引脚颜色可靠得多。

这块模块为什么是低电平响

这次使用的有源蜂鸣器模块由 PB12 控制,低电平有效:PB12 输出低电平时蜂鸣器响,输出高电平时蜂鸣器停。

把这种细节收进 Buzzer_Set() 里,外部调用就不必反复记住“低电平才是打开”:

#include "buzzer.h"
#include "stm32f10x.h"

#define BUZZER_PORT  GPIOB
#define BUZZER_PIN   GPIO_Pin_12

void Buzzer_Init(void)
{
    GPIO_InitTypeDef gpio;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    gpio.GPIO_Pin = BUZZER_PIN;
    gpio.GPIO_Mode = GPIO_Mode_Out_PP;
    gpio.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_Init(BUZZER_PORT, &gpio);

    Buzzer_Set(0);
}

void Buzzer_Set(uint8_t on)
{
    if (on)
    {
        GPIO_ResetBits(BUZZER_PORT, BUZZER_PIN);
    }
    else
    {
        GPIO_SetBits(BUZZER_PORT, BUZZER_PIN);
    }
}

Buzzer_Set(1) 的含义始终是“响”,Buzzer_Set(0) 的含义始终是“停”。至于函数内部究竟写低电平还是高电平,只由手上这个模块的有效电平决定。

初始化的最后一行也不能省。GPIO 刚配置完成时,明确写一次关闭状态,下载或复位后才不会因为输出状态而突然叫起来。

这里的 PB12 只留给这个独立蜂鸣器实验。整车中它会承担电机方向控制,不能同时又接蜂鸣器的 IN;一根 GPIO 不能替两个彼此无关的模块同时发命令。

让声音只在该出现的时候出现

蜂鸣器最容易写成一直响。程序一发现异常就打开它,却没有想过异常持续十秒、半分钟,旁边的人会听见什么。

简单报警可以一直响,表示“问题还没有解除”;短促地响几下,更适合表示“刚刚发生了一次事件”。区别不在蜂鸣器本身,而在程序什么时候调用 Buzzer_Set(1),什么时候再调用 Buzzer_Set(0)。

例如,状态从正常变成异常的那一刻响一下;状态一直异常时不重复触发;恢复正常后允许下一次报警。

最后橘猫说

蜂鸣器看起来只会响和不响,背后却已经有了完整的分工:模块负责把电变成声音,GPIO 负责发出开关命令,程序决定什么情况值得提醒。先分清有源、无源和有效电平,声音才会在真正需要的时候出现。

最后蜘蛛说

一开始调试的时候,高低电平搞反了,你就是全场最靓的仔。

posted @ 2026-10-03 23:25  Zw-awa  阅读(21)  评论(0)    收藏  举报