学习使用的开发板: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 控制蜂鸣器发声时长* @parm mx:发声的时长* @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 控制蜂鸣器发声时长* @parm mx:发声的时长* @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译码器111switch(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[] = {//S1M1, 4,M1, 4,M5, 4,M5, 4,//S2M6, 4,M6, 4,M5, 4+4,//S3M4, 4,M4, 4,M3, 4,M3, 4,//S4M2, 4,M2, 4,M1, 4+4,//S5M5, 4,M5, 4,M4, 4,M4, 4,//S6M3, 4,M3, 4,M2, 4+4,//S7M5, 4,M5, 4,M4, 4,M4, 4,//S8M3, 4,M3, 4,M2, 4+4,//S9M1, 4,M1, 4,M5, 4,M5, 4,//S10M6, 4,M6, 4,M5, 4+4,//S11M4, 4,M4, 4,M3, 4,M3, 4,//S12M2, 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[] = {//S1P,4,P,4,P,4,M6,2,M7,2,//S2H1,4+2,M7,2,H1,4,H3,4,//S3M7,4+4+4,M3,2,M3,2,//S4M6,4+2,M5,2,M6,4,H1,4,//S5M5,4+4+4,M3,4,//S6M4,4+2,M3,2,M4,4,H1,4,//S7M3,4+4,P,2,H1,2,H1,2,H1,2,//S8M7,4+2,M4_,2,M4,4,M7,4,//S9M7,4+4,P,4,M6,2,M7,2,//S10H1,4+2,M7,2,H1,4,H3,4,//S11M7,4+4+4,M3,2,M3,2,//S12M6,4+2,M5,2,M6,4,H1,4,//S13M5,4+4+4,M2,2,M3,2,//S14M4,4,H1,2,M7,2+2,H1,2+4,//S15H2,2,H2,2,H3,2,H1,2+4+4,//S16H1,2,M7,2,M6,2,M6,2,M7,4,M5_, 4,//S17M6,4+4+4,H1,2,H2,2,//S18H3,4+2,H2,2,H3,4,H5,4,//S19H2,4+4+4,M5,2,M5,2,//S20H1,4+2,M7,2,H1,4,H3,4,//S21H3,4+4+4+4,//S22M6,2,M7,2,H1,4,M7,4,H2,2,H2,2,//S23H1,4+2,M5,2+4+4,//S24H4,4,H3,4,H2,4,H1,4,//S25H3,4+4+4,H3,4,//S26H6,4+4,H5,4,H5,4,//S27H3,2,H2,2,H1,4+4,P,2,H1,2,//S28H2,4,H1,2,H2,2,H2,4,H5,4,//S29H3,4+4+4,H3,4,//S30H6,4+4,H5,4+4,//S31H3,2,H2,2,H1,4+4,P,2,H1,2,//S32H2,4,H1,2,H2,2+4,M7,4,//S33M6,4+4+4,P,4,0xFF//终止标志
};
项目链接:蜂鸣器——播放音乐
效果如下:
51单片机蜂鸣器演奏天空之城
以上就是本篇博客的所有内容,感谢你的阅读
如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。