【51单片机】蜂鸣器演奏音乐——小星星&天空之城
学习使用的开发板:STC89C52RC/LE52RC
编程软件:Keil5
烧录软件:stc-isp
开发板实图:
蜂鸣器
蜂鸣器在开发板的位置如下:
蜂鸣器是一种将电信号转换为声音信号的器件,常用来产生设备的按键音、报警音等提示信号
蜂鸣器按驱动方式可分为有源蜂鸣器和无源蜂鸣器
有源蜂鸣器
:内部自带振荡源,将正负极接上直流电压即可持续发生,频率固定。可用于按键音,报警音无源蜂鸣器
:内部不带振荡源,需要控制器提供振荡脉冲才可发声,调整提供振荡脉冲的频率,可发出不同频率的声音
蜂鸣器的原理图如下:
- VCC:电源正极
- BZ1:蜂鸣器
- BEEP:蜂鸣器I/O口
本单片机使用五线四项步进电机 ULN2003D
驱动电路
可以看到 BEEP 连通 P25
常见的蜂鸣器驱动电路为三极管驱动
- Buzzer:蜂鸣器
- VCC:电源正极
- GND:电源接地
三极管分为 NPN
和 PNP
,其中 N 为 N(Native)型半导体,P 为 P(Positive)型半导体。
三极管认识可参看B站“爱上半导体”《三极管是如何导电?超形象动画让你一看就懂!》、《三极管的饱和与放大》
此处了解,NPN型,R1处给高电平导通,低电平截止;PNP型,R1处给低电平导通,高电平截止
按键发声
蜂鸣器的使用很简单,因为只有一个I/O口——BEEP(P2^5)
控制蜂鸣器发声有两个因素——频率
和时长
频率控制蜂鸣器发声的音高,时长控制此次发声持续多久。代码如下:
#include <REGX52.h>
#include <INTRINS.h>
#include "Delay.h"
sbit Buzzer_BZ = P2^5;
/**
* @brief软件延时500us
* @parm无
* @retval无
*/
void Delay500us()//@11.0592MHz
{
unsigned char i;
_nop_();
i = 227;
while (--i);
}
/**
* @brief控制蜂鸣器发声时长
* @parmmx:发声的时长
* @retval无
*/
void Buzzer_Time(unsigned int mx)
{
unsigned int i;
//若传入mx:500,预期是响500ms
//但一次for循环耗时0.5ms,若要响500ms,for循环次数要加倍
for(i = 0; i < 2 * mx; ++i)
{
Buzzer_BZ = !Buzzer_BZ;
Delay500us();//每1ms响一次,1ms对应频率为1KHz
}
}
Buzzer_Time() 为发声逻辑,通过控制Buzzer_BZ(P2^5)变化的频率实现发声频率,500us变化一次,1 -> 0 -> 1为1ms,则频率为1KHz
完整代码如下:
蜂鸣器模块
Buzzer.h
#ifndef __BUZZER_H__
#define __BUZZER_H__
void Buzzer_Time(unsigned int mx);
#endif
Buzzer.c
#include <REGX52.h>
#include <INTRINS.h>
#include "Delay.h"
sbit Buzzer_BZ = P2^5;
/**
* @brief软件延时500us
* @parm无
* @retval无
*/
void Delay500us()//@11.0592MHz
{
unsigned char i;
_nop_();
i = 227;
while (--i);
}
/**
* @brief控制蜂鸣器发声时长
* @parmmx:发声的时长
* @retval无
*/
void Buzzer_Time(unsigned int mx)
{
unsigned int i;
//若传入mx:500,预期是响500ms
//但一次for循环耗时0.5ms,若要响500ms,for循环次数要加倍
for(i = 0; i < 2 * mx; ++i)
{
Buzzer_BZ = 1;
Delay500us();//每2ms响一次,1ms对应频率为1KHz,2ms对应频率为500Hz
}
}
按键模块
SoleKey.h
#ifndef __SOLEKEY_H__
#define __SOLEKEY_H__
unsigned char SoleKey();
#endif
SoleKey.c
#include <REGX52.h>
#include "Delay.h"
/**
* @brief 获取独立按键的键码(哪个被按下)
* @parm无
* @retval独立按键的键码
*/
unsigned char SoleKey()
{
unsigned char keyNum = 0;
if(P3_1 == 0){Delayms(20);while(P3_1 == 0);Delayms(20);keyNum = 1;}
if(P3_0 == 0){Delayms(20);while(P3_0 == 0);Delayms(20);keyNum = 2;}
if(P3_2 == 0){Delayms(20);while(P3_2 == 0);Delayms(20);keyNum = 3;}
if(P3_3 == 0){Delayms(20);while(P3_3 == 0);Delayms(20);keyNum = 4;}
return keyNum;
}
延时器模块
Delay.h
#ifndef __DELAY_H__
#define __DELAT_H__
void Delayms(unsigned int xms);//等待指定毫秒
#endif
Delay.c
/**
* @brief 延迟一定时间
* @parm延迟的时间,单位是毫秒,范围:0 ~ 65535
* @retval无
*/
void Delayms(unsigned int xms)//@12.000MHz
{
while(xms--)
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
数码管模块
Nixie.h
#ifndef __NIXIE_H__
#define __NIXIE_H__
void LightNixieTube(unsigned char location, unsigned char num);
#endif
Nixie.c
#include <REGX52.H>
#include "Delay.h"
//指定的数字有哪些地方要亮,对应数值的数组
unsigned char NixieTubeNums[] = {0x3F, 0x06, 0x5B, 0x4F,0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};
/**
* @brief 显示指定位置指定数字
* @parm 第一个参数是从左往右要显示的位置 范围: 1~8
* @parm 第二个参数是要显示的数字 范围: 0~9
* @retval 无
*/
void LightNixieTube(unsigned char location, unsigned char num)
{
//第一个位置是LED8,对应38译码器111
switch(location)
{
case 1:P2_4 = 1;P2_3 = 1;P2_2 = 1;break;
case 2:P2_4 = 1;P2_3 = 1;P2_2 = 0;break;
case 3:P2_4 = 1;P2_3 = 0;P2_2 = 1;break;
case 4:P2_4 = 1;P2_3 = 0;P2_2 = 0;break;
case 5:P2_4 = 0;P2_3 = 1;P2_2 = 1;break;
case 6:P2_4 = 0;P2_3 = 1;P2_2 = 0;break;
case 7:P2_4 = 0;P2_3 = 0;P2_2 = 1;break;
case 8:P2_4 = 0;P2_3 = 0;P2_2 = 0;break;
}
//指定的数字有哪些地方要亮
P0 = NixieTubeNums[num];
}
主程序——获取按键键码,显示在数码管上,并发出按键提示音
main.c
#include <REGX52.h>
#include "Buzzer.h"
#include "Nixie.h"
#include "SoleKey.h"
void main()
{
unsigned char Key;
LightNixieTube(1, 0);
while(1)
{
Key = SoleKey();
if(Key)
{
LightNixieTube(1, Key);
Buzzer_Time(100);
}
}
}
项目链接:蜂鸣器——按键发声
无源蜂鸣器演奏音乐
简单乐理
c1为中央C,中音1,最中间的按键,以此划分区域
钢琴上每8个白键一组,算上黑键则12一组,例如从c ~ b
相邻的黑白键相差半音,升半音符号和降半音符号如下:
一组按键对应简谱如下:
相邻组的音高相差八度,如小字1组的 c 比小字组的 c 高八度。对应简谱如下:
无源蜂鸣器通过调整振荡脉冲的频率实现发出不同频率的声音。
我们通过定时器中断函数
实现频率的变化,定时器使用参看【51单片机】定时器
定时器中断函数
模版如下:
//定时器中断函数——实现蜂鸣器不同音高发声
void Timer0_Rountine(void) interrupt 1
{
TH0 = ??
TL0 = ??
}
定时器逻辑简单描述就是:
- 有两个8位计数器,TH0 和 TL0,TH0 存储高位数据。TL0 存储低位数据。两个计数器记录完整的数据,范围:0 ~ 65535
- 根据系统频率,如12MHz,12T模式,则 12 / 12 = 1MHz,即每 1us 对计数器加一
- 当计数器溢出时,发出定时器中断,执行上述定时器中断函数
- 通过设置 TH0 和 TL0 重载值,可以实现不同时间发出中断,如 TH0 和 TL0 组合为 64535,距离溢出还有 1000,则溢出还需 1000us,即1ms,如此就实现了每1ms发出一次中断
如何通过定时器实现不同频率的发声呢?—— 通过设置 TH0 和 TL0 重装值,并在中断函数中改变BEEP状态,就可以实现每隔一段时间改变BEEP状态,这就是频率。
不同的重装值,就会对应不同的频率,发出不同的音高
对此,我们需要获取相应音符对应的重装值
频率与C调音符对照表如下:
后续曲目只涉及小字组(低音) ~ 小字2组(高音)
对频率进行如下转变:如低音1
262Hz = 0.000262MHz = 3816.793893us(周期)
因为BEEP进行两次操作为一次发生,所以周期需要 / 2
3816.793893 / 2 = 1908.396947,取整为1908,即需要每1908us 对 BEEP 状态改变一次
重装值:65535 - 1908 = 63628
重装值如下:
有了重装值就可以进行编码了
小星星
小星星简谱如下:
重装值可以设置频率,音高,但还需要控制音符的演奏时长
对应时长,有二分音符,四分音符…
音符之间为两倍差值
定义四分音符——500ms为基准,二分音符则持续1000ms,八分音符持续250ms,十六分音符持续125ms
简谱的左侧4/4
表示以四分音符为一拍,每小节四拍
第二小节6 6 5 -
,每个音符都是四分音符,持续500ms,但-
表示延续,即5 -
,表示中音5延迟一拍,持续两个四分音符,1000ms
小星星较为简单,到此就可以进行编码了
先定义每个音符对应的重装值
unsigned int code Frequency[] = {
0,
63628, 63731, 63835, 63928, 64021, 64103, 64185, 64260, 64331, 64400, 64463, 64524,
64580, 64633, 64684, 64732, 64777, 64820, 64860, 64898, 64934, 64968, 65000, 65030,
65058, 65085, 65110, 65134, 65157, 65178, 65198, 65217, 65235, 65252, 65268, 65283
};
0表示休止符,不发声
再对音符进行编号,对应重装值下标,并设置基准四分音符时长为500ms
//播放速度,值为四分音符的时长(ms)
#define SPEED 500
//音阶和频率对照表的索引
#define P 0 //休止符
//低音
#define L1 1
#define L1_ 2
#define L2 3
#define L2_ 4
#define L3 5
#define L4 6
#define L4_ 7
#define L5 8
#define L5_ 9
#define L6 10
#define L6_ 11
#define L7 12
//中音
#define M1 13
#define M1_ 14
#define M2 15
#define M2_ 16
#define M3 17
#define M4 18
#define M4_ 19
#define M5 20
#define M5_ 21
#define M6 22
#define M6_ 23
#define M7 24
//高音
#define H1 25
#define H1_ 26
#define H2 27
#define H2_ 28
#define H3 29
#define H4 30
#define H4_ 31
#define H5 32
#define H5_ 33
#define H6 34
#define H6_ 35
#define H7 36
其中,L1_ 表示 L1 升半音,也就是 c 右侧的黑键
接下来就可以扒谱了,将简谱抽象成一个数组,每两个元素为一组,分别为频率对应重装值的下标 和 持续时长,持续时长以125ms为单位
如1 1 5 5 | 6 6 5 -
,转化为数组为
M1,4,
M1,4,
M5,4,
M5,4,
M6,4,
M6,4,
M5,4+4
小星星简谱如下:
//小星星乐谱
//两个一组:频率索引,播放时长
unsigned char code Music[] = {
//S1
M1, 4,
M1, 4,
M5, 4,
M5, 4,
//S2
M6, 4,
M6, 4,
M5, 4+4,
//S3
M4, 4,
M4, 4,
M3, 4,
M3, 4,
//S4
M2, 4,
M2, 4,
M1, 4+4,
//S5
M5, 4,
M5, 4,
M4, 4,
M4, 4,
//S6
M3, 4,
M3, 4,
M2, 4+4,
//S7
M5, 4,
M5, 4,
M4, 4,
M4, 4,
//S8
M3, 4,
M3, 4,
M2, 4+4,
//S9
M1, 4,
M1, 4,
M5, 4,
M5, 4,
//S10
M6, 4,
M6, 4,
M5, 4+4,
//S11
M4, 4,
M4, 4,
M3, 4,
M3, 4,
//S12
M2, 4,
M2, 4,
M1, 4+4,
//终止符
0xFF
};
最后用终止符防止数组越界,在遇到终止符时关闭定时器
演奏音乐的逻辑如下:
//控制频率 遍历乐谱
unsigned char FrequencySelect, MusicSelect;
void main()
{
Timer0_Init();
while(1)
{
//终止符:音乐结束
if(Music[MusicSelect] != 0xFF)
{
FrequencySelect = Music[MusicSelect];//选择音符对应频率
MusicSelect++;
Delayms(SPEED / 4 * Music[MusicSelect]);//选择播放时长
MusicSelect++;
//相邻音符停顿一下下
TR0 = 0;
Delayms(5);
TR0 = 1;
}
else//终止符,停止播放
{
TR0 = 0;//关闭定时器
while(1);
}
}
}
//蜂鸣器按频率发声
void Timer0_Rountine(void) interrupt 1
{
//如果不是休止符
if(Frequency[FrequencySelect])
{
TH0 = Frequency[FrequencySelect] / 256;//高8位
TL0 = Frequency[FrequencySelect] % 256;//低8位
Buzzer_BZ = !Buzzer_BZ;
}
}
- 在主函数中,遍历乐谱,选择对应频率的下标。
- Delayms 实现发声的持续时长,在Delayms 前和 Delayms期间,通过定时器中断实现不同频率的发声
- 遇到休止符则不改变Buzzer_ER,不发声
- 遇到终止符则关闭定时器,停止演奏
天空之城
对一些音符进行说明
半音,即四分音符的一半——八分音符,对应频率和时长如下:
M6, 2,
M7, 2,
延音,延长原音符的一半,即四分音符+八分音符,对应频率和时长如下:
H1, 4+2,
升半音,对应频率和时长如下:
M4_, 2,
连音,连线的音符为同一音,对应频率和时长如下:
H1, 2,
M7, 2+2,
H1, 2+4
循环符号,回到乐谱最开始循环
完整乐谱如下:
//天空之城乐谱
unsigned char code Music[] = {
//S1
P,4,
P,4,
P,4,
M6,2,
M7,2,
//S2
H1,4+2,
M7,2,
H1,4,
H3,4,
//S3
M7,4+4+4,
M3,2,
M3,2,
//S4
M6,4+2,
M5,2,
M6,4,
H1,4,
//S5
M5,4+4+4,
M3,4,
//S6
M4,4+2,
M3,2,
M4,4,
H1,4,
//S7
M3,4+4,
P,2,
H1,2,
H1,2,
H1,2,
//S8
M7,4+2,
M4_,2,
M4,4,
M7,4,
//S9
M7,4+4,
P,4,
M6,2,
M7,2,
//S10
H1,4+2,
M7,2,
H1,4,
H3,4,
//S11
M7,4+4+4,
M3,2,
M3,2,
//S12
M6,4+2,
M5,2,
M6,4,
H1,4,
//S13
M5,4+4+4,
M2,2,
M3,2,
//S14
M4,4,
H1,2,
M7,2+2,
H1,2+4,
//S15
H2,2,
H2,2,
H3,2,
H1,2+4+4,
//S16
H1,2,
M7,2,
M6,2,
M6,2,
M7,4,
M5_, 4,
//S17
M6,4+4+4,
H1,2,
H2,2,
//S18
H3,4+2,
H2,2,
H3,4,
H5,4,
//S19
H2,4+4+4,
M5,2,
M5,2,
//S20
H1,4+2,
M7,2,
H1,4,
H3,4,
//S21
H3,4+4+4+4,
//S22
M6,2,
M7,2,
H1,4,
M7,4,
H2,2,
H2,2,
//S23
H1,4+2,
M5,2+4+4,
//S24
H4,4,
H3,4,
H2,4,
H1,4,
//S25
H3,4+4+4,
H3,4,
//S26
H6,4+4,
H5,4,
H5,4,
//S27
H3,2,
H2,2,
H1,4+4,
P,2,
H1,2,
//S28
H2,4,
H1,2,
H2,2,
H2,4,
H5,4,
//S29
H3,4+4+4,
H3,4,
//S30
H6,4+4,
H5,4+4,
//S31
H3,2,
H2,2,
H1,4+4,
P,2,
H1,2,
//S32
H2,4,
H1,2,
H2,2+4,
M7,4,
//S33
M6,4+4+4,
P,4,
0xFF//终止标志
};
项目链接:蜂鸣器——播放音乐
效果如下:
51单片机蜂鸣器演奏天空之城
以上就是本篇博客的所有内容,感谢你的阅读
如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。
原文地址:https://blog.csdn.net/m0_72563041/article/details/143606167
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!