【嵌入式成长10】STC89C51蜂鸣器|有源/无源蜂鸣器区分、三极管驱动、定时器播放音乐实战

一、蜂鸣器应用场景

蜂鸣器把电信号转换成声音信号,电子产品用来做状态提示:按键滴声、报警、故障提醒。

空调、电水壶、风扇、单片机开发板上都可以见到蜂鸣器。

二、有源蜂鸣器 vs 无源蜂鸣器

⚠️这里的"源"指振荡源,不是电源!

类型 有源蜂鸣器(Active) 无源蜂鸣器(Passive)
内部结构 自带振荡电路 无振荡源,类似小喇叭
驱动条件 施加直流电压就发声 必须输入一定频率的方波脉冲才发声
音调 频率固定,不能变调 修改脉冲频率,输出不同音调,可以播放音乐
识别引脚 顶部印有+标识;长引脚为正极 同样长脚正极,外观很难区分,只能看手册
  • 有源蜂鸣器:IO直接输出高低电平控制通电断电即可响,代码简单,只能一种音调。
  • 无源蜂鸣器:需要单片机输出高低交替方波;可以演奏音乐,是本章实验对象。

💡想让单片机"唱歌",必须使用无源蜂鸣器

三、硬件驱动电路解析

STC89C51单片机IO口驱动电流很小,不足以直接驱动蜂鸣器,开发板使用NPN三极管做开关放大电流,配合续流二极管保护器件。

开发板硬件:

  • 蜂鸣器控制引脚:BUZZE → P2.1
  • 三极管FMMT491做开关;R48、R47基极限流电阻;D25为续流二极管,吸收关断瞬间反向电动势,保护三极管。

电路逻辑:

  1. P2.1输出高电平,三极管导通,无源蜂鸣器得到脉冲信号发声。
  2. P2.1输出低电平,三极管截止,蜂鸣器停止。

注意:无源蜂鸣器只给恒定高电平是不会响的,需要不断翻转IO输出方波。

四、基础代码实验

示例1:软件延时翻转IO,无源蜂鸣器发声(12M晶振)

循环翻转P2.1引脚,生成方波,蜂鸣器发出滴滴声。

复制代码
#include<reg52.h>

sbit speak = P2^1;   //蜂鸣器控制引脚P2.1

//毫秒延时函数 @12MHz
void delay_ms(unsigned int xms)
{
    unsigned int i, j;
    for(i=xms;i>0;i--)
    {
        for(j=124;j>0;j--);
    }
}

void main(void)
{
    while(1)
    {
        speak = 1;
        delay_ms(1);
        speak = 0;
        delay_ms(1);
    }
}

缺点:软件延时占用CPU,程序卡在延时中,无法处理其他业务;实际项目优先用定时器中断翻转IO

示例2:有源蜂鸣器简单提示音

如果你硬件是有源蜂鸣器,不需要方波,直接控制电平持续时间:

复制代码
#include<reg52.h>
sbit speak = P2^1;
void delay_ms(unsigned int xms)
{
    unsigned int i, j;
    for(i=xms;i>0;i--)
        for(j=124;j>0;j--);
}

void main(void)
{
    while(1)
    {
        speak = 1;    //响
        delay_ms(500);
        speak = 0;    //停
        delay_ms(500);
    }
}

五、进阶实战:无源蜂鸣器定时器中断播放音乐《Happy New Year》

原理:使用定时器0中断,定时翻转蜂鸣器IO,产生不同频率方波;数组存储每一个音符对应的定时器初值与节拍时长;主循环遍历乐曲数组播放歌曲。

复制代码
#include <reg52.h>
#define uchar unsigned char

sbit speak = P2 ^ 1;
bit flag = 0;        //电平翻转标志
uchar ptr = 0x00;    //歌曲数组索引
uchar high;          //定时器初值高8位
uchar low;           //定时器初值低8位

//乐曲数组:Happy New Year
//格式:[TH0,TL0,节拍时长],0xff休止符,0x00歌曲结束
uchar code music[] = {
    0xfc, 0x44, 0x7f, 0xfc, 0x44, 0x7f, 0xfc, 0x44, 0xff, 0xfa, 0x68, 0xff,
    0xfd, 0x23, 0x7f, 0xfd, 0x23, 0x7f, 0xfd, 0x23, 0xff, 0xfc, 0x44, 0xff,
    0xfc, 0x44, 0x7f, 0xfd, 0x23, 0x7f, 0xfd, 0x82, 0xff, 0xfd, 0x82, 0xff,
    0xfd, 0x23, 0x7f, 0xfd, 0x23, 0x7f, 0xfc, 0xac, 0xff, 0xff, 0xff, 0xff,
    0xfc, 0xac, 0x7f, 0xfd, 0x23, 0x7f, 0xfd, 0x34, 0xff, 0xfd, 0x34, 0xff,
    0xfd, 0x23, 0x7f, 0xfc, 0xac, 0x7f, 0xfd, 0x23, 0xff, 0xfc, 0x44, 0xff,
    0xfc, 0x44, 0x7f, 0xfd, 0x23, 0x7f, 0xfc, 0xac, 0xff, 0xfa, 0x68, 0xff,
    0xfc, 0x0c, 0x7f, 0xfc, 0xac, 0x7f, 0xfc, 0x44, 0xff, 0xff, 0xff, 0xff,
    0x00
};

//毫秒延时 @12MHz
void delay_ms(unsigned int xms)
{
    unsigned int i, j;
    for (i = xms; i > 0; i--)
    {
        for (j = 124; j > 0; j--)
        {
        }
    }
}

//定时器0中断:翻转蜂鸣器IO产生方波
void Count1(void) interrupt 1
{
    TH0 = high;
    TL0 = low;
    if (flag == 0)
    {
        speak = 0;
        flag = 1;
    }
    else
    {
        speak = 1;
        flag = 0;
    }
}

//定时器初始化
void Init(void)
{
    TMOD = 0x01;   //定时器0模式1 16位
    EA = 1;        //总中断打开
    ET0 = 1;       //开启定时器0中断
}

void main(void)
{
    uchar time;
    Init();
    TH0 = high;
    TL0 = low;
    while (1)
    {
        if (music[ptr] != 0xff && music[ptr] != 0x00)
        {
            TR0 = 0;           //关闭定时器,停止发声
            speak = 1;
            delay_ms(10);
            high = music[ptr];     //取出音符TH0
            low = music[ptr + 1];  //取出音符TL0
            time = music[ptr + 2]; //取出节拍时长
            TR0 = 1;               //开启定时器,播放当前音符
            delay_ms(time);
            ptr += 3;
        }
        else if (music[ptr] == 0xff) //休止符,静音
        {
            time = music[ptr + 2];
            delay_ms(time);
            ptr += 3;
        }
        else //0x00歌曲播放完毕,停顿2秒从头循环
        {
            TR0 = 0;
            speak = 1;
            delay_ms(2000);
            ptr = 0;
        }
    }
}

代码逻辑说明

  1. 定时器0中断负责定时翻转P2.1引脚电平,生成方波信号;不同初值对应不同频率,对应不同音符。
  2. music[]数组存储整首歌,每3个字节一组:TH0初值、TL0初值、节拍时间
  3. 0xff代表休止符,不发声;0x00代表歌曲结束,停顿2秒,索引置0循环重播。
  4. 播放音符时打开定时器;休止/结束关闭定时器。

六、开发踩坑总结

  1. 有源无源千万不要搞混:无源蜂鸣器恒定高电平不会响,必须输入方波;有源蜂鸣器给方波声音异常。
  2. 单片机IO不能直接驱动蜂鸣器,必须依靠三极管放大电流;开发板已经做好外围电路,直接操作IO引脚。
  3. 续流二极管不能省略,感性负载关断会产生反向高压,容易损坏三极管和单片机。
  4. 软件延时生成方波会阻塞CPU;音乐项目推荐定时器中断翻转IO。
  5. 晶振改变,定时器初值需要重新调整,音调才准确。

七、本章小结

  1. 有源蜂鸣器:直流电平控制,音调固定;无源蜂鸣器需要方波脉冲,可以播放音乐。
  2. 三极管做开关放大电流,解决单片机IO驱动能力不足的问题。
  3. 无源蜂鸣器可以软件延时翻转IO发声;高级用法使用定时器中断生成不同频率方波播放乐曲。
  4. 乐曲数组保存音符定时器初值与节拍,实现循环播放歌曲。
相关推荐
fanged4 小时前
English at Work小结(TODO)
学习
youcans_4 小时前
【嵌入式软件AI编程】08. 第一个STM32工程
stm32·单片机·ai编程·嵌入式软件·claude code
X-future4264 小时前
OpenClaw源码学习
学习
kaixin_啊啊5 小时前
中国研究生数学建模竞赛(华为杯)学习笔记——三维瀑布图绘图代码
笔记·学习·数学建模
动词ing5 小时前
【学习笔记】数据结构(链表合并 双指针合并有序链表)
数据结构·笔记·学习
风之清扬5 小时前
Agent学习之四-初识Agent CLI
人工智能·科技·学习
传奇开心果编程5 小时前
【xilem0.4基础语法学与练】第45课 xilem_core
学习·rust·前端框架
Vcaker5 小时前
Linux学习28-Kubernetes service
linux·运维·学习
那年窗外下的雪.5 小时前
AIDC 学习日志|第 25 天|设备输出反推、MAC Flapping 与 EAD 撤销
前端·网络·git·学习·macos
崇山峻岭之间6 小时前
HC32F460PETB的PH2的TIM4_2_CLK
单片机