51单片机STC89C52RC——11.1 蜂鸣器播放音乐

发布于:2024-06-28 ⋅ 阅读:(14) ⋅ 点赞:(0)

目录

目的/效果

一,STC单片机模块

二,蜂鸣器

 2.1 介绍

2.2 板子位置+电路图

2.3 发声原理

2.4 音符和频率

三,创建Keil项目

四,代码 

4.1 乐谱代码

4.1.1 《义勇军进行曲》

 4.1.2 《天空之城》

4.1.3 《小美满》 

4.1.4 《兰花草》

4.1.5 《救护车》

4.1.6 《消防车》

乐谱待优化点

 4.2 蜂鸣器播放的音乐代码

五,代码编译、下载到51单片机


目的/效果

1:让蜂鸣器响起来

2:蜂鸣器播放音乐(《义勇军进行曲》《天空之城》《小美满》《兰花草》等) 

一,STC单片机模块

二,蜂鸣器

2.1 介绍

蜂鸣器是一种将电信号转换为声音信号的器件,常用来产生设备的按键音、报警音等提示信号

蜂鸣器按驱动方式可分为有源蜂鸣器无源蜂鸣器。

有源蜂鸣器:内部自带振荡源,将正负极接上直流电压即可持续发声,频率固定

无源蜂鸣器:内部不带振荡源,需要控制器提供振荡脉冲才可发声,调整提供振荡脉冲的频率,可发出不同频率的声音(本节内容全部基于无源蜂鸣器)

2.2 板子位置+电路图

2.3 发声原理

翻转蜂鸣器的IO口,

    while(1){
        P2_5=!P2_5;//翻转蜂鸣器端口
    }

上面这段代码 执行时,可能听不到声音。

根据人耳朵的听力范围20-20000Hz

那么我们延时周期时长范围为

12000000/12/20000=50us,(最高频率时间间隔)

12000000/12/20=50000us=50ms (最低频率时间间隔)


/**
 * 函    数:主函数
 * 参    数:无
 * 返 回 值:无
 */
void main()
{
	while(1){
		Delay_ms(2);//延时2毫秒【1秒钟翻转500次,相当于500Hz】
		P2_5=!P2_5;//翻转蜂鸣器端口
	}
}

2.4 音符和频率

标准的A的频率为440(每秒440次),相邻两个半音之间的比值是\sqrt[12]{2},所以我们根据标准音去计算其他音的频率,如下表

音符 频率/Hz(计算值,保留小数) 频率/Hz(每秒震动次数 取整)
低音1 411.5292003 412
低音1# 436 436
低音2 412.4730746 412
低音2# 437 437
低音3 413.416949 413
低音4 438 438
低音4# 414.3608233 414
低音5 439 439
低音5# 415.3046976 415
低音6(标准) 440 440
低音6# 466.1637615 466
低音7 493.8833013 494
中音1 523.2511306 523
中音1# 554.365262 554
中音2 587.3295358 587
中音2# 622.2539674 622
中音3 659.2551138 659
中音4 698.4564629 698
中音4# 739.9888454 740
中音5 783.990872 784
中音5# 830.6093952 831
中音6 880 880
中音6# 932.327523 932
中音7 987.7666025 988
高音1 1046.502261 1047
高音1# 1108.730524 1109
高音2 1174.659072 1175
高音2# 1244.507935 1245
高音3 1318.510228 1319
高音4 1396.912926 1397
高音4# 1479.977691 1480
高音5 1567.981744 1568
高音5# 1661.21879 1661
高音6 1760 1760
高音6# 1864.655046 1865
高音7 1975.533205 1976

有了上面的频率(震动次数)我们再通过系统中断来翻转蜂鸣器.每秒翻转对应次数蜂鸣器就能发出对应频率的声音。

再利用晶振频率计算器单个震动的周时长

单个周期时长=晶振周期/12时钟周期/频率

比如 标准音A 所耗周期时长12000000/12/440=2272,由于蜂鸣器电频翻转只需要一半,所以还需要将周期减半1136。再利用定时器溢出发生中断,计算定时器的重转值。重装值=65536-周期时长65536-1136=64400 其他如下表

音符 频率/Hz(计算值,保留小数) 频率/Hz(每秒震动次数 取整) 周期时长/us 周期/2 us 周期取整 定时器重装值
低音1 261.6255653 262 3816.793893 1908.396947 1908 63628
低音1# 277.182631 277 3610.108303 1805.054152 1805 63731
低音2 293.6647679 294 3401.360544 1700.680272 1701 63835
低音2# 311.1269837 311 3215.434084 1607.717042 1608 63928
低音3 329.6275569 330 3030.30303 1515.151515 1515 64021
低音4 349.2282314 349 2865.329513 1432.664756 1433 64103
低音4# 369.9944227 370 2702.702703 1351.351351 1351 64185
低音5 391.995436 392 2551.020408 1275.510204 1276 64260
低音5# 415.3046976 415 2409.638554 1204.819277 1205 64331
低音6(标准) 440 440 2272.727273 1136.363636 1136 64400
低音6# 466.1637615 466 2145.922747 1072.961373 1073 64463
低音7 493.8833013 494 2024.291498 1012.145749 1012 64524
中音1 523.2511306 523 1912.045889 956.0229446 956 64580
中音1# 554.365262 554 1805.054152 902.5270758 903 64633
中音2 587.3295358 587 1703.577513 851.7887564 852 64684
中音2# 622.2539674 622 1607.717042 803.8585209 804 64732
中音3 659.2551138 659 1517.450683 758.7253414 759 64777
中音4 698.4564629 698 1432.664756 716.3323782 716 64820
中音4# 739.9888454 740 1351.351351 675.6756757 676 64860
中音5 783.990872 784 1275.510204 637.755102 638 64898
中音5# 830.6093952 831 1203.369434 601.6847172 602 64934
中音6 880 880 1136.363636 568.1818182 568 64968
中音6# 932.327523 932 1072.961373 536.4806867 536 65000
中音7 987.7666025 988 1012.145749 506.0728745 506 65030
高音1 1046.502261 1047 955.1098376 477.5549188 478 65058
高音1# 1108.730524 1109 901.7132552 450.8566276 451 65085
高音2 1174.659072 1175 851.0638298 425.5319149 426 65110
高音2# 1244.507935 1245 803.2128514 401.6064257 402 65134
高音3 1318.510228 1319 758.1501137 379.0750569 379 65157
高音4 1396.912926 1397 715.8196135 357.9098067 358 65178
高音4# 1479.977691 1480 675.6756757 337.8378378 338 65198
高音5 1567.981744 1568 637.755102 318.877551 319 65217
高音5# 1661.21879 1661 602.0469597 301.0234798 301 65235
高音6 1760 1760 568.1818182 284.0909091 284 65252
高音6# 1864.655046 1865 536.1930295 268.0965147 268 65268
高音7 1975.533205 1976 506.0728745 253.0364372 253 65283

所以我们这里要定义音符 对应中断初始值


//索引与频率对照表
unsigned int FreqTable[]={
	0,//占位休止符
	63628,63731,63835,63928,64021,64103,64185,64260,64331,64400,64463,64528,
	64580,64633,64684,64732,64777,64820,64860,64898,64934,64968,65000,65030,
	65058,65085,65110,65134,65157,65178,65198,65217,65235,65252,65268,65283,
};

再将对应音符和索引定义好。


//音符与索引对应表,P:休止符,L:低音,M:中音,H:高音,下划线:升半音符号#
#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

三,创建Keil项目

详细参考:51单片机STC89C52RC——创建Keil项目-CSDN博客

四,代码 

4.1 乐谱代码

4.1.1 《义勇军进行曲》


//《义勇军进行曲》乐谱 //最短是 16分音符=1
unsigned char Music_GuoGe[]={
	//音符,时值,
  M1,2+1,
  M3,1,
  M5,2,
  M5,2,
  
  M6,4,
  M5,4,
  
  M3,2+1,
  M1,1,
  M5,1,
  M5,1,
  M5,1,
  
  M3,4,
  M1,4,
  
  L5,1,
  L5,1,
  L5,1,
  L5,1,
  L5,1,
  L5,1,
  
  M1,4,
  P,2,
  L5,2,
  
  M1,4+2,
  M1,2,
  
  M1,2+1,
  M1,2,
  
  L5,2,
  L6,1,
  L7,1,
  
  M1,4,
  M1,4,
  
  P,4,
  M3,4,
  
  M1,2,
  M2,1,
  M3,1,
  
  M5,4,
  M5,4,
  
  M3,2+1,
  M3,1,
  
  M1,2,
  M3,2+1,
  
  M5,2,
  M3,2+1,
  M2,4+4,
  
  M6,4,
  M5,4,
  
  M2,4,
  M3,4,
  
  M5,2,
  M3,2,
  P,2,
  M5,2,
  
  M3,2,
  M2,1,
  M3,1,
  M1,4,
  
  M3,4,
  P,4,
  
  L5,2+1,
  L6,1,
  M1,2,
  M1,2,
  
  M3,2+1,
  M3,1,
  M5,2,
  M5,2,
  
  M2,2,
  M2,1,
  M2,1,
  L6,4,
  
  M2,4+2,
  L5,2,
  
  M1,4+2,
  M1,2,
  
  M3,4+2,
  M3,2,
  
  M5,4+4,
  
  M1,2+1,
  M3,1,
  M5,2,
  M5,2,
  
  M6,4,
  M5,4,
  
  M3,2+1,
  M1,  1,
  M5,1,
  M5,1,
  M5,1,
  
  M3,2,
  P,2,  
  M1,2,
  P,2,
  
  L5,4,
  M1,4,
  
  
  M3,2+1,
  M1,  1,
  M5,1,
  M5,1,
  M5,1,
  
  M3,2,
  P,2,  
  M1,2,
  P,2,
  
  L5,4,
  M1,4,
  
  L5,4,
  M1,4,
  
  L5,4,
  M1,4,
  
  M1,4,
  P,4,
  
  P,4,
  P,4,
  P,4,
  P,4,
  0xFF	//终止标志
};

 4.1.2 《天空之城》


//《天空之城》乐谱
unsigned char code Music_CastleSky[]={
	//音符,时值,
	
	//1
	P,	4,
	P,	4,
	P,	4,
	M6,	2,
	M7,	2,
	
	H1,	4+2,
	M7,	2,
	H1,	4,
	H3,	4,
	
	M7,	4+4+4,
	M3,	2,
	M3,	2,
	
	//2
	M6,	4+2,
	M5,	2,
	M6, 4,
	H1,	4,
	
	M5,	4+4+4,
	M3,	4,
	
	M4,	4+2,
	M3,	2,
	M4,	4,
	H1,	4,
	
	//3
	M3,	4+4,
	P,	2,
	H1,	2,
	H1,	2,
	H1,	2,
	
	M7,	4+2,
	M4_,2,
	M4_,4,
	M7,	4,
	
	M7,	8,
	P,	4,
	M6,	2,
	M7,	2,
	
	//4
	H1,	4+2,
	M7,	2,
	H1,	4,
	H3,	4,
	
	M7,	4+4+4,
	M3,	2,
	M3,	2,
	
	M6,	4+2,
	M5,	2,
	M6, 4,
	H1,	4,
	
	//5
	M5,	4+4+4,
	M2,	2,
	M3,	2,
	
	M4,	4,
	H1,	2,
	M7,	2+2,
	H1,	2+4,
	
	H2,	2,
	H2,	2,
	H3,	2,
	H1,	2+4+4,
	
	//6
	H1,	2,
	M7,	2,
	M6,	2,
	M6,	2,
	M7,	4,
	M5_,4,
	
	
	M6,	4+4+4,
	H1,	2,
	H2,	2,
	
	H3,	4+2,
	H2,	2,
	H3,	4,
	H5,	4,
	
	//7
	H2,	4+4+4,
	M5,	2,
	M5,	2,
	
	H1,	4+2,
	M7,	2,
	H1,	4,
	H3,	4,
	
	H3,	4+4+4+4,
	
	//8
	M6,	2,
	M7,	2,
	H1,	4,
	M7,	4,
	H2,	2,
	H2,	2,
	
	H1,	4+2,
	M5,	2+4+4,
	
	H4,	4,
	H3,	4,
	H3,	4,
	H1,	4,
	
	//9
	H3,	4+4+4,
	H3,	4,
	
	H6,	4+4,
	H5,	4,
	H5,	4,
	
	H3,	2,
	H2,	2,
	H1,	4+4,
	P,	2,
	H1,	2,
	
	//10
	H2,	4,
	H1,	2,
	H2,	2,
	H2,	4,
	H5,	4,
	
	H3,	4+4+4,
	H3,	4,
	
	H6,	4+4,
	H5,	4+4,
	
	//11
	H3,	2,
	H2,	2,
	H1,	4+4,
	P,	2,
	H1,	2,
	
	H2,	4,
	H1,	2,
	H2,	2+4,
	M7,	4,
	
	M6,	4+4+4,
	P,	4,
	
	P,	4,
	P,	4,
	P,	4,
	P,	4,
	P,	4,
	0xFF	//终止标志
};

4.1.3 《小美满》 节选


//《小美满》乐谱
unsigned char code Music_XMM[]={
	//音符,时值,
	
	//
	P,2,
	M5,2,
	H1,2,
	H3,2,
	
	H5,4,
	H5,1,
	H6,1,
	H5,1,
	H6,1,

	H5,2,
	H3,2,
	H2,2,
	H1,2,
	
	H3,4,
	H5,1,
	H6,1,
	H5,1,
	H6,1,

	
	H5,4,
	H6,2,
	H5,2,
	H1,4,
	
	
	H6,2,
	H5,2,
	H1,4,
	
	M6,2,
	H1,2,
	
	
	H4,1,
	H5,1,
	H5,1,
	H5,1,
	H5,2,
	H1,2,
	
	H1,4,
	M7,4,
	
	H1,8,
	
	H5,2,
	H5,1,
	H5,1,
	H3,2,
	HH1,2,
	
	H5,4,
	
	H1,1,
	H6,1,
	H6,1,
	H6,1,
	H6,2,
	H5,2,
	
	H3,8,
	H1,1,
	H1,1,
	H1,1,
	M7,1,
	H1,2,
	H2,2,
	H3,2,
	H6,1,
	H5,1,
	H5,3,
	M6,1,
	
	H1,1,
	H1,1,
	H1,1,
	H1,1,
	H1,2,
	
	H5,1,
	H3,1,
	H2,8,
	
	H5,1,
	H5,3,
	H5,1,
	H3,2,
	HH1,2,
	H5,8,
	
	H1,1,
	H6,1,
	H6,1,
	H6,1,
	H6,2,
	H5,2,
	H3,4,
	H3,3,
	M6,1,
	
	H1,1,
	H1,1,
	H1,1,
	M7,1,
	H1,2,
	H2,2,
	H3,2,
	H6,1,
	H5,1,
	H5,4,
	
	
	H1,1,
	H1,1,
	H1,1,
	H1,1,
	H1,2,
	M7,2,
	H1,1,
	H6,1,
	H5,1,
	H6,1,
	H5,2,
	H2,2,
	
	H3,4,
	H3,1,
	H2,1,
	H1,2,
	H2,1,
	H2,1,
	H2,1,
	H2,1,
	H2,2,
	H1,2,
	H3,4,
	P,	4,
	P,	4,
	P,	4,
	P,	4,
	0xFF	//终止标志
};

4.1.4 《兰花草》


//《兰花草》
unsigned char code Music_LHC[]=
{
	L6,2,
	M3,2,
	M3,2,
	M3,2,
	M3,4,
	M3,2,
	M2,2,
	
	M1,2,
	M1,1,
	M2,1,
	M1,2,
	L7,2,
	L6,4,
	
	M6,2,
	M6,2,
	M6,2,
	M6,2,
	M6,4,
	M6,2,
	M5,2,

	M3,2,
	M5,2,
	M5,2,
	M4_,2,
	M3,8,

	M3,2,
	M6,2,
	M6,2,
	M5,2,
	M3,4,
	M3,2,
	M2,2,

	M1,2,
	M2,2,
	M1,2,
	L7,2,
	L6,4,
	L3,4,

	L3,2,
	M1,2,
	M1,2,
	L7,2,
	L6,4,
	L6,2,
	M3,2,
	M2,2,
	M2,1,
	M1,1,
	L7,2,
	L5,2,
	L6,8,
	
	P,4,
	P,4,
	P,4,
	P,4,
	0xff//结束
};

4.1.5 《救护车》



//救护车
unsigned char Music_Ambulance[]={
  H3,4,
  H1,4,
  0xFF	//终止标志
};

4.1.6 《消防车》


//消防车
unsigned char Music_FireTruck[]={
  H5,4,
  H7,4,
  0xFF	//终止标志
};

乐谱待优化点

1:由于这里设备内存限制,不可能将以上所有乐谱编译。

2:乐谱的音符和拍子的长短没能进行较为好的对象化编程,可以考虑{音符、时值}的代码模式进行再次封装。且节拍时值最好能固定,4分音符时值设置为T,其他时值根据这个做调整。比如下表

音符 写法 时长值 时长值
全音符 1--- 4T 四拍
二分音符 1-- 2T 二拍
四分音符 1 T 一拍
八分音符 1 T/2 半拍
十六分音符 以此类推 T/4 四分之一拍

要精确最好能将一个乐谱的最小时值设为T,或者将T设置为2的n次幂的数字,这样在做T/n时没有余数更为准确。 

3:乐谱没有考虑到和弦

 4.2 蜂鸣器播放的音乐代码

#include <REGX52.H>
#include "Delay.h"
#include "Timer0.h"

//蜂鸣器端口定义
sbit Buzzer=P2^5;

//播放速度,值为四分音符的时长(ms)
#define SPEED	500

//音符与索引对应表,P:休止符,L:低音,M:中音,H:高音,下划线:升半音符号#
#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

//索引与频率对照表
unsigned int FreqTable[]={
	0,
	63628,63731,63835,63928,64021,64103,64185,64260,64331,64400,64463,64528,
	64580,64633,64684,64732,64777,64820,64860,64898,64934,64968,65000,65030,
	65058,65085,65110,65134,65157,65178,65198,65217,65235,65252,65268,65283,
};

// 乐谱
unsigned char code Music[]=
{
	/*这里是乐谱的内容*/
	P,4,
	P,4,
	P,4,
	P,4,
	0xff//结束
};

unsigned char FreqSelect,MusicSelect;

void main()
{ 
	Timer0Init();
	while(1)
	{
		if(Music[MusicSelect]!=0xFF)	//如果不是停止标志位
		{
			FreqSelect=Music_LHC[MusicSelect];	//选择音符对应的频率
			MusicSelect++;
			Delay_ms(SPEED/4*Music[MusicSelect]);	//选择音符对应的时值
			MusicSelect++;
			TR0=0;
			Delay_ms(5);	//音符间短暂停顿
			TR0=1;
		}
		else	//如果是停止标志位
		{
			TR0=0;
			while(1);
		}
	}
}

void Timer0_Routine() interrupt 1
{
	if(FreqTable[FreqSelect])	//如果不是休止符
	{
		/*取对应频率值的重装载值到定时器*/
		TL0 = FreqTable[FreqSelect]%256;		//设置定时初值
		TH0 = FreqTable[FreqSelect]/256;		//设置定时初值
		Buzzer=!Buzzer;	//翻转蜂鸣器IO口
	}
}

注意:

1:这里面注意Music这个数组变量就是上面对应的乐谱代码的变量。如果设备内存允许你可以将以上曲目全部放入内存,用按钮动态设置切换上面的曲目(上一曲、下一曲、暂停/继续)

2:SPEED可以根据实际情况调整快慢(甚至可以做成按钮去控制播放速度

五,代码编译、下载到51单片机

代码编译请参考

51单片机STC89C52RC——代码编译-CSDN博客

代码下载请参考

《51单片机STC89C52RC——STCAI-ISP代码下载-CSDN博客