资源描述
第 届
电子设计大赛论文
单片机音乐发生器
组长:
组员:
目 录
1、系统方案 - 2 -
1.1、方案比较与选择 - 2 -
1.1.1、单片机选择与论证 - 2 -
1.1.2、功放选择与论证 - 2 -
1.1.3、显示器件选择与论证 - 2 -
1.1.4、键盘形式选择与论证 - 3 -
1.1.5、材料选择与论证 - 3 -
2、理论分析与计算 - 3 -
2.1、声音播放的原理 - 3 -
2.1.1、普通声音产生原理................................. - 3 -
2.1.2、音乐产生原理......................................- 4 -
2.2、音符节拍处理 - 5 -
3、电路与程序设计 - 6 -
3.1、电路的设计 - 6 -
3.1.1、总体框图设计 - 6 -
3.1.2、时钟电路设计 - 6 -
3.1.3、复位电路设计 - 7 -
3.1.4、按键电路设计 - 7 -
3.1.5、显示电路设计 - 8 -
3.1.6、扬声器电路设计 - 9 -
3.2、程序设计...............................................- 10 -
3.2.1、系统整体流程图 - 10 -
3.2.2、键盘子程序流程图 - 10 -
3.2.3、液晶显示子程序流程图 - 11 -
4、测试方案与测试结果 - 12 -
4.1、测试方案 - 12 -
4.2、测试结果 - 12 -
4.3、测试分析 - 13 -
附录 - 13 -
附录1:电路原理图 - 13 -
附录2:部分源程序 - 14 -
1、系统方案
1.1、方案比较与选择
1.1.1、单片机选择与论证
方案一:8051单片机。8051内部包含一个8位CPU,128BRAM,4KBROM,21个特殊功能寄存器,4个8位并行I/O口,一个全双工串行口,2个16位定时器、计数器,5个中断源。
方案二:SST单片机。SST单片机省去了开发用的编程器和仿真器。省去了外部EEPROM、电压监控芯片、上电复位电路和5个UART的扩展芯片。它自带在线下载和在线仿真功能,带有5路PCA模块(主成分分析),PCA功能比普通单片机有更强的计时性。利用PCA的脉冲宽度调制(PWM)模式可产生一个8位PWM。
方案三:AT89C52单片机。AT89C52是美国ATMEL公司生产的低电压,高性能CMOS8位单片机,片内含4k字节的可反复擦写1000次的只读程序存储器(PEROM)和128字节的随机存取数据存储器(RAM),片内置通用8位中央处理器(CPU)和Flash存储单元。
基于对功能齐全性的考虑,我们采用方案三。
1.1.2、功放选择与论证
方案一:LM386。LM386是一种音频集成功放,具有自身功耗低、更新内链增益可调整、电源电压范围大和总谐波失真小等优点的功率放大器。
方案二:NPN型三极管。集电极电流受基极电流的控制,并且基极电流很小的变化,会引起集电极电流很大的变化,且变化满足一定的比例关系。
出于对市场供应情况和价格方面的考虑,我们采用方案一。
1.1.3、显示器件选择与论证
方案一:数码管。数码管显示能在低电压、小电流条件下驱动发光,能与CMOS、ITL电路兼容。发光响应时间极短(<0.1μs),高频特性好,单色性好,亮度高。体积小,重量轻,抗冲击性能好。寿命长,使用寿命在10万小时以上,甚至可达100万小时。但显示功能有限,只能显示数字和个别字母。
方案二:液晶显示器(LCD)。液晶显示器每一个点在收到信号后就一直保持那种色彩和亮度,恒定发光,显示质量高。其接口都是数字式的,和单片机系统的接口更加简单可靠,操作更加方便。功耗主要消耗在其内部的电极和驱动IC上,因而耗电量比其它显示器要少得多。
方案三:阴极射线管显示器(CRT)。此种显示器图像色彩丰富,还原性好,有丰富的几何失真调整能力。但不能长期暴露在磁场下,会磁化或损坏,不能让强光直射,会降低发光效率,不能在高温下使用,工作性能和使用寿命会大打折扣。
出于价格和电路复杂程度的考虑,在键控音符输入时采用方案一,在歌曲信息的显示时采用方案三。
1.1.4、键盘形式选择与论证
方案一:独立式按键。独立按键的输入线较多,结构复杂,一般适用于按键较少,操作速度较高的场合。
方案二:行列式键盘。行列式键盘由行和列线交叉组成,一般适用于按键较多的场合。使用4×4矩形键盘,这样键盘模块仅使用8根线与处理器相连,节省了系统资源。
由于我们功能比较少,矩形键盘占用电路板位置较大且电路复杂,因此选择方案一。
1.1.5、材料选择与论证
方案一:玻璃板。玻璃板光滑、透明,但是质量较重,易碎,并且不易加工。
方案二:塑料板。塑料板质量轻,不易碎,但是也不好加工,且不环保。
方案三:木板。木板质量比塑料板重,但比玻璃板轻,易于加工,可塑性强。
基于成本及加工复杂程度的考率,采用方案二。
2、理论分析与计算
2.1、声音播放的原理
2.1.1普通声音产生原理
人耳能听到的声音频率范围是几十到几千赫兹之间,单片机的I/O输出引脚上能输出高电平或低电平。设计一个程序,令单片机的某一个引脚按照一个的时间间隔输出一些符合规律的高低电平信号,那么就能得到一系列的矩形波。入伏哦这种时间间隔反映的频率在人耳所能听到的频率范围内,那么就能输出一定的声音信息。
图1
要输出的稳定的矩形波,可以利用延时程序来控制输出高电平或低电平的持续时间,当持续时间到时就令该信号反向,从而实现电平的转换,如图1所示。图中,从单片机引脚上输出的信号,高电平和低电平保持的时间分别为t1和t2,信号的基本输出周期为T,即频率f的倒数。在程序编写时,可令t1和t2相等。这样,当要产生某一频率的音频信号时,只要先计算得到这个周期时间的一半,然后利用延时程序来控制单片机的输出引脚在该时间内输出稳定的高电平或低电平。该时间结束时,又利用程序使单片机的输出引脚的输出信号电平发生反相。如此循环执行后,就能得到设计要求的音频信号了。
2.1.2音乐产生原理
一首乐曲是由多个音符构成的。每一个音符都对应着一个确定的频率。另外,每一个音符会根据乐曲的要求设定一个确定的节拍
音符频率的处理方法如下:
程序设计如果利用定时器计数的方式来产生延时的效果,就可以将歌曲中每一个音符所对应的频率换算成相对应的技术初值。然后,将这首音乐所有音符的计数初值编成一个表,并把每一个音符的计数初值与一个确定的数字码来联系。如表1所示为利用定时器T0工作于方式1时,一些简谱音符所对应的频率,计数初值和简谱码。
音符
频率(Hz)
简谱码(T)
音符
频率(Hz)
简谱码(T)
低1D0
262
63628
#4FA#
740
64860
#1D0#
277
63731
中5SO
748
64898
低2RE
294
63835
#5S0#
831
64934
#2RE#
311
63928
中6LA
880
64968
低3M
330
64021
#6LA#
932
64994
低4FA
349
64103
中7S1
988
65030
#4FA#
370
64185
高1DO
1046
65058
低5SO
392
64260
#DO#
1109
65085
#5S0#
415
64331
高2RE
1175
65110
低6LA
440
64400
#2RE#
1245
65134
#6LA#
466
64463
高3M
1318
65157
低7S1
494
64524
高4FA
1397
65178
中1DO
523
64580
#4FA#
1480
65198
#1D0#
554
64633
高5SO
1568
65217
中2RE
578
64684
#5SO#
1661
65235
#2RE#
622
64723
高6LA
1760
65252
中3M
659
64777
#6LA#
1865
65268
中4FA
698
64820
高7SI
1967
65283
表1
例如,要计算中音DO、中音RE、中音MI的计数初值
中音DO: TC=2∧16– 10∧6/(523*2)=65536-956=65480=0FC44H
中音RE: TC=2∧16 - 10∧6/(587*2)=65536-888=64684=0FCACH
中音MI: TC=2∧16 - 10∧6/(659*2)=65536-759=64777=0FD09H
2.2、音符节拍的处理
一首乐曲的每一个音符除了频率之外,还有不同的节拍,也就是这个音符发音的持续时间。如表2所示为节拍码与实际码之间的对照表。
节拍码
实际节拍
节拍码
实际节拍
1
1/4拍
5
1又1/4拍
2
2/4拍
6
1又1/2拍
3
3/4拍
8
2拍
4
1拍
A
2又1/2拍
表2
如果1拍为0.4s,1/4拍是0.1s,只要设定延迟时间就可求得节拍的时间。假设1/4拍为1DELAY,那么1拍应为4DELAY。如表3所示为1/4和1/8拍的时间设定。
1/4拍的时间设定
1/8拍的时间设定
曲调值
DELAY
曲调值
DELAY
调4/4
125ms
调4/4
62ms
调3/4
187ms
调3/4
94ms
调2/4
250ms
调2/4
125ms
表3
1/4拍的延迟时间=187ms
延时程序为: DELAY: MOV R7,#02
DELA1:MOV R4, #187
DELA1:MOV R3,#248
DJNZ R3, $
DJNZ R4, DELA2
DJNZ R7, DELA1
3、电路与程序设计
3.1、电路的设计
3.1.1、总体框图设计
系统总体框图如图2所示。
单
片
机
时钟与复位
键盘输入
拓展RAM
拓展ROM
音频发生
音频放大大
扬声器
LCD显示
图2
3.1.2、时钟电路设计
AT89C52内部有一个用于构成振荡器的高增益反相放大器,它的输入端为芯片引脚XTAL1,输出端引脚为XTAL2。这两个引脚跨接石英晶体和微调电容,构成一个稳定的自激振荡器,如图3所示为AT89C52的内部时钟方式的电路。
图3
图中电容C1和C2的值选择为30pF。该电容的大小会影响振荡器频率的高低、振荡器的稳定性和起振的快速性。晶体振荡频率的范围通常是在1.2~12MHz。晶体的频率越高,系统的时钟频率越高,单片机的运行速度也就越快。但同时,运行速度快对存储器的速度要求也就越高,对印制电路板的工艺要求也就越高,即要求线间的寄生电容要小。晶体和电容应尽可能安装得与单片机芯片靠近,以减小寄生电容,更好地保证振荡器稳定、可靠地工作。
3.1.3、复位电路设计
AT89C52的复位是由外部的复位电路实现的,在本次设计中,我们采用了按键电平复位电路。按键手动电平复位是通过RST端经电阻与电源Vcc接通来实现,具体电路如图4所示。
图4
3.1.4、按键电路设计
此电路实现的功能是每当有按键按下时,通过数码管显示相应数字。每个按键代表不同的音符,通过程序控制扬声器发出不同音符。如图5所示为独立式按键的具体电路。
图5
3.1.5、显示电路
3.1.5.1 LED数码管显示电路
在本次设计中我们采用的是7管LED数码管,它由7个发光二极管组成。这七个发光二极管a-g呈“日”字形排列,当某一发光二极管导通时,相应地点亮某一点或某一段笔画,通过发光二极管不停地亮暗组合形成不同的数字、字母等符号。由于采用的是共阴极接法,所以在单片机输出口和LED输入口相连时没有外接电阻。如图6所示为数码管实际连接电路。
图6
3.1.5.2 LCD液晶显示器电路
在本次设计中,我们采用的是FG12864E LCD,它是一种图形点阵液晶显示器。由于LCD的电源供电情况有别于普通的+5V电源,其驱动电源情况必须通过一个供电电路来提供,如图7所示。
图7
图8
如图8所示为LCD具体连接电路。显示数据通过单片机的P0引脚来传送;左半屏与右半屏显示的选择,引脚分别连接LCD的CS1和CS2。
3.1.6、扬声器电路
图9
如图9所示为扬声器电路实际连接图,将单片机与喇叭通过一个LM386连接,扬声器的工作原理是当两输入端有电平差时,扬声器发声。所以将喇叭的一个输出端接地,另一端与集成运放的输出相连。突出采用的连接方式,可以通过改变滑动变阻器的大小来改变接入运放的电压,并实现控制音量大小的调节。
3.2、程序的设计
3.2.1、系统整体流程图
图10
如图10所示为系统整体流程图。将歌曲的节拍和频率储存到数据表中,播放歌曲时利用查表函数为计数器赋初值,产生周期不等的方波,即可播放音乐。
3.2.3、键盘子程序流程图
如图11所示为键盘子程序流程图。
图11
3.2.2、液晶显示子程序流程图
如图12所示为液晶显示子程序流程图。
图12
4、测试方案与测试结果
4.1、测试方案
基本要求(1):能播放标准C调的Do、Re、Mi、Fa、So、La、Si、Do作为起始测试音。
基本要求(2):测试音结束后等待2秒,播放一首自选曲目,时间>30秒。
发挥部分(1):能播放指定曲目。
发挥部分(2):具有键盘,可现场编曲并播放。
发挥部分(3):具有液晶显示功能,可显示简谱。
发挥部分(4):可将简谱转换成显示五线谱。
4.2、测试结果
4.2.1、仿真测试
通过Keil和Proteus仿真,在播放Do,Re,Mi,Fa,So,La,Xi,Do等待两秒后,可以听到自选歌曲和指定曲目,LCD显示歌曲信息。按下控制键后,进入编曲模式,7个独立键盘分别代表七个音符,可现场编曲。同时,数码管显示每个按键代表的数字。如图13所示,为数码管仿真结果。
图13
4.2.2、成品测试
通过对成品进行调试实验,所得到的结果与仿真结果相同。
4.3、测试分析
由测试结果可以看出,成品满足基本要求和发挥部分的部分要求。
附录
附录1:电路原理图
附录2:部分源程序
#include<reg51.h>
#include<absacc.h>
#define uchar unsigned char
#define uint unsigned int
#define LCDPORT P0
#define E P3^1
#define RW P3^3
#define DI P3^4
#define CS1 P3^6
#define CS2 P3^5
#define BUSYSTATUS P0^7
#define LCDSTARTROW 0xc0
#define LCDPAGE 0xB8
#define LCDLINE 0x40
void Run(void);//主程序
void KeyScan(void); // 按键
void PlayKey(void); //按键程序
void delay_1ms(uchar i);//延时1ms
void PlayMusic1(void); // 播放12345671
void PlayMusic(void); //播放同一首歌
void PlayMusic2(void); //播放规定曲目1
void DisPlay(void); // 数码管
//void lcd_init(void); //lcd初始化
void init_com(void);
void sound_delay(uchar n);
void timer0(void);
uchar code hz1[];
/*一系列变量*/
sbit Beep=P2^7;
//sbit busy=P0^1;
uchar Th0,Tl0;
uchar flag=0;//模式,0表示音乐,1表示按键
uchar key=0;//按键代号
uchar a;
uchar gaodi=0;
//uchar data count=0;
//uint counter=0;
//12345671的频率
uchar code SONG_TONE[]={115,102,91,86,77,68,61,0};
//12345671的节拍
uchar code SONG_LONG[]={17,18,20,21,23,25,28,0};
/*比赛规定曲目*/
//魂斗罗 赤色要塞
uchar code SONG_TONE2[]={34,38,45,51,45,51,57,61,
57,61,68,77,68,102,91,77,
68,68,45,51,45,43,38,
68,68,45,51,45,43,57,
68,68,45,51,45,43,38,
68,68,45,51,45,43,57,0};
//魂斗罗的节拍
uchar code SONG_LONG2[]={13,12,10,9,10,9,8,28,
8,7,6,6,6,5,5,6,
35,13,10,9,19,11,85,
35,13,10,9,19,11,68,
35,13,10,9,19,11,85,
35,13,10,9,19,11,68,0};
/*同一首歌的源代码*/
uchar code Music[]={
0Xef,0Xef,
0X88,0X14,0X24,0X36,0X42,0X34,0X14,0X28,0X14,0X94,
0X1f,0X88,0X14,0X24,0X34,0X32,0X42,0X54,0X14,
0X46,0X32,0X54,0X22,0X32,0X32,0X22,0X2C,0X38,0X54,0X74,
0X76,0X62,0X68,0X54,0X52,0X62,0X74,0X62,0X52,0X3f,
0X46,0X42,0X54,0X64,0X54,0X42,0X32,0X28,0X0a4,0Xa2,
0X92,0X84,0X94,0X1f,0Xb8,0X68,0X46,0X52,0X68,
0X74,0X72,0X72,0X74,0X62,0X52,0X3f,0Xb8,0X68,
0X46,0X52,0X68,0X64,0X62,0X62,0X64,0X42,0X32,0X2f,
0X58,0X14,0X24,0X36,0X42,0X34,0X12,0X12,0X26,0X22,0X24,0X22,0X12,
0X94,0X9c,0Xa8,0Xa6,0X92,0X84,0X64,0X54,0X22,0X22,0X46,0X42,0X44,0X32,0X22,
0X5f,
0Xef,0Xef,0xFF,
}
//按键音符
uchar code KeyCode[ ]=
{0XFF,0XFF,0xF8,0x8B,0xF9,0x5B,0xFA,0x14,0xFA,0x66,0xFB,0x03,0xFB,0x8F,0xFC,0x0B,//低音
0xFC,0x43,0xFC,0xAB,0xFD,0x08,0xFD,0x33,0xFD,0x81,0xFD,0xC7,0xFE,0x05,//中音
0xFE,0x21,0xFE,0x55,0xFE,0x84,0xFE,0X99,0XFE,0xC0,0xFE,0xE3,0xFF,0x02,//高音};
//功能程序
void Run(void)
{
if(flag==0){
PlayMusic1(); //12345671
PlayMusic(); //同一首歌
PlayMusic2();//魂斗罗
}
else
PlayKey();
}
void KeyScan(void)//按按键1-7,数码管的显示
{
if(P1==0X7f)//如果P1.7=0
{
delay_1ms(5);
if(P1==0X7f)
gaodi++;
if(gaodi>2)
gaodi=0;
a=gaodi;
}
else if(P1==0XFE)
{
delay_1ms(12);
if(P1==0XFE)
key=1;
a=key;
}
else if(P1==0XFD)
{
delay_1ms(12);
if(P1==0XFD)
key=2;
a=key;
}
else if(P1==0XFB)
{
delay_1ms(12);
if(P1==0XFB)
key=3;
a=key;
}
else if(P1==0XF7)
{
delay_1ms(12);
if(P1==0XF7)
key=4;
a=key;
}
else if(P1==0XEF)
{
delay_1ms(12);
if(P1==0XEF)
key=5;
a=key;
}
else if(P1==0XDF)
{
delay_1ms(12);
if(P1==0XDF)
key=6;
a=key;
}
else if(P1==0XBF)
{
delay_1ms(12);
if(P1==0XBF)
key=7;
a=key;
}
else
return;
}
void PlayKey(void)
{
if(key==0)
return;
else
{
Th0=KeyCode[gaodi*14+key*2];
Tl0=KeyCode[gaodi*14+key*2+1];
TR0=1;
delay_1ms(187);
TR0=0;
key=0;
}
}
time0() interrupt 1 //定时器0中断
{
TH0=Th0;
TL0=Tl0;
Beep=~Beep;
}
void interrupt0() interrupt 0 //外部终端0
{
flag=~flag;
if(flag==0)
{
a=8;
P2=0;
}
}
//延时1ms
void delay_1ms(uchar i)
{
uchar j,k;
for(j=0;j<i;j++)
for(k=0;k<148;k++);
}
/*播放音乐主程序*/
//12345671
void PlayMusic1(void)
{
uint i=0;
uint j,k;
while(SONG_LONG[i]!=0||SONG_TONE[i]!=0)
{ //播放各个音符,SONG_LONG 为拍子长度
for(j=0;j<SONG_LONG[i]*30;j++)
{
Beep=~Beep;
//SONG_TONE 延时表决定了每个音符的频率
for(k=0;k<SONG_TONE[i]/3;k++);
}
delay_1ms(10);
i++;
TR0=0;
if(flag!=0)
break;
}
}
//同一首歌的实现部分
void PlayMusic(void)
{
uchar yinfu,jiepai;
uchar i,j;
for(i=0;Music[i]!=0XFF;i++)
{
yinfu=(Music[i]>>4);
jiepai=(Music[i]&0X0F);
if(yinfu==0)
continue;
Th0=MusicCode[(yinfu-1)*2];
Tl0=MusicCode[(yinfu-1)*2+1];
TR0=1;
for(j=jiepai;j>0;--j)
{
delay_1ms(150);//调歌曲快慢:187(越大越慢)
}
TR0=0;
if(flag!=0)
break;
}
}
void PlayMusic2(void)
{
uint i=0;
uint j,k;
while(SONG_LONG2[i]!=0||SONG_TONE2[i]!=0)
{ //播放各个音符,SONG_LONG 为拍子长度
for(j=0;j<SONG_LONG2[i]*30;j++)
{
Beep=~Beep;
//SONG_TONE 延时表决定了每个音符的频率
for(k=0;k<SONG_TONE2[i]/3;k++);
}
delay_1ms(10);
i++;
TR0=0;
if(flag!=0)
break;
}
}
/*数码管显示模块*/
void Display(void)//显示
{
switch(a)
{
case 0: P2= 0X3F;break;
case 1: P2= 0X06;break;
case 2: P2= 0X5B;break;
case 3: P2= 0X4F;break;
case 4: P2= 0X66;break;
case 5: P2= 0X6D;break;
case 6: P2= 0X7D;break;
case 7: P2= 0X07;break;
default: P2=0X00;break;
}
}
void delay (unsigned int n)
{ unsigned int i ;
for( ; n>0 ;n--)
for ( i=500; i>0 ; i--);
}
bit bCheckBusy ()
{ LCDPORT=0xff;
RW = 1;
DI=0;
E=1;
E=0;
return BUSYSTATUS ;
}
void vWriteData (unsigned char ucData)
{ while(bCheckBusy()) ;
LCDPORT=0xff;
RW=0;
DI=1;
LCDPORT=ucData;
E=1;
E=0;
}
void vWriteCMD(unsigned char ucCMD)
{ while ( bCheckBusy() );
LCDPORT=0xff;
RW=0;
DI=0;
LCDPORT=ucCMD;
E=1;
E=0;
}
void vLCDInitialize()
{ CS1=1;
CS2=1;
vWriteCMD (0x38) ;
vWriteCMD (0x0f) ;
vWriteCMD (0x01) ;
vWriteCMD (0x06) ;
vWriteCMD (LCDSTARTROW);
}
Void vShowCustomRow ( unsigned char ucPage ,unsigned char ucLine ,unsigned char ucWidth ,unsigned char *ucaRow)
{ unsigned char ucCount ;
if(ucLine<64)
{ CS1=1; CS2=0;
vWriteCMD(LCDPAGE+ucPage) ;
vWriteCMD(LCDLINE+ucLine) ;
if ((ucLine + ucWidth)<64)
{ for (ucCount =0 ; ucCount<ucWidth ;ucCount ++)
vWriteData (*(ucaRow+ucCount)) ;
}
{ for(ucCount=0;ucCount<64-ucline;ucCount++)
vWriteData(*(ucaRow+ucCount)) ;
CS1=0;CS2=1;
vWriteCMD(LCDPAGE+ucPage);
vWriteCMD(LCDLINE);
for (ucCount=64-ucLine;ucCount<ucWidth;ucCount++)
vWriteData(*(ucaRow+ucCount)) ;
}
}
else { CS1=0;CS2=1;
vWriteCMD(LCDPAGE+ucPage);
vWriteCMD(LCDLINE);
for(uccount=0;uccount<ucwidth;uccount++)
vwritedata(*(ucarow+uccount));
}
}
void vShowOneChin(unsigned char ucPage,unsigned char ucLine,unsigned char *ucaChinMap){
vShowCustomRo(ucPage,ucLine,16,ucaChinMap);
vShowCustomRow(ucPage+1,ucLine,16,ucaChinMap+16);
}
/*主程序模块*/
void main(void)//主程序
{
P1=0XFF;//P1口全置位为1
P2=0;//P2口全置位为0
TMOD=0X01;//设置T0为工作方式1
IT0=1;//置外部中断为边沿(下降沿)触发方式
展开阅读全文