单片机学习笔记---蜂鸣器播放提示音&音乐(天空之城)

目录

蜂鸣器播放提示音

蜂鸣器播放音乐(天空之城)

准备工作

主程序

中断函数


上一节讲了蜂鸣器驱动原理和乐理基础知识,这一节开始代码演示!

蜂鸣器播放提示音

先创建工程:蜂鸣器播放提示音

把我们之前模块化的程序文件添加进来

但是这次我们 是要静态显示,所以要把Nixie.c文件中这里删掉

我们要实现的效果是我们按下按键之后,会出现按键提示音。

创建一个Buzzer.c和Buzzer.h文件

具体代码解释请看注释:

Buzzer.c

cpp 复制代码
#include <REGX52.H>
#include <INTRINS.H>//_nop_函数的头文件

//蜂鸣器端口:普中A2的板子是P2^5,其他板子的可以试试P1^5
sbit Buzzer=P2^5;

//在博主以后的博客中,这个函数就作为蜂鸣器的私有延时函数
void Buzzer_Delay500us()		//@12.000MHz
{
	unsigned char i;

	_nop_();//延时一微秒的函数
	i = 247;
	while (--i);
}

//蜂鸣器的发声时长函数
void Buzzer_Time(unsigned int ms)
{
	unsigned int i;//蜂鸣器翻转的次数
	for(i=0;i<ms*2;i++)//一个for循环500us=0.5ms,x*0.5ms*2延时xms
	{
		Buzzer=!Buzzer;//翻转蜂鸣器
		//蜂鸣器的标准提示音是1000Hz
		Buzzer_Delay500us();//每500us=0.5ms翻转一次,那蜂鸣响的周期就是1000Hz
	}
}

Buzzer.h

cpp 复制代码
#ifndef __BUZZER_H__
#define __BUZZER_H__

void Buzzer_Time(unsigned int ms);

#endif

main.c

cpp 复制代码
#include <REGX52.H>
#include "Delay.h"
#include "Key.h"
#include "Nixie.h"
#include "Buzzer.h"

unsigned char KeyNum;

void main()
{
	Nixie(1,0);//第一位数码管显示0
	while(1)
	{
		KeyNum=Key();//按键按下后将键码赋值给keyNum
		if(KeyNum)//如果按键按下
		{
			Buzzer_Time(100);//蜂鸣器响100ms
			Nixie(1,KeyNum);//第1位数码管,显示键码
		}
	}
}

效果请看视频:

蜂鸣器播放提示音

以上就是蜂鸣器播放提示音的代码演示!

蜂鸣器播放音乐(天空之城)

接下来开始演示蜂鸣器播放音乐(天空之城)

开始之前,我们先接着上一篇解释一下这张表格****(我们之后都叫它表1)****

  1. 想要的频率在最左边,然后我们不能直接产生频率,我们只有周期,1除以频率就是周期。
  2. 我们需要半个周期给它翻转一次,翻转两次才是一个周期,所以翻转频率就是周期除以2,把这个频率取整就是我们的计数值(需要计这么多的数值然后就产生中断)。
  3. 那么它怎么计这么长呢?溢出才能产生中断对吧?我们就需要给它提前装好中间值(重装载值),在这个值开始计,计到65536溢出就产生中断了。
  4. 为了方便找到对应的音符,我们给这些音符弄个索引,相当于编号。

(PS:手机页面请把表格往左滑动就可看到重装载值)

表1:

|----|--------|-------------|-------------|------|-------|----|
| 音符 | 频率(Hz) | 周期(us) | 周期/2(us) | 取整 | 重装载值 | 索引 |
| 1 | 262 | 3816.793893 | 1908.396947 | 1908 | 63628 | 1 |
| 1# | 277 | 3610.108303 | 1805.054152 | 1805 | 63731 | 2 |
| 2 | 294 | 3401.360544 | 1700.680272 | 1701 | 63835 | 3 |
| 2# | 311 | 3215.434084 | 1607.717042 | 1608 | 63928 | 4 |
| 3 | 330 | 3030.30303 | 1515.151515 | 1515 | 64021 | 5 |
| 4 | 349 | 2865.329513 | 1432.664756 | 1433 | 64103 | 6 |
| 4# | 370 | 2702.702703 | 1351.351351 | 1351 | 64185 | 7 |
| 5 | 392 | 2551.020408 | 1275.510204 | 1276 | 64260 | 8 |
| 5# | 415 | 2409.638554 | 1204.819277 | 1205 | 64331 | 9 |
| 6 | 440 | 2272.727273 | 1136.363636 | 1136 | 64400 | 10 |
| 6# | 466 | 2145.922747 | 1072.961373 | 1073 | 64463 | 11 |
| 7 | 496 | 2016.129032 | 1008.064516 | 1008 | 64528 | 12 |
| 1 | 523 | 1912.045889 | 956.0229446 | 956 | 64580 | 13 |
| 1# | 554 | 1805.054152 | 902.5270758 | 903 | 64633 | 14 |
| 2 | 587 | 1703.577513 | 851.7887564 | 852 | 64684 | 15 |
| 2# | 622 | 1607.717042 | 803.8585209 | 804 | 64732 | 16 |
| 3 | 659 | 1517.450683 | 758.7253414 | 759 | 64777 | 17 |
| 4 | 698 | 1432.664756 | 716.3323782 | 716 | 64820 | 18 |
| 4# | 740 | 1351.351351 | 675.6756757 | 676 | 64860 | 19 |
| 5 | 784 | 1275.510204 | 637.755102 | 638 | 64898 | 20 |
| 5# | 831 | 1203.369434 | 601.6847172 | 602 | 64934 | 21 |
| 6 | 880 | 1136.363636 | 568.1818182 | 568 | 64968 | 22 |
| 6# | 932 | 1072.961373 | 536.4806867 | 536 | 65000 | 23 |
| 7 | 988 | 1012.145749 | 506.0728745 | 506 | 65030 | 24 |
| 1 | 1046 | 956.0229446 | 478.0114723 | 478 | 65058 | 25 |
| 1# | 1109 | 901.7132552 | 450.8566276 | 451 | 65085 | 26 |
| 2 | 1175 | 851.0638298 | 425.5319149 | 426 | 65110 | 27 |
| 2# | 1245 | 803.2128514 | 401.6064257 | 402 | 65134 | 28 |
| 3 | 1318 | 758.7253414 | 379.3626707 | 379 | 65157 | 29 |
| 4 | 1397 | 715.8196135 | 357.9098067 | 358 | 65178 | 30 |
| 4# | 1480 | 675.6756757 | 337.8378378 | 338 | 65198 | 31 |
| 5 | 1568 | 637.755102 | 318.877551 | 319 | 65217 | 32 |
| 5# | 1661 | 602.0469597 | 301.0234798 | 301 | 65235 | 33 |
| 6 | 1760 | 568.1818182 | 284.0909091 | 284 | 65252 | 34 |
| 6# | 1865 | 536.1930295 | 268.0965147 | 268 | 65268 | 35 |
| 7 | 1976 | 506.0728745 | 253.0364372 | 253 | 65283 | 36 |

接下来开始新创建一个工程:蜂鸣器播放音乐(天空之城)

把之前写好的定时器模块和Delay函数模块添加进来

接下来就对本节代码的每一个模块进行讲解,PS:讲解重点在代码的注释里。

准备工作

为了方便可以重定义端口号(如果最后整个程序写完之后蜂鸣器没有响,那么请看看自己的板子是不是普中的A2开发板,如是,则要把P1^5改成P2^5):

cpp 复制代码
//蜂鸣器端口定义
sbit Buzzer=P1^5;

乐理zhonggu,一个四分音符所用时间是500ms,重定义四分音符的时间,把500ms重命名为Speed:

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

然后我们把表1上的音符与索引重定义:

cpp 复制代码
//音符与索引对应表,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

其中0在乐谱上表示休止符,表示不弹,这里是没有声音的

然后根据表1上的频率和索引弄成一个数组:

cpp 复制代码
//索引与频率对照表
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,
};

接下来我们就把天空之城的乐谱中的每一个音符放进一个数组中

这是《天空之城》的乐谱:

这里我们定义十六分音符为1个时长(125ms),则八分音符就是2个时长,四分音符就是4个时长(500ms),二音符就是8个时长,全音符就是16个时长。时长即下面写的时值。

cpp 复制代码
//乐谱
unsigned char code Music[]={
	//音符,时值,
	
	//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,
	
	0xFF	//终止标志,防止数组越界之后乱音,用一个最大值来做一个终止标志,也可以设别的值
};

定义两个变量

cpp 复制代码
//FreSelect就是FreqTable[]中的元素的下标,
//MusicSelect就是Music[]中的元素下标

unsigned char FreqSelect,MusicSelect;

主程序

cpp 复制代码
void main()
{
	Timer0Init();//定时器初始化,1ms后溢出调到中断函数中执行中断
	
	//之后每次中断函数执行完之后返回主程序中进入while循环
	while(1)
	{
		if(Music[MusicSelect]!=0xFF)	//如果不是停止标志位
		{
			FreqSelect=Music[MusicSelect];	//选择音符对应的频率
			//选择好Music[]里的音符(索引/编号的重定义)之后赋值给FreqTable[]的下标FreqSelect,
			//然后在中断函数里就把FreqSelect这个下标对应的重装载值赋值给TL0和TH0,
			//也就是让它产生对应的频率即可发出对应的音符
			
			MusicSelect++;//Music[]的下标+1就是该音符的时长的下标
			Delay(SPEED/4*Music[MusicSelect]);	//选择音符对应的时值
			//SPEED是500ms
			//SPEED/4=一个十六分音符的时间125ms
			//125ms*Music[MusicSelect]=125ms*时值=某个音符的时长
			
			MusicSelect++;//Music[]的下标+1就是下一个音符的下标
			//为了停顿后进入下一次while循环的MusicSelect的初值
			
			TR0=0;//关闭定时器
			Delay(5);	//音符间短暂停顿
			TR0=1;//开启定时器
		}
		else	//如果是停止标志位0xFF
		{
			TR0=0;//关闭定时器
			while(1);//程序就一直停在这里了
		}
	}
}

中断函数

cpp 复制代码
//如果不是休止符,定时器初始化后第一次进来时是FreqTable[0]=0,没有出声,
//第二次进来中断函数才真正开始选(重装载值)
void Timer0_Routine() interrupt 1 //中断函数每1ms进来一次
{
	if(FreqTable[FreqSelect])	
	{
		/*取对应频率值的重装载值到定时器*/
		TL0 = FreqTable[FreqSelect]%256;		//设置定时初值
		TH0 = FreqTable[FreqSelect]/256;		//设置定时初值
		Buzzer=!Buzzer;	//翻转蜂鸣器IO口
	}
}

效果请看视频:

蜂鸣器播放音乐

蜂鸣器播放音乐部分完成!

本节的源码放在评论区了,自取!

如有问题,可评论区留言!

相关推荐
邓校长的编程课堂26 分钟前
助力信息学奥赛-VisuAlgo:提升编程与算法学习的可视化工具
学习·算法
Evand J31 分钟前
物联网智能设备:未来生活的变革者
人工智能·物联网·智能手机·智能家居·智能手表
missmisslulu41 分钟前
电容笔值得买吗?2024精选盘点推荐五大惊艳平替电容笔!
学习·ios·电脑·平板
yunhuibin1 小时前
ffmpeg面向对象——拉流协议匹配机制探索
学习·ffmpeg
hengzhepa1 小时前
ElasticSearch备考 -- Search across cluster
学习·elasticsearch·搜索引擎·全文检索·es
夜间去看海2 小时前
基于单片机的智能浇花系统
单片机·嵌入式硬件·智能浇花
蜡笔小新星2 小时前
Python Kivy库学习路线
开发语言·网络·经验分享·python·学习
VirtuousLiu2 小时前
LM74912-Q1用作电源开关
单片机·嵌入式硬件·ti·电源设计·lm74912·电源开关
打地基的小白2 小时前
软件I2C-基于江科大源码进行的原理解析和改造升级
stm32·单片机·嵌入式硬件·通信模式·i2c
攸攸太上2 小时前
JMeter学习
java·后端·学习·jmeter·微服务