【嵌入式成长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. 乐曲数组保存音符定时器初值与节拍,实现循环播放歌曲。
相关推荐
那年窗外下的雪.1 小时前
Linux 学习笔记
linux·笔记·学习
爱奥尼欧2 小时前
【daily practice】最长数字串怎么找?岛屿数量如何沉岛秒数?六棍拼两三角形有几种验法?(5.字符串中找出连续最长的数字串、岛屿数量、拼三角)
笔记·学习
Bernice橘子2 小时前
职场英语学习计划Day040
学习
郎哥编程课堂2 小时前
单片机零基础训练营第2天:嵌入式开发常用的三种进制
单片机·嵌入式硬件
LCG元2 小时前
STM32 DAC+DMA+定时器触发生成任意波形:数据对齐陷阱、输出缓冲器选型与频率精度实测
stm32·单片机·嵌入式硬件
etc95272 小时前
IAR下如何同时指定section的LMA和VMA
arm开发·嵌入式硬件·mcu
橙橙笔记3 小时前
Python学习第三部分
开发语言·python·学习
weixin_431600443 小时前
Agent Workflow 学习向:最小拖拽画布,看得见地编排,再一键跑通
后端·学习·ai·dify
LuminousCPP3 小时前
嵌入式-51单片机入门(三)-数码管驱动:从单管点亮、多位扫描到动态刷新与消隐原理
笔记·单片机·嵌入式硬件·51单片机