基于单片机简易电子琴设计Word文件下载.docx
《基于单片机简易电子琴设计Word文件下载.docx》由会员分享,可在线阅读,更多相关《基于单片机简易电子琴设计Word文件下载.docx(39页珍藏版)》请在冰豆网上搜索。
1.2设计意义
①可以了解音乐的基本知识;
②加深对单片机的使用;
③学会自己做项目;
1.3设计任务
实现电子琴发声控制系统;
要求电路实现如下功能:
利用现成电脑音响作为发声部件,21个音符键,实现高音、中音、低音的1、2、3、4、5、6、7的发音。
并在存储几首歌曲的内容,可以实现自动播放。
2.系统总体方案及硬件设计
2.1总体设计
音乐是有由不同的音阶组成的,而不同的音阶又是由不同的频率发出的,那么产生不同的频率,就可以发出不同的音乐了。
而利用单片机就可以产生不同的频率的方波,因此选择单片机为为主来设计。
通过程序编写实现单片机输出不同的频率,输出的方波信号再通过接口给电脑音响,让其发声。
同时电子琴加入led用来显示。
本设计的主要工作是程序编写,通过程序让电子琴实现音乐演奏,歌曲播放以及记录已按下的音符,并播放,最后实现led显示。
而硬件主要有单片机最小
系统,键盘模块,发声模块,还有一个电源模块。
总体框图2
2.2硬件设计
电路图1
注:
本系统有主控单片机、键盘、led显示模块、发声模块以及电源组成。
2.2.2单片机的最小工作系统:
这里用的单片机的型号是STC90C516RD+,配以12M的晶振,以及复位电路供电电路构成最小系统。
2.2.3电源设计:
这里电源直接用直流5v电源;
2.2.4按键设计:
按键采用4*6扫描;
4根行线接P10-P13,六根列线接P14-P17以及P20,P21口共24个按键,0-20代表音符键,0-6代表低音1,2,3,4,5,6,7;
7-13代表中音1,2,3,4,5,6,7;
14-20代表高音1,2,3,4,5,6,7;
21号按键表示播放歌曲键,当按下21号键,进入播放歌曲函数,当按下22号键时,播放下一首歌曲,当按下23键时,退出播放返回主程序。
而在主程序中时,代表演奏状态,当按下23号键时,进入录音状态,此时有个绿色的指示灯会亮。
而进入录音后,再按一次23键,指示灯灭,退出录音状态,返回主程序。
2.2.5LED灯设计:
七个红色的LED代表按键的音符DO,RE,MI...分别接到P0口的各个I端口
音符DO时,一个LED亮,音符MI时,2个LED亮。
。
同时为了区分高中低音,三个八度LED显示不同,当低音音符播放时,LED闪亮,当是中音时,LED也闪亮,但是闪亮的频率更快,当是高音时,LED全亮,这是通过调节LED亮灭时间来实现,也可以说是PWM吧。
还有一个绿色LED指示当前状态,当电子琴处于录音时,LED亮,否则,灭;
2.2.6发声模块:
这里没有自己做功放,而是使用电脑的音响。
3系统软件的的编写
3.1电子琴基本原理
首先的弄清楚电子琴的基本原理:
声音的频谱范围约在几十到几千赫兹,若能利用程序来控制单片机的某个口线不断输出“高”“低”电平,则在该口线上就能产生一定频率的方波,讲该方波接上喇叭就能发出一定频率的声音,若再利用程序控制“高”“低”电平的持续时间,就能改变输出波形的频率从而改变音调。
乐曲中,每个音符对应着确定的频率,下表给出各音符频率。
如果单片机某个口线输出“高”“低”电平的频率和某个音符的频率一样,那么将此口线接上喇叭就可以发出此音符的声音。
根据这个原理就能设计出,对于单片机来说要产生一定频率的方波大致是先将某口线输出高电平然后延迟一段时间再输出低电平,如此循环的输出就会产生一定频率的方波,通过改变延迟的时间就可以改变输出方波的频率。
单片机内部有两个定时计数器T1和T0,单片机的定时计数器实际上是个计数装置,它既可以对单片机内部晶振驱动时钟计数,也可以对外部输入的脉冲计数,对内部晶振计数时称为定时器,对外部时钟计数时称为计数器。
当对单片机内部晶振驱动时钟计数时,每个机器周期定时计数器的计数值就加,当计数值达到计数最大值时计数完毕并通知单片机。
音乐中各个音符的频率表如下:
音符频率表3
弄懂后开始程序的实现
3.2主程序模块
N
Y
KeyNo==21KeyNo<
21KeyNo==23
Y
Y
主程序框图
参数计算
发音原理
若要产生音频脉冲,只要算出某一音频的周期(1/频率),再将此周期除以2,即为半周期的时间。
利用定时器计时半周期时间,每当计时终止后就将P1.0反相,然后重复计时再反相。
就可在P1.0引脚上得到此频率的脉冲。
利用AT89C51的内部定时器使其工作计数器模式(MODE1)下,改变计数值TH0及TL0以产生不同频率的方法产生不同音阶。
计算举例
例如,频率为523Hz,其周期T=1/523=1912μs,因此只要令计数器计时956μs/1μs=956,每计数956次时将I/O反相,就可得到中音DO(523Hz)。
计数脉冲值与频率的关系式是:
N=fi÷
2÷
fr,式中,N是计数值;
fi是机器频率(晶体振荡器为12MHz时,其频率为1MHz);
fr是想要产生的频率。
其计数初值T的求法如下:
T=65536-N=65536-fi÷
fr
例如:
设K=65536,fi=1MHz,求中音DO(261Hz)。
fr=65536-1000000÷
fr=65536-500000/fr,中音DO的T=65536-500000/523=64580。
3.2.3计算结果
(1)单片机12MHZ晶振,中音符与计数T0相关的计数值如表所示:
音符
频率(HZ)
计数值(T值)
低1DO
262
63628
#4FA#
740
64860
#1DO#
277
63737
中5SO
784
64898
低2RE
294
63835
#5SO#
831
94934
#2RE#
311
63928
中6LA
880
64968
低3MI
330
64021
#6LA#
932
64994
低4FA
349
64103
中7SI
968
65030
370
64185
1046
65058
低SO
392
64260
1109
65085
415
64331
高2RE
1175
65110
低6LA
440
64400
1245
65134
466
64463
高3MI
1318
65157
低7SI
494
64524
高4FA
1397
65178
中1DO
523
64580
1490
65198
554
64633
高5SO
1568
65217
中2RE
587
1661
65235
622
64884
高6LA
1760
65252
中3MI
659
64732
1865
65268
中4FA
698
64820
高7SI
1967
65283
采用查表程序进行查表时,可以为这个音符建立一个表格,有助于单片机通过查表的方式来获得相应的数据:
低音0-19之间,中音在20-39之间,高音在40-59之间。
用单片机播放音乐,或者弹奏电子琴,实际上是按照特定的频率,输出一连串的方波。
为了输出合适的方波,首先应该知道音符与频率的关系。
(2)音调数据表
曲调值
DELAY
调4/4
125ms
62ms
调3/4
187ms
94ms
调2/4
250ms
上表中的频率数值,有些过多,去掉不常用的黑键频率,只是把白键对应的数据存放在单片机中,即可满足绝大部分的应用需求。
定义音调数据表的程序如下:
DW63628,63835,64021,64103,64260,64400,64524;
64580,低音区:
1234567
DW64580,64671,64777,64820,64898,64968,6503065058中音区:
DW65058,65110,65157,65178,65217,65252,6528365312高音区:
把这个数据表,放在程序中,需要播音的时候,就从表中取出一个数据送到定时器,当定时器溢出中断的时候,再对输出引脚取反,那么,在扬声器中,即可听到上表中频率的声音。
3.2主程序中涉及的函数讲解:
voidYinFu_DiZhongGao_Flag(ucharkeyval)
{
if(keyval<
7)
flag_yinfu_dizhonggao=1;
//低音
elseif(keyval<
14)
flag_yinfu_dizhonggao=2;
//中音
elseif(keyval<
21)
flag_yinfu_dizhonggao=3;
//高音
}
此函数是根据按下键值来确定音符是高音、中音还是低音flag_yinfu_dizhonggao=1代表低音;
lag_yinfu_dizhonggao=2;
代表中音。
flag_yinfu_dizhonggao=3;
代表高音。
而LED显示函数(定时器1中断服务函数)会根据lag_yinfu_dizhonggao的值来显示
voidTimer_Init()
TMOD=0x11;
//定时器T0,T1设置为方式1,
TH1=0xD8;
//10ms中断一次
TL1=0xF0;
TH0=th0;
TL0=tl0;
IE=0x82;
//开启总中断和定时器T0,T1溢出中断
ET1=1;
IP=0x02;
//T0优先于T1
定时器0用来产生不同方波频率,使音响发声
定时器1用来控制LED显示,当工作在演奏模式时,按下音符键时,判断音符键是高音还是中音或者低音,当是高音时,LED全亮,占空比为1,当是中音时,LED快速闪烁,占空比为1/2;
当是低音时,LED相对较慢的闪烁,占空比为1/4;
以下是中断服务程序:
voidplay_Tone()interrupt1using0//定时器T0溢出中断,产生不同频率的矩形波
Beep=~Beep;
voidplay_LED()interrupt3using2
if(Recordonoff)//开启录音后计时按键按下的时间
{
TH1=0xB1;
TL1=0xE0;
Record_Timer++;
}
if(flag_yinfu_dizhonggao==1||flag_yinfu_dizhonggao==3)//低音或高音
{
if((cont==3&
&
flag_yinfu_dizhonggao==1)||flag_yinfu_dizhonggao==3)//低音占空比为1/4、高音占空比为1
{
led=LED_Dis[yuzhi];
}
elseled=0x00;
if(flag_yinfu_dizhonggao==2)
if(cont1)//控制占空比为1/2
{cont1=~cont1;
led=LED_Dis[yuzhi];
else
{led=0x00;
cont1=~cont1;
}
cont++;
if(cont>
3)cont=0;
}
中断函数中的余值在主程序中是这样赋值的:
yuzhi=(KeyNo+3)%7;
//判断DOREMIFA...
根据yuzhi判断DO、RE。
当时DO时,亮一个LED灯,当时RE时,亮两个LED...
//21个音符的频率对应的T值
Uintcode
Tone_Delay_Table[]={63628,63835,64021,64103,64260,64400,64524,64580,64684,64777,64820,64898,64968,65030,65058,65110,65157,65178,65217,65252,65283}
voidmain()
ucharKeystate=0;
Timer_Init();
led=0x00;
//初始化led
led_zhishi=0;
指示LED灭(进入演奏状态,若进入录音状态则亮)
while
(1)
Keystate=KeyState();
//如果有按键按下,返回1
if(Keystate)
{
key_scan();
if(KeyNo<
21)//KeyNo存储按下键的键值
{
yuzhi=(KeyNo+3)%7;
th0=Tone_Delay_Table[KeyNo]/256;
//赋初值,产生
//按键所对应的音符的频率
tl0=Tone_Delay_Table[KeyNo]%256;
YinFu_DiZhongGao_Flag(KeyNo);
//判断是高中低音
TR0=1;
//Turnontimer
TR1=1;
elseif(KeyNo==21)//进入播放歌曲状态
{PlayMusic();
Delayms(2000);
elseif(KeyNo==23)//进入录音状态
{Recordonoff=~Recordonoff;
Record_yinfu();
}
}
else
TR0=0;
//关闭定时器
TR1=0;
led=0x00;
cont=0;
//控制占空比变量,让LED以不同频率闪烁
Delayms
(2);
3.3播放歌曲模块
KeyNo==22KeyNo==23
播放歌曲函数程序图4
总共有三首歌可供播放,三首歌循环播放,当按下退出键23,返回主程序,进入演奏状态。
函数实现如下:
//音乐的频率所对应的T值
//包含了低音SO,LA,SI,中音DO,RE,MI,FA,SO,RE,SI,高音DO,LA,MI
ucharcodeMusicYinFu[]={0XFc,0X44,0XFc,0Xac,0XFd,0X09,0XFd,0X34,0XFD,0X82,0XFD,0Xc8,0XFD,0X06,0XFb,0X04,0Xfb,0X90,
0Xfc,0X0c,0Xfe,0X22,0Xf9,0X5b,0Xfa,0x15};
//月亮代表我的心
//这里的数据的高4位代表音符对应的序列号,低4位代表节拍数
//例如0x82,表示第八个音符,延时两个节拍
ucharcodeMusic_One[]={0x02,0x82,0x16,0x32,0x54,0x02,0x52,0xA6,0x32,0x54,0x02,0x52,0x64,0x74,0xB6,0x64,0x52,0x5C,0x32,0x22,0x16,0x12,0x14,0x32,0x22,0x26,0x12,0x94,0x22,0x32,0x2C,0x32,0x52,0x36,0x22,0x14,0x54,0xAC,0x92,0xA2,0x96,0xA2,0x96,0x82,0x3C,0x54,0x36,0x22,0x14,0x54,0xAC,0x92,0xA2,0x16,0x12,0x14,0x22,0x32,0x2C,0x02,0x82,0x16,0x32,0x56,0x12,0xA6,0x32,0x56,0x52,0x66,0x72,0xB6,0x62,0x1C,0xff};
voidPlayMusic(void)
{
ucharyinfu,jiepai,yinfu_led;
uchari,j;
//循环变量
uchar*p1=Music_One,*p2=MusicYinFu;
//p1开始指向第一首歌
//p2去歌曲的音符和节拍
for(i=0;
*(p1+i)!
=0XFF;
i++)
yinfu=((*(p1+i))>
>
4);
//取出音符对应的序号,如
//Music_One[2]=0x82,音符代表MusicYinFu[8]
jiepai=((*(p1+i))&
0X0F);
//取出节拍数
yinfu_led=yinfu%7;
//音符对应LED显示号
if(yinfu!
=0){
th0=*(p2+(yinfu-1)*2);
tl0=*(p2+(yinfu-1)*2+1);
TR0=1;
ET0=1;
else//如Music_One[1]=0x02,这里的音符为0,表停顿
Beep=1;
for(j=jiepai;
j>
0;
--j)
led=LED_Dis[yinfu_led-1];
Delayms(180);
//一拍延时时间
key_scan();
if(KeyNo==23)//退出播放
{ET0=1;
break;
elseif(KeyNo==22)//下一曲
{
if(p1==Music_One)若正在播放第一首,那么下一曲就是第二首
{ET0=0;
p1=Music_Two;
elseif(p1==Music_Two)//若是第二首,下一首是第三首
{ET0=0;
p1=Music_Three;
else
p1=Music_One;
}//同理
i=0;
KeyNo=26;
//一定要改变KeyNo的值,否则只播放一个音符
Delayms(1000);
//歌曲切换,停顿一下
break;
//跳出之前播放的歌曲,以使播放下一曲
}
}
if(*(p1+i+1)==0XFF)//判断歌曲是否播放完毕
ET0=0;
Beep=0;
//暂停一下
if(p1==Music_One)//如果刚刚结束第一首歌,播放第二首歌
i=0;
p1=Music_Two;
Delayms(2000);
elseif(p1==Music_Two)//如果刚刚结束第二首歌,播放第三首歌
p1=Music_Three;
else//如果刚刚结束第三首歌,播放第一首歌
p1=Music_One;
Delayms(3000);
TR0=0;
ET0=0;
if(KeyNo==23)
break;
}
ET0=1;
3.4录音函数实现:
N
Y
KeyNo<
21KeyNo<
程序框图:
5
程序实现:
voidRecord_yinfu()
ucharkeystate,i=0;
ucharhistory[100]={0};
//记录按下的键