草庐IT

STC89C52单片机蜂鸣器介绍以及《孤勇者》歌曲代码示例

Xiao | 2023-04-10 原文

目录

蜂鸣器介绍

驱动电路

三极管驱动

集成电路驱动

音乐的相关知识

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

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

实际代码演示:


蜂鸣器介绍

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

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

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

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

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

驱动电路

三极管驱动

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

集成电路驱动

我们开发板上的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再进行下一个音符的演奏

主函数代码分析:

我们配置的中断函数主要是用使蜂鸣器以不同的频率翻转从而发声,所以重装载值普遍还是比较小的,可以认为时时刻刻都在执行中断服务函数。

FreqSelect=Music[MusicSelect];   
MusicSelect++;

是根据我们写的谱子的奇数位的元素来调用Music[]中的重装载值,写入到中断服务函数中,从而改变中断的时间,进而改变蜂鸣器的发音频率

Delay(SPEED/4*Music[MusicSelect]);   
MusicSelect++;

是用来模拟谱子中节拍的问题,在Delay函数进行的时候,中断函数的重装载值一直是同一个,这样音调保持不变,于是就实现了同一个音的持续发声

TR0=0;
Delay(5);    
TR0=1;

不同音符间短暂停顿,利用延时开关定时器实现。如果没有这个操作的话,声音就是那种连续的,像是一口气唱歌一样。所以短暂中断实有必要。

实际代码演示:

音乐《孤勇者》片段

主函数:

ps:SPEED的宏定义可以根据自己的喜好来定义哦,700只是一个参考数值

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

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

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

//音符与索引对应表,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 int code Music[]={//乐谱较长加上关键字code将其储存在ROM(flash)
	//音符,时值,
	//1
//	M3,8,
//	P,4,
//	P,1,
//	M1,1,
//	M2,1,
//	M1,1,
//	
//	//2
//	M3,8,
//	P,3,
//	M1,1,
//	M2,1,
//	M1,1,
//	M2,1,
//	M3,1,
//	
//	//3
//	L6,3,
//	M1,1,
//	L6,3,
//	M1,1,
//	L6,3,
//	M1,1,
//	M3,2,
//	M1,2,
//	
//	//4
//	L7,8,
//	L6,1,
//	M1,1,
//	M6,2,
//	M6,1,
//	M6,1,
//	M6,1,
//	M5,5,
//	M6,2,
//	M6,1,
//	M5,1,
//	M6,1,
//	M5,1,
//	M6,1,
//	M5,1,
//	
//	//5
//	M3,1,
//	M3,3,
//	M3,4,
//	P,4,
//	P,2,
//	L6,1,
//	M1,1,
//	
//	//6
//	M6,3,
//	M6,1,
//	M6,1,
//	M5,1,
//	M6,1,
//	M5,1,
//	M7,3,
//	M7,1,
//	M7,1,
//	M6,2,
//	M7,1,
//	
//	//7
//    M7,1,
//	M6,2,
//	M3,1,
//	M3,8,
//	P,1,
//	M3,1,
//	M5,1,
//	M3,1,
//	
//	//8
//    M2,3,
//	M3,1,
//	M2,3,
//	M3,1,
//    M2,3,
//	M3,1,
//    M5,1,
//	M3,1,
//	M5,1,
//	M3,1,

//	//9
//    M2,3,
//	M3,1,
//    M2,3,
//	M3,1,
//	M2,4,
//	P,2,
//	M1,1,
//	M2,1,
//	
//	//10
//    M3,2,
//	L6,2,
//	M1,2,
//	M3,2,
//	M2,3,
//	M3,1,
//	M2,1,
//	M1,1,
//	M1,2,
//	
//	//11
//    L6,8,
//	P,4,
//	P,2,
//	M6,1,
//	M7,1,

//---------高潮部分----------//
	//12
    H1,1,
	H2,1,
	M7,1,
	H1,1,
	H1,2,
	H1,1,
	M7,1,
	H1,1,
	H2,1,
	M7,1,
	H1,1,
	H1,2,
	H1,1,
	H2,1,
	
	//13
	H3,1,
	H2,1,
	H3,1,
	H2,1,
	H3,2,
	H3,1,
	H2,1,
	H3,2,
	H5,2,
	H3,2,
	M6,1,
	M7,1,
	
	//14
	H1,1,
	H2,1,
	M7,1,
	H1,1,
	H1,2,
	H1,1,
	M7,1,
	H1,1,
	H2,1,
	M7,1,
	H1,1,
	H1,2,
	H1,1,
	M7,1,
	
	//15
	H3,1,
	H2,1,
	H3,1,
	H2,1,
	H3,2,
	H3,1,
	H2,1,
	H3,2,
	H5,2,
	H3,2,
	H5,2,
	
	//16
	H3,3,
	H5,1,
	H3,3,
	H5,1,
	H3,1,
	H5,1,
	H6,1,
	H3,1,
	H5,2,
	H5,2,
	
	//17
	H3,3,
	H5,1,
	H3,3,
	H5,1,
	H3,1,
	H5,1,
	H6,1,
	H3,1,
	H5,2,
	H5,1,
	H5,1,
	
	//18
	H3,1,
	H2,1,
	H2,2,
	H2,2,
	H1,1,
	H3,1,
	H3,1,
	H2,1,
	H2,2,
	H2,2,
	H1,1,
	H1,1,
	
	//19
	M6,8,
	P,4,
	P,2,
	H5,1,
	H5,1,
	
	//20
	H3,1,
	H2,1,
	H2,2,
	H2,2,
	H1,1,
	H3,1,
	H3,1,
	H2,1,
	H2,2,
	H2,2,
	H1,1,
	H1,1,
	
	//21
	M6,8,
	P,4,
	P,4,
	
	
	0xFF	//终止标志
};

unsigned int 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口(注意这里的重装值是周期的一半,故仅进行一次蜂鸣器的翻转)
	}
}

定时器:

.h文件

#ifndef __TIMER0_H__
#define __TIMER0_H__

void Timer0Init(void);

#endif

.c文件 

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

延时函数Delay()

.h文件

#ifndef __DELAY_H__
#define __DELAY_H__

void Delay(unsigned int xms);

#endif

 .c文件 

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

还有就是,如果出现问题,那么请您及时检查一下自己是不是有如下问题:
1,蜂鸣器是否为无源的;

2,蜂鸣器的引脚是否为P2^5;

3,演奏音乐对时序要求高,中间不可以穿插运行别的程序,至少我反复尝试是这样的;

如果想要边放音乐,边运转一个步进电机啊什么的,建议将音乐模块单独拿出来,不如我有写过语音模块JQ8900-16P的使用方法,对于新手非常好,虽然有些大材小用,但是我个人认为还是比较方便的;

链接如下:

JQ8900-16P模块的配置与使用

啰啰嗦嗦这么多,到这里就算写完了,若有不当之处,恳请指正!
对了,还有什么想用单片机听的音乐,可以在评论区留言哦,会尽快更新的,玩的开心!!

有关STC89C52单片机蜂鸣器介绍以及《孤勇者》歌曲代码示例的更多相关文章

  1. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  2. C51单片机——实现用独立按键控制LED亮灭(调用函数篇) - 2

    说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时

  3. micropython复现经典单片机项目(二)可视化音频 频谱解析(基本搞定) - 2

    本人是音乐爱好者,从小就特别喜欢那个随着音乐跳动的方框效果,就是这个:arduino上一大把对,我忍你很久了,我就想用mpy做,全网没有,行我自己研究。果然兴趣是最好的老师,我之前有篇博客专门讲音频,有兴趣的可以回顾一下。提到可视化频谱,必然绕不开fft,大学学过这玩意,当时一心玩,老师讲的一个字都么听进去,网上教程简略扫了一下,大该就是把时域转频域的工具,我大mpy居然没有fft函数,奶奶的,先放着。音频信息如何收集?第一种傻瓜式的ADC,模拟转数字,原始粗暴,第二种,I2S库,我之前博客有讲过,数据是PCM编码。然后又去学PCM编码,一学豁然开朗,舒服,以代码为例:audio_in=I2S

  4. 51单片机——74HC595的应用(SPI实践) - 2

    目录SPI总线SPI总线概述 SPI总线分类SPI优点及缺点SPI接口硬件原理SPI四种工作模式 74HC595应用74HC595芯片概述74HC595封装及管脚功能74HC595工作原理 ​编辑 74HC595串行转并行点亮LED灯 程序实现  Proteus运行结构示意图SPI总线SPI总线概述 SPI(SerialPeripheralinterface):串行外围设备接口 用途:用来在微控制器与外围设备芯片之间实现数据交换 特点:高速、全双工、同步 SPI总线分类四线制全双工SPI(同时收发)MISO    主机输入/从机输出MOSI    主机输出/从机输入SCLK   串行时钟CS或

  5. 【毕业设计】基于单片机的智能温控农业大棚系统 - 物联网 stm32 - 2

    文章目录1简介2绪论2.1课题背景与目的3系统设计详细设计描述3.2硬件部分温度测量电路其他电路部分3.3软件部分主程序子系统程序温湿度程序流程键盘显示子程序3.4实现效果3.5部分相关代码4最后1简介Hi,大家好,这里是丹成学长,今天向大家介绍一个单片机项目基于单片机的智能温控农业大棚系统大家可用于课程设计或毕业设计单片机-嵌入式毕设选题大全及项目分享:https://blog.csdn.net/m0_71572576/article/details/1254090522绪论2.1课题背景与目的近年来我国的温室控制取得了长足的进步,首先在温室群控制方面,进行了初步的探索和理论研究,其次在温室

  6. AT24C04、AT24C08、AT24C16系列EEPROM芯片单片机读写驱动程序 - 2

    一、概述在之前的一篇博文中,记录了AT24C01、AT24C02芯片的读写驱动,先将之前的相关文章include一下:1.IIC驱动:4位数码管显示模块TM1637芯片C语言驱动程序2.AT24C01/AT24C02读写:AT24C01/AT24C02系列EEPROM芯片单片机读写驱动程序本文记录分享AT24C04、AT24C08、AT24C16芯片的单片机C语言读写驱动程序。二、芯片对比介绍型号容量bit容量byte页数字节/页器件寻址位可寻址器件数WordAddress位数/字节数备注AT24C044k5123216A2A149/1WordAddress使用P0位AT24C088k1024

  7. 基于51单片机、DS1302时钟模块的电子闹钟设计 - 2

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录一、设计原理1.DS1302介绍2.闹钟音乐播放原理二、程序设计1.DS1302.h2.ds1302.c3.music.h4.main.c三、电路图四、运行结果1.proteus仿真2.开发板实验五、总结六、附件提示:以下是本篇文章正文内容,下面案例可供参考一、设计原理1.DS1302介绍DS1302是美国DALLAS公司推出的一种高性能、低功耗、带RAM的实时时钟电路,它可以对年、月、日、周、时、分、秒进行计时,具有闰年补偿功能,工作电压为2.0V~5.5V。该芯片采用普通32.768kHz晶振,DS1302工作时功耗很

  8. [蓝桥杯单片机]学习笔记——串口通信的基本原理与应用 - 2

    目录一、原理部分1、什么是串行通信(1)并行通信与串行通信(2)串行通信的制式(3)串行通信的主要方式  2、配置串口(1)SCON和PCON:串行口1的控制寄存器(2)SBUF:串行口数据缓冲寄存器 (3)AUXR:辅助寄存器​编辑(4)ES、PS:与串行口1中断相关的寄存器(5)波特率设置  3、串口框架编写二、程序案例一、原理部分1、什么是串行通信(1)并行通信与串行通信微控制器与外部设备的数据通信,根据连线结构和传送方式的不同,可以分为两种:并行通信和串行通信。并行通信:数据的各位同时发送与接收,每个数据位使用一条导线,这种方式传输快,但是需要多条导线进行信号传输。串行通信:数据一位一

  9. 一、51单片机 使用Proteus掌握LCD1602显示屏的使用(仿真及代码) - 2

    1、单片机控制液晶显示模块1602LCD的显示。液晶显示器(LiquidCrystalDisplay,LCD)具有省电、体积小、抗干扰能力强等优点,LCD显示器分为字段型、字符型和点阵图形型。(1)字段型。以长条状组成字符显示,主要用于数字显示,也可用于显示西文字母或某些字符,广泛用于电子表、计算器、数字仪表中。(2)字符型。专门用于显示字母、数字、符号等。一个字符由5、7或5、10的点阵组成,在单片机系统中已广泛使用(3)点阵图形型。广泛用于图形显示,如笔记本电脑、彩色电视和游戏机等。它是在平板上排列的多行列的矩阵式的晶格点,点大小与多少决定了显示的清晰度。引脚包括8条数据线、3条控制线和3

  10. 微机课设 | 基于STC15单片机的简易数字密码锁设计 - 2

    在日常的生活和工作中,住宅与部门的安全防范、单位的文件档案、财务报表以及一些个人资料的保存多以加锁的办法来解决。若使用传统的机械式钥匙开锁,人们常需携带多把钥匙, 使用极不方便, 且钥匙丢失后安全性即大打折扣。在安全技术防范领域,具有防盗报警功能的电子密码锁逐渐代替了传统的机械式密码锁,电子密码锁具有安全性高、成本低、功耗低、易操作等优点。本文主要介绍运用51单片机设计数字密码锁的方法。本设计采用自上而下的数字系统设计方法,将数字密码锁系统分解为若干子系统,并且进一步细划为若干模块,然后用C语言来设计这些模块,通过KEIL软件编译,并且进行实机调试。调试结果表明:该数字密码锁能够效验4位十进制

随机推荐