STC89C52单片机蜂鸣器介绍以及《卡农》歌曲代码示例

目录

蜂鸣器介绍

驱动电路

三极管驱动

集成电路驱动

音乐的相关知识

音符与计时器重装载值对应表

将乐谱转换为宏定义的音调谱

实际代码演示:


蜂鸣器介绍

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

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

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

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

这里显然我们单片机上面的蜂鸣器是无源蜂鸣器,需要我们手动编写代码为其配置振荡脉冲的频率,而使其发出不同的音调。

驱动电路

三极管驱动

左图为高电平导通,右图为低电平导通

集成电路驱动

我们单片机上的ULN2003D驱动芯片的OUT1~~OUT4是用来驱动电机的,自然OUT5是用来驱动蜂鸣器的(BEEP),最后OUT6,7没有接线(之所以这样都是为了节约引脚口而考虑)

音乐的相关知识

我了解的也不多,所以可能有错的请大家见谅:

一个曲子简单的是由音调节拍决定的,音调是什么1,2,3,4,5,6,7这些数字,然后节拍是什么4分音符,8分音符。也就是音调持续的时间长短。

那么如何在蜂鸣器上模拟出各种各样的音调呢,首先是要知道不同音调有不同的频率,所以我们只要设法精确的将频率的信号输入到蜂鸣器就行

观察规律我们发现:每个音符满足12平分率

(前面一个音符的频率)*2^(1/12)=(后面一个音符的频率)

或者说(后面一个音符的频率)/{2^(1/12)}=(前面一个音符的频率)

 那么频率的公式为f=1/T,我们可以发现相邻音调之间就差那么十几赫兹,所以要求的精度还是比较高的,所以我们用定时器来计时,得到精确的频率脉冲。

之前学过波的相关知识,一个波形要有波峰和波谷才算一个完整的周期,所以我们在音符频率对应的周期内要将蜂鸣器的电压翻转2次。为了使定时器方便编码(不能说计时一半还没溢出就进行中断,来进行翻转),我们在以一个音调周期一半为一个单位进行计时并中断来翻转蜂鸣器,然后就实现了一个周期翻转2次的目的。

音符与计时器重装载值对应表

将低音L1为示例:T=1/f=1/262=0.0038167938931298,Tx1000000=3,816.793893129771

T/2=1,908.396946564885,取整1908,重装载值=65536-T/2=63628

 有了这个表以后就可以先将音符宏定义(例如高音用H开头,低音用L开头),然后创建一个数组将音符与对应的重装载值对应即可;

将乐谱转换为宏定义的音调谱

以天空之城简谱的节选为例,简单说明一下谱子里面包含的信息:

 下面我来写天空之城的第一行的音符,我以一个一分音符为时间基准,那么一节有4拍就是四个四分音符16

(将空音符定义为P,高音用H开头,低音用L开头,中央音符用M开头)

//第一小节
	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,

这样一来将乐谱建立一个数组,那么音调与节拍就交替存在了

注意:还有一点就是,那个不同音调之间要有停顿感,为了实现这一目的所以在实际操作的时候,每个音符演奏以后,要将定时器延时5~10ms再进行下一个音符的演奏

实际代码演示:

音乐《卡农》片段

主函数:

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

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

//播放速度,将一个四分音符的时长设置为600(ms),并以四分音符的时长为基准
#define SPEED	600

//音符与索引对应表,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[]={//乐谱较长加上关键字code将其储存在ROM(flash)
	//音符,时值,
	//1
	H5,2,
	H3,1,
	H4,1,
	H5,2,
	H3,1,
	H4,1,
	H5,1,
	M7,1,
	M6,1,
	M7,1,
	H1,1,
	H2,1,
	H3,1,
	H4,1,
	
	//2
	H3,2,
	H1,1,
	H2,1,
	H3,2,
	M3,1,
	M4,1,
	M5,1,
	M6,1,
	M5,1,
	M4,1,
	M5,1,
	H1,1,
	M7,1,
	H1,1,
	
	//3
	M6,2,
	H1,1,
	M7,1,
	M6,2,
	M5,1,
	M4,1,
	M5,1,
	M4,1,
	M3,1,
	M4,1,
	M5,1,
	M6,1,
	M7,1,
	H1,1,
	
	//4
	M6,2,
	H1,1,
	M7,1,
	H1,2,
	M7,1,
	H1,1,
	M7,1,
	M6,1,
	M7,1,
	H1,1,
	H2,1,
	H3,1,
	H4,1,
	H5,1,
	
	//5
	H5,2,
	H3,1,
	H4,1,
	H5,2,
	H3,1,
	H4,1,
	H5,1,
	M7,1,
	M6,1,
	M7,1,
	H1,1,
	H2,1,
	H3,1,
	H4,1,
	
	//6
	H3,2,
	H1,1,
	H2,1,
	H3,2,
	M3,1,
	M4,1,
	M5,1,
	M6,1,
	M5,1,
	M4,1,
	M5,1,
	H1,1,
	M7,1,
	H1,1,
			
	0xFF	//终止标志
};

unsigned char FreqSelect,MusicSelect;//MusicSelect为乐谱数组下标,FreqSelect音调宏定义

void main()
{
	Timer0Init();
	while(1)
	{
		if(Music[MusicSelect]!=0xFF)	//如果不是停止标志位
		{
			FreqSelect=Music[MusicSelect];	//选择音符对应的频率
			MusicSelect++;

			Delay(SPEED/4*Music[MusicSelect]);	//选择音符对应的时长
			MusicSelect++;

			TR0=0;//不同音符间短暂停顿,利用延时开关定时器实现
			Delay(5);	
			TR0=1;
		}
		else	//如果是停止标志位
		{
			TR0=0;  //关闭定时器
			while(1);
		}
	}
}

void Timer0_Routine() interrupt 1
{
	if(FreqTable[FreqSelect])	//如果是休止符(0),那么不播放声音,只进行延时
	{
		//取对应频率值的重装载值到定时器(确认音高)FreqSelect=Music[MusicSelect]
		TL0 = FreqTable[FreqSelect]%256;		//设置低位定时初值
		TH0 = FreqTable[FreqSelect]/256;		//设置高位定时初值
		Buzzer=!Buzzer;	//翻转蜂鸣器IO口(注意这里的重装值是周期的一半,故仅进行一次蜂鸣器的翻转)
	}
}

定时器:

void Timer0Init(void)
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0=1;
	EA=1;
	PT0=0;
}

延时函数Delay()

void Delay(unsigned int xms)
{
	unsigned char i, j;
	while(xms--)
	{
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
	}
}

版权声明:本文为CSDN博主「爱吃炸鸡的小猪」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_60521516/article/details/122744689

生成海报
点赞 0

爱吃炸鸡的小猪

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

IIC通信协议总结(详细说明完整过程)

IIC协议简介 IIC(inter-integrated Circuit集成电路总线)总线支持设备之间的短距离通信,用于处理器和一些外围设备之间的接口,它需要两根信号线来完成信息交换。IIC的一个特殊

imx6用文件io操作gpio

1 在用户空间配置并操作GPIO的必要性 有时,为了快速方便的测试IO口,我们可以将GPIO暴露给用户空间,直接在用户空间配置并操作GPIO,前提是没有驱动使用这个GPIO。 2 IM