任务510蜂鸣器和音乐发生器.docx

上传人:b****6 文档编号:7741756 上传时间:2023-01-26 格式:DOCX 页数:17 大小:116.61KB
下载 相关 举报
任务510蜂鸣器和音乐发生器.docx_第1页
第1页 / 共17页
任务510蜂鸣器和音乐发生器.docx_第2页
第2页 / 共17页
任务510蜂鸣器和音乐发生器.docx_第3页
第3页 / 共17页
任务510蜂鸣器和音乐发生器.docx_第4页
第4页 / 共17页
任务510蜂鸣器和音乐发生器.docx_第5页
第5页 / 共17页
点击查看更多>>
下载资源
资源描述

任务510蜂鸣器和音乐发生器.docx

《任务510蜂鸣器和音乐发生器.docx》由会员分享,可在线阅读,更多相关《任务510蜂鸣器和音乐发生器.docx(17页珍藏版)》请在冰豆网上搜索。

任务510蜂鸣器和音乐发生器.docx

任务510蜂鸣器和音乐发生器

任务5.10蜂鸣器的原理和驱动

5.9.1任务介绍

在实际应用中,经常利用利用单片机控制蜂鸣器产生各种音乐用于报警和提示,如手机的铃声、时钟的音乐报时和按键提示音等。

本节的任务是:

通过单片机I/O控制开发板上的蜂鸣器演奏音乐。

5.9.2知识准备

1、蜂鸣器的原理和分类

蜂鸣器按结构分有压电式蜂鸣器和电磁式蜂鸣器两种类型。

压电蜂鸣器内部有压电陶瓷和金属片,是利用压电陶瓷的压电效应,带动金属片的振动来发声,频率在1KHz~10KHz。

电磁式蜂鸣器内部有磁铁和线圈,振动膜片在电磁线圈和磁铁的相互作用下,周期性地振动发声,频率在0.5KHz~5KHz。

压电蜂鸣器结构简单耐用,声音大,多用于报警器等设备。

电磁蜂鸣器音色好,多用于语音、音乐等设备。

蜂鸣器按其是否带有振荡器又分为有源和无源两种类型。

有源蜂鸣器内部带有振荡器,只需要在其供电端加上额定直流电压,其内部的振荡器就可以产生固定频率的信号,驱动蜂鸣器发出声音。

无源蜂鸣器内部没有振荡器,需要在其供电端上加上高低不断变化的电信号

才可以驱动发出声音。

开发板上的蜂鸣器属于电磁式无源蜂鸣器。

2、开发板蜂鸣器驱动电路

开发板蜂鸣器驱动电路如图5.10.1所示。

 

5.10.1开发板蜂鸣器驱动电路

蜂鸣器的驱动和继电器相似,需要几十mA的电流,通常借助于三极管来作为中间功率驱动。

图5.10.1中,蜂鸣器接在PNP三极管(8550)的集电极上,单片机I/O接三极管的基极,电阻R10是三极管基极限流电阻,电阻R11是上拉电阻。

蜂鸣器内部线圈是感性器件,二极管D10并联在蜂鸣器两端,起到限制反峰电压的作用。

3、无源蜂鸣器的程序驱动

无源蜂鸣器本身不带振荡器,只有让蜂鸣器不停的处于“通电-断电”的状态,才能发出声音。

对于开发板上蜂鸣器驱动电路而言,只需要让控制蜂鸣器的I/O口不停的“置1-置0”就可以了。

下面是利用51单片机P.20驱动蜂鸣器的程序。

#incldue

#defineucharunsignedchar

#defineuintunsigendint

sbitBuzzer=P2^0;//蜂鸣器接口定义

#defineBuzzerOnBuzzer=0

#defineBuzzerOffBuzzer=1

//ms级延时函数

voidDelayMs(uintxms)

{

uinti,j;

for(i=0;i

for(j=0;j<122;j++);

}

//主函数

voidmain()

{

while

(1)

{

BuzzerOn;

DelayMs

(1);

BuzzerOff

DelayMs

(1);

}

}

 

在程序中,1ms让蜂鸣器通电,1ms让蜂鸣器断电,频率500Hz。

程序编译下载后,蜂鸣器引脚控制端接P2.0引脚,蜂鸣器发出“嗡嗡”的声音。

然后在把延时改为2ms、3ms、5ms,蜂鸣器会发出不同频响的声音。

4、蜂鸣器程序改造和按键提示音

利用延时函数来驱动蜂鸣器显然不是个好的办法,下面的程序通过定时器驱动蜂鸣器,开发板上2个独立按键,每个按键按下时,蜂鸣器响一声,持续时间为0.3秒。

2个按键对应的频率分别为500Hz和1KHz。

要求程序不堵塞CPU,效率高。

(1)工程结构和主函数

工程结构图如图5.10.2所示。

除了主函数外,还有按键模块、定时器模块和键值处理模块。

 

图5.10.2工程结构图

主函数如下:

#include

#include"MicroDefine.h"

#include"Timer.h"

#include"IndependentKey.h"

#include"KeyProcess.h"

/***************************************************************************

*函数名称:

main()

*功能:

主函数

*入口参数:

*出口参数:

*说明:

按键被按下时,蜂鸣器产生0.5S提示音。

按键A提示音频率:

500Hz

按键B提示音频率:

1KHz

***************************************************************************/

 

voidmain()

{

ucharKeyValue=0;

DelayMs(200);

TimerInit();//定时器初始化

while

(1)

{

if(FlagSystem1Ms==1)//1ms时标信号

{

FlagSystem1Ms=0;

KeyValue=KeyGetValue();//获取键值

KeySound(KeyValue);//键值处理

}

}

}

 

程序解释:

主函数内容简单,获取按键键值,并处理键值。

(2)其它功能模块

定时器模块(Timer.c,Timer.h不列出)

#include"Timer.h"

bitFlagSystem1Ms=0;//1m时标信号

ucharT1High=0;//T0高8位

ucharT1Low=0;//T1低8位

/***************************************************************************

*函数名称:

TimerInit()

*功能:

定时器初始化

*入口参数:

*出口参数:

*说明:

定时器0:

产生1ms时标信号,方式2

定时器1:

驱动蜂鸣器,方式1

***************************************************************************/

voidTimerInit()

{

TMOD=0x12;

TH0=TL0=56;

TH1=TL1=0;

TR0=1;//只开定时器0,定时器1的开启由按键决定

}

 

ET0=ET1=1;

EA=1;

TR0=1;//开定时器0,

TR1=0;//定时器1关闭,按键按下,才能开定时器1

}

/***************************************************************************

*函数名称:

Timer0Isr()

*功能:

定时器0中断服务函数

*入口参数:

*出口参数:

*说明:

***************************************************************************/

voidTimer0Isr()interrupt1

{

staticucharCnt200us=0;

if(++Cnt200us<=5)//产生1ms时标信号

{

Cnt200us=0;

FlagSystem1Ms=1;

}

}

/***************************************************************************

*函数名称:

Timer1Isr()

*功能:

定时器1中断服务函数

*入口参数:

*出口参数:

*说明:

通过改变定时器的初装值,产生不同频率的蜂鸣器驱动信号

***************************************************************************/

voidTimer1Isr()interrupt3

{

TH1=T1High;//加载初值,键值处理模块设置初值

TL1=T1Low;

Buzzer=~Buzzer;//电平反转

}

 

程序解释:

使用了2个定时器,定时器0用来产生1ms时标信号,定时器1用来驱动蜂鸣器。

在定时器初始化中,定时器1配置好后,先不开定时器1,只有按键按下后,才能开定时器1。

定时器1使用了方式1(16位),每一次溢出后,在中断服务函数中,手动重装初值,初值的大小由键值处理模块给定。

在中断服务函数中,实现蜂鸣器驱动I/O的电平反转。

键值处理模块(KeyProcess.h)

#include"KeyProcess.h"

/***************************************************************************

*函数名称:

KeySound()

*功能:

按键提示音

*入口参数:

按键值

*出口参数:

*说明:

***************************************************************************/

voidKeySound(ucharKeyValue)

{

staticucharSoundState=0;//状态机变量

staticuintCnt1Ms=0;//1ms计数器

switch(SoundState)

{

case0:

//状态0:

按键被按下,开定时器1

{

if(KeyValue==0x81)//按键A被按下,蜂鸣器响

{

T1High=0xFC;//定时器1初值,输出500Hz方波

T1Low=0x18;

TH1=0xFF;//让定时器快速完成第一次溢出,

TL1=0xFF;

TR1=1;//开定时器

SoundState=1;//跳转到状态0

}

if(KeyValue==0x82)//按键B被按下,蜂鸣器响

{

T1High=0xFE;

T1Low=0x0c;

TH1=0xFF;

TL1=0xFE;

 

SoundState=1;

TR1=1;

}

}break;

case1:

//状态1:

延时299ms,关闭蜂鸣器

{

if(++Cnt1Ms>=299)

{

Cnt1Ms=0;

TR1=0;//关闭定时器,蜂鸣器不响

SoundState=0;//跳转到状态0

}

}

}

}

 

程序解释:

KeySound()由状态机构成,分成两个状态。

状态0:

检测到按键被按下,根据按键值,给定时器赋初值,并启动定时器。

状态1:

延时299ms后,关闭定时器1,并返回到状态0。

利用状态机构成按键提示音函数,不堵塞CPU,系统效率高。

5.9.3任务实施

1、音符和频率的关系

通过以上知识的学习,初学者了解到蜂鸣器发声的原理,即通过I/O口输出脉冲信号,再将信号通过三极管放大,推动发声器件(蜂鸣器)发声。

脉冲信号的频率不同,蜂鸣器发出的声音不同。

要完成本节的音乐播放器,需弄清楚两个概念即可,也就是“音符”和“节拍”。

音符其实就是我们常说的“DoReMiFaSolLaSiDo”,每一个音符对应一定的频率。

以中音“1”为例,其频率为523Hz,周期为1秒/523=1912us,半周期为956us。

通常没有做特殊说明,我们把音乐的一个节拍的时间长度定位0.4秒,1/4节拍的时间则为0.1秒。

如果以1/4节拍为基准长度,则1拍为4个基准长度,1/2拍为2个基准长度。

定时器要产生中音“1”对应的频率(523Hz),则定时器每次溢出的时间为半个周期(即956us)。

假设中音“1”的时间长度为1/4拍,则定时器溢出的次数为0.1秒/956us=105次。

1/2拍和1拍对应的溢出次数分别105次*2和105次*4。

为了程序的方便,我们把低音音符(“5”~“7”)、中音音符(“1”~“7”)和高音音符(“

”~“

”)的频率、半周期及1/4节拍内半周期数放在表5.10.1中。

表5.10.114个音符频率对应表

数组下标

音符

频率(Hz)

周期(us)

半周期(us)

1/4节拍(0.1S)

内半周期数的个数

0

5

392

2551

1276

78

1

6

440

2273

1136

88

2

7

494

2024

1012

99

3

1

523

1912

956

105

4

2

587

1074

851

117

5

3

659

1517

758

132

6

4

698

1436

716

140

7

5

783

1277

638

157

8

6

880

1136

568

176

9

7

988

1012

506

198

10

1046

956

478

209

11

1175

851

425

235

12

1318

759

379

264

根据表5.10,我们先构建两个数组,分别存放以上音符的半周期时间和1/4拍内半周期的个数。

//13个音符的半周期长度

ucharcodeTableNoteTime[]={1276,1136,1012,956,851,758,716,638,568,506,478,425,379};

//13个音符1/4拍内半周期的个数

ucharcodeTableNoteNum[]={78,88,99,105,117,132,140,157,176,198,209,235,264};

 

图5.10.3是儿童歌曲“两支老虎”的简谱。

 

图5.10.3“两只老虎”简谱

根据简谱,我们把简谱中对应的音符和其拍数再分别放到数组中。

需要说明的是,该数组元素和简谱中音符的对应关系,例如音符“1”,在数组TableNoteTime[]中是第4个元素,所以在音乐简谱数组中应写为“3”。

拍数以1/4拍为基本单位,拍数数组中元素的“1”、“2”和“4”分别对应音符的1/4拍、1/2拍和1拍。

//简谱中对应的音符

ucharcodeTableMusicNote[]={3,4,5,3,3,4,5,3,5,6,7,5,6,7,7,8,7,6,5,3,7,8,7,6,5,3,4,0,3,4,0,3};

//简谱中音符对应的拍数

ucharcodeTalbeMusicBeat[]={2,2,2,2,2,2,2,2,2,2,4,2,2,4,1,1,1,1,2,2,1,1,1,1,2,2,2,2,4,2,2,4};

 

有了这4个数组,音乐播报就变得简单了。

根据TableMusicNote[]数组中的值,从TableNoteTime[]数组中找到待播放音符的半周期长度,将半周期长度作为定时器的溢出值,启动定时器,蜂鸣器就会产生该音符。

在播报音符的同时,根据TableMusicNote[]数组中的值,从TableNoteNum[]数组中找到该音符所对应的1/4拍内半周期的个数,然后再从TalbeMusicBeat[]数组中,找到该音符在简谱中的拍数,两者相乘,即为定时器产生该音符所对应频率的时间长度。

播报完了第一个音符,然后再播报第二个音符...直到最后一个音符,一首曲子就播放完了。

2、程序实现

(1)工程架构和主函数

程序的工程架构如图5.10.4所示。

除了主函数模块,至于键值获取模块和定时器模块。

音乐播放放在定时器模块中完成。

 

图5.10。

4工程结构图

主函数如下:

#include

#include"MicroDefine.h"

#include"Timer.h"

#include"IndependentKey.h"

/***************************************************************************

*函数名称:

main()

*功能:

主函数

*入口参数:

*出口参数:

*说明:

***************************************************************************/

voidmain()

{

ucharKeyValue=0;

ucharCnt1Ms=0;

DelayMs(200);

TimerInit();//定时器初始化

while

(1)

{

if(FlagSystem1Ms==1)//1ms时标信号到

{

FlagSystem1Ms=0;

if(++Cnt1Ms>=10)//10ms时标信号到

{

Cnt1Ms=0;

KeyValue=KeyGetValue();//获取按键值

if(KeyValue==0x81)

{

if(FlagMusicEnd==0)//上一次播放完毕,则启动新的播放

{

TR1=1;//启动定时器

FlagMusicEnd=1;//音乐播放中

}

}

}

}

}

}

}

}

 

程序解释:

在主程序中检测按键按下,则启动定时器1,并把音乐结束标志位置1。

(2)其它功能模块

键值处理模块(省略)

定时器模块

Timer.h:

#ifndef_TIMER_H_

#define_TIMER_H_

#include

#include"MicroDefine.h"

sbitBuzzer=P2^0;

#defineMusicPlaySpeed20//音乐播放速度宏定义

//变量声明

externbitFlagSystem1Ms;//1m时标信号

externbitFlagMusicEnd;//音乐播放结束标志位

//定时器声明

externvoidTimerInit();//定时器初始化

#endif

 

Timer.c:

#include"Timer.h"

bitFlagSystem1Ms=0;//1m时标信号

bitFlagMusicEnd=0;//音乐播放结束标志位

//13个音符的半周期长度

uintcodeTableNoteTime[]={1276,1136,1012,956,851,758,\

716,638,568,506,478,425,379};

//13个音符对应的1/4拍内半周期的个数

uintcodeTableNoteNum[]={78,88,99,105,117,132,140,157,\

176,198,209,235,264};

//简谱中对应的音符

ucharcodeTableMusicNote[]={3,4,5,3,3,4,5,3,5,6,7,5,6,7,7,8,\

7,6,5,3,7,8,7,6,5,3,4,0,3,4,0,3};

//简谱中音符对应的拍数

ucharcodeTalbeMusicBeat[]={2,2,2,2,2,2,2,2,2,2,4,2,2,4,1,1,\

1,1,2,2,1,1,1,1,2,2,2,2,4,2,2,4};

 

/***************************************************************************

*函数名称:

TimerInit()

*功能:

定时器初始化

*入口参数:

*出口参数:

*说明:

定时器0:

产生1ms时标信号,方式2

定时器1:

驱动蜂鸣器,方式1

***************************************************************************/

voidTimerInit()

{

TMOD=0x12;

TH0=TL0=56;

TH1=TL1=0xFF;

ET0=ET1=1;

EA=1;

TR0=1;//只开定时器0,定时器1的开启由按键决定

}

/***************************************************************************

*函数名称:

Timer0Isr()

*功能:

定时器0中断服务函数

*入口参数:

*出口参数:

*说明:

***************************************************************************/

voidTimer0Isr()interrupt1

{

staticucharCnt200us=0;

if(++Cnt200us>=5)//产生1ms时标信号

{

Cnt200us=0;

FlagSystem1Ms=1;

}

}

/***************************************************************************

*函数名称:

Timer1Isr()

*功能:

定时器1中断服务函数

*入口参数:

*出口参数:

 

*说明:

通过改变定时器的初装值,产生不同频率的蜂鸣器驱动信号

MusicPlaySpeed是头文件中宏定义,决定音乐的播放速度,其值不能小于10

***************************************************************************/

voidTimer1Isr()interrupt3

{

staticucharNoteCnt=0;//播放音符计数器

staticuintHalfPeriodCnt=0;//半周期计数器

uintTimer1Temp=0;//定时器长度暂存值

//根据当前播放音符,计算出对应的定时器初值

Timer1Temp=65536-TableNoteTime[TableMusicNote[NoteCnt]];

TH1=Timer1Temp/256;

TL1=Timer1Temp%256;

Buzzer=!

Buzzer;

//播放一个音符的时间到

if(++HalfPeriodCnt>=(TableNoteNum[TableMusicNote[NoteCnt]]\

*TalbeMusicBeat[NoteCnt])*MusicPlaySpeed/10)

{

HalfPeriodCnt=0;

//简谱中的音符全部播放完

if(++NoteCnt>=32)

{

NoteCnt=0;

TR1=0;//定时器停止

FlagMusicEnd=0;//音乐播放结束结束标志位清零

}

}

}

 

程序解释:

程序中使用了两个定时器,和之前按键音一样,一个用来产生系统时标信号,另外一个用于用于驱动蜂鸣器。

按键启动定时器1,定时器1溢出后,从播报的第一音符起,取出该音符半周期时间长度,计算出定时器1的初值,让定时器1输出该音符对应的脉冲波。

定时器1每溢出1次,半周期计数器(HalfPeriodcnt)加1,音符播放的时间长度由半周期计数器的值来决定。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工程科技 > 交通运输

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1