南京工程学院单片机课程设计电子琴.docx
《南京工程学院单片机课程设计电子琴.docx》由会员分享,可在线阅读,更多相关《南京工程学院单片机课程设计电子琴.docx(25页珍藏版)》请在冰点文库上搜索。
南京工程学院单片机课程设计电子琴
目录1
一、设计任务(要求)2
二、总体方案2
三、原理框图4
四、系统模块详细设计与调试5
(-)键盘扫描模块5
(二)数码管显示模块5
(三)音调编码读取模块5
(四)定时器中断模块6
五、设计总结6
六、参考文献8
七、附件材料8
一、设计任务(要求)
利用51系列单片机、按键、扬声器等器件,设计一个电子琴。
具体功能要求如下:
设计键盘电路,使其具有数字键1、2、3、4、5、6、7、8、9作为电子琴按键,演示键DEMO,其中1-7号键按下后即发出相应的音调。
8号键按下后依次发出T、2、3、4、5、6、7"音调,9号键按下后依次发出“7、6、5、4、3、2、1”音调,按下DEMO键,自动演示一首歌曲,用单片机某一接口发出音频脉冲,驱动扬声器发音。
利用定时器定时,可以发出不同频率的脉冲,不同频率的脉冲经扬声器驱动电路放大滤波后,就会发出不同的音调•定时器按设置的定时参数产生中断,一次中断发出脉冲低电平,下一次反转发出脉冲高电平・由于定时参数不同,就发岀了不同频率的脉冲•按键一次,发50个脉冲•发完后继续检测键盘,如果键还按下,继续发音,发脉冲个数的多少可以决定节拍的长短。
二、总体方案
按照设计任务(要求),电子琴的主要涉及以下儿个模块:
1>定时器控制不同频率发音;
2、矩阵键盘的扫描;
3、除此以外,我还在本次设计中加入了数码管显示,分别显示当前弹
奏的音符。
于是乂多了数码管显示模块;
实现方案:
通过不断地键盘进行扫描。
当有按键按下时,执行该按键对应的程序,包括:
发出相应的音符或音只、数码管显示当前音符。
当按键松开,将音符停止发出,如果是音乐,将进行演奏。
其中,不同的音符,采用定时器中断,从而发出不同的音符。
即改变不同的定时器初始值。
对于音乐的音符,采用自编的特殊编码,以便程序的调用。
本程序的设计采用了三位数编码,即:
利用三位数表示一个音符。
笫一位由1-7组成,表示音符第二位由1・4组成,表示音阶的低、中、高和超高;第三位由1-9组成,表示该音阶的拍数,计算方式是1/2拍x数字。
例如:
6,2,2……表示一拍中音6;4,3,1……表示半拍高音4。
关于对音符编码的调用,设一个音的编码为则,调用的频率参数为bx7+c,该参数用于修改定时器初始值;调用的时间参数为c,该参数用于修改定时器执行时间。
三、原理框图
各模块初始
四、系统模块详细设计与调试
(-)键盘扫描模块
1、矩阵键盘工作原理
矩阵键盘的按键设在行、列线的交点上。
行线通过上拉电阻接到+5V电源上。
无键盘按下时,行线出于高电平状态;当有按键按下时,行线电平状态将山此按键相连接的列线的电平决定。
2、设计思路
通过扫描法识别键盘。
即,先将某一列线置为低电平,其余各列线置为高电平,检查各行线电平的变化,如果某行线电平的为低电平,就可以确立此行此列交义点处的按键被按下。
(二)数码管显示模块
1、硬件电路
数码管的显示,是通过74LS374锁存器来实现的。
段选和位选分别是P3.5和P3.4。
数据的输出为POD。
(详见附录一:
硬件原理图)
2、设计思路
通过分别控制段选、位选锁存器的导通(低电平)、锁存信号(高电平)。
从而分时给数码管写入数据并锁存。
(三)音调编码读取模块
1>编码规则
本程序的设计采用了三位数编码,即:
利用三位数表示一个音符。
笫一位曲1-7组成,表示音符1-7;第二位由1-4组成,表示音阶的低、中、高和超高;第三位由1-9组成,表示该音阶的拍数,计算方式是1/2拍x数字。
例如:
6,2,2……表示一拍中音6;4,3,1……表示半拍高音4。
2、读取方式
首先读取前两位数,以确定音调。
然后读取最后一位,以确定拍数(时间)。
然后依次递增3位。
音调确定后,通过查表获取定时器初始值,并调用定时器中断。
拍数,
即定时器中断执行的循环次数,以此来控制声音的时间。
(四)定时器中断模块
1、定时器的调用
本程序的设讣釆用定时器中断的方法来使蜂鸣器发出不同的频率声音,从而实现电子琴的音调。
定时器的初始值不同,产生的频率也就不同。
定时器的调用是循环不断的,它的循环包括两类:
一类是一个音节频率的循环,从而控制一个拍数的长短;另一类是下一个音节的到来,重新写入初值,直至结束标志位的到来,即0,0,0。
如果再调用程序中,再加入一个整体循环,则可以反复执行音乐播放。
即,检测到结束标志后,重新播放音乐而不间断。
(本程序,没有使用该循环。
)
2、初始值的生成与写入
定时器的初始值,是通过网络搜索得到的,不同的初值代表着会发出不同频率的音调。
将它制成表,以便程序读取。
五、设计总结
我课程设计的题H是电子琴的设计。
主要用的了之前所学的定时器中断、矩阵键盘扫描、数码管显示、C51编程等相关的单片机及接口技术的知识和理论。
在课设中,也充分的做到了学以致用,把之前所学的知识应用的实际当中去。
同时,也通过实践来检验之询所学的理论,发现了很多对理论知识的误解。
这些,也都在实践中得到了很好的弥补。
在设计程序的初始阶段,我本打算一次性完成任务,但侮次尝试都以失败告终。
后来,我学会了分模块处理问题,逐一突破。
这也是我在此次课程设计中的一大收获。
让我充分懂得了,如何去处理一个较大的工程项目。
首先,我先实现了使单片机发出不同音调的基本功能,然后开始考虑键盘扫描。
但是在调试时,总是出现BUG。
在部分按键按下后,蜂鸣器会一直响,无法终止。
这个问题一直困扰了我很久,尝试了很多方法都没能成功,终于在一天早上醒来后的第一次尝试,终于成功了,即在键盘扫描中加入一个程序,当音乐执行到结束标志后,此时若没有键盘被按下时,令蜂鸣器停止鸣叫不做任何响应,直至有键盘再被按下。
最后,我将两个模块进行结合,于是便成功的写出了电子琴的程序。
而且成功进行了调试。
第一天验收,老师让我再加入数码管显示功能。
于是,我乂写了数码管显示模块的程序。
数码管显示是通过锁存器来完成的。
当我程序写完之后进行调试,数码管没有进行显示,多次调试发现:
只有长时间按下按键才会有相应字符显示。
于是我尝试把段选信号的选通与锁存指令和P0数据写在一起,这样就可以立即显示当前字符了。
第二天验收,顺利通过。
事后,我乂采用了外部中断的方法进行电子琴的模式切换等其他功能,从而不断地丰富了电子琴的功能。
同时,也对程疗:
进行了整理,使程序更加规范、美观、易读。
总而言之,此次课设,我学的东西很多。
也深深地懂得理论不进行实际,是不行的。
只有通过实践才能检验自己对理论知识的理解和掌握。
在写程序的时候,一定要有良好的习惯,格式要有自己的排版方式,不能混乱。
这样易于检查和调整。
通过这次课设。
大大提高了我对单片机以及编程的能力与信心。
我相信,以后通过自己的不断深入的学习,我一定能够熟练的运用单片机到实际当中去。
六、参考文献
[1]李升,《单片机原理与接口技术》,北京:
北京大学出版社,2011年7月;
[2]网友:
lv_xiaowei,《音调与频率对应关系表》,
http:
//wenku.baidu.Com/view/d90a081a6edb6flaff001f45.html
2012年01月030;
七、附件材料
附录一:
硬件原理图
附录二:
程序清单及说明
附录三:
元器件清单
附录四:
实物图
//////I///L0VE//u////////
/一//////////////////////////-//
亠亠J.
八r%C
头文件
#inelude
#include
#defineucharunsignedchar
#defineuintunsignedint
sbitspeaker=P3人6;
sbitaO=P2AO;
sbital=P2Ai;
sbita2=P2人2;
sbita3=P2人3;
sbita4=P2人4;
sbitb2=P3人2;
sbitb4=P3人4;
sbitb5=P3人5;
unsignedchartimerOh,timerOl;
voidjian();
//
音调设置及数码管编码
*****/
//频率■•半周期数据表高八位
codeunsignedcharFREQH[]={
0xF2,0xF3,0xF5,0xF5,0xF6,0xF7,0xF8,//低音1234567
0xF9,0XF9,OxFA,OxFA,OxFB,OxFB,OxFC,//l,2,3,4,5,6,7
OxFC,OxFC,OxFD,OxFD,OxFD,OxFD,OxFE,〃高音1234567
OxFE,OXFE,OxFE,OxFE,OxFE,OxFE,OxFF};//超高音
1234567
//频率■半周期数据表低八位
codeunsignedcharFREQL[]={
0x42,OxCl,0x17,0xB6,OxDO,OxDl,0xB6,//低音1234567
0x21,OxEl,0x8C,0xD8,0x68,0xE9,Ox5B,//l,2,3,4,5,6,7
0x8F,OxEE,0x44,0x6B,0xB4,0xF4,Ox2D,//高音1234567
0x47,0x77,0xA2,0xB6,OxDA,OxFA,0x16};//超高音
1234567
//
unsignedcharconst
display[]={0x3f,0x06,0x5b,0x4f,0x66,
0x6d,Ox7d,0x07,0x7f,0x6f};
*******
处兴乐谱及其独立音调
*****
5,2,1,
3,2,1,2,2,4,
5,2,2,
1,2,1,6,1,1,5,1,4,5,1,1,6,1,1,6,1,2,5,1,1,
5,2,3,3,2,1,2,2,4,
5,2,1,
6,2,1,
5,2,1,
3,2,1,
2,2,4,
1,2,1,
2,2,1,
6,1,1,
1,2,1,
2,2,4,
2,2,1,
2,2,1,
2,2,1,
3,2,1,
5,2,2,
5,2,1,
6,2
1,
1,2,3,6,1,1,1,2
:
1,0,C
),0};
codeunsi©
inedcharshenc
l[]={
1,2,2,
2,2,2,
3,2,2,
4,2,2,
5,2,2,
6,2,2,
7,2,
2,
1,3,2,
0,0,0}
■
codeunsie
in"charjianc
l[]={
1,3,2,
7,2,2,
6,2,2,
5,2,2,
4,2,2,
3,2,2,
2,2,
2,
1,2,2,
0,0,0}
■
codeunsigned
charsongbie[:
]={
5,2,2,
3,2,1,
5,2,1,
1,3,4,
6,2,2,
1,3,2,
5,2,4,
5,2,2,
1,2,1,
2,2,1,
3,2,2,
2,2,1,
1,2,1,
2,2
4,
5,2,2,
3,2,1,
5,2,1,
1,3,3,
7,2,1,
6,2,2,
1,3,2,
5,2,4,
5,2,2,
2,2,1,
3,2,1,
4,2,3,
7,2,1,
1,2,4,
6,2,2,
1,3,2,
1,3,4,
7,2,2,
6,2,1,
7,2,1,
1,3,4,
6,2,1,
7,2,1,
1,3,1,
6,2,1,
6,2,1,
5,2,1,
3,2,
1,
1,2,1,
2,2,4,
5,2,2,
3,2,1,
5,2,1,
1,3,3,
7,2,1,
6,2,2,
1,3,2,
5,2,4,
3,2,2,
1,2,1,
2,2,1,
3,2,2,
2,2,1,
1,2,1,
2,2
4,
5,2,2,
3,2,1,
5,2,1,
1,3,3,
7,2,1,
6,2,2,
1,3,2,
5,2,4,
5,2,2,
2,2,1,
3,2,1,
4,2,3,
7,2,1,
1,2,4,
0,0,0};
//单音
//
codeunsigned
1,2,1,0,0,0};
codeunsigned
2,2,1,0,0,0};
codeunsigned
3.2.1,0,0,0
};
codeunsigned
4.2.1,0,0,0,};
codeunsigned
5.2.1,0,0,0};
codeunsigned
6.2.1,0,0,0};
codeunsigned
7.2.1,0,0,0
};
codeunsigned
1.3.1,0,0,0
chardou[]={
charre[]={
charmi[]={
charfa[]={
charso[]={
charla[]={
charxi[]={
chardoo[]={
};
***
时间参数:
延时、定时器中断
*****
voidtOint()interrupt1//TO中断程序,控制发音的音调
{
TRO=0;//先关闭TO
speaker=!
speaker;//输出方波,发音
THO=timerOh;//下次的中断时间,这个时间,控制音调高低
TLO=timerOl;
TRO=1;//启动TO
}
//
voiddelay(unsignedchart)//延时程序,控制发音的时间长度
{
unsignedchartl;
unsignedlongt2;
for(tl=0;tlfor(t2=0;t2<8000;t2++);//延时期间,可进入TO中断去发音
TRO=0;//关闭TO,停止发音
}
voidjpdelay(void)
{
unsignedchari;
for(i=300;i>0;i一);
}
*****/
voidsong(unsignedchartime)//演奏一个音符{a3=0;
THO=timerOh;〃控制音调
TLO=timerOl;
TRO=1;〃启动TO,由TO输出方波去发音delay(time);//控制时间长度
}
声频调用程序
单音节
//
//
voidyinl(void)
{
unsignedchark,i,time;
TMOD=1;
ETO=1;
EA=1;
1=0;
time=1;
while(time){
k=dou[i]+7*dou[i+1]
1;//第"i个是音符,第i+1个
是第儿个八度
timerOh=FREQH[k];//从数据表中读出频率数值
timerOl=FREQL[k];〃实际上,是定时的时间长度time=dou[i+2];//读出时间长度数值
i+=3;
song(time);//发出一个音符
}}
voidyin2(void)
{
unsignedchark,i,time;
TMOD=1;
ETO=1;
EA=1;
1=0;
time=1;
while(time){
k=re[i]+7*re[i+1]・1;//第4个是音符,第i+l个是第儿个八度
timerOh=FREQH[k];//从数据表中读出频率数值
timerOl=FREQL[k];//实际上,是定时的时间长度
time=re[i+2];//读出时间长度数值
i+=3;
song(time);//发出一个音符
}}
voidyin3(void)
{
unsignedchark,i,time;
TMOD=1;
ETO=1;
EA=1;
time=1;
while(time){
k=mi[i]+7-mi[i+1]-1;//第i个是音符,第i+l个是第儿个八度
timerOh=FREQH[k];//从数据表中读出频率数值
timerOl=FREQL[k];//实际上,是定时的时间长度
time=mi[i+2];//读出时间长度数值
i+=3;
song(time);//发出一个音符
}}
voidyin4(void)
{
unsignedchark,i,time;
TMOD=1;
ETO=1;
EA=1;
1=0;
time=1;
while(time){
k=fa[i]+7*fa[i+1]-1;//第i个是音符,第i+1个是第儿个八度
timerOh=FREQH[k];//从数据表中读出频率数值
timerOl=FREQL[k];〃实际上,是定时的时间长度
time=fa[i+2];//读出时间长度数值
i+=3;
song(time);//发出一个音符
}}
voidyin5(void)
unsignedchark,i,time;
TMOD=1;
ETO=1;
EA=1;
1=0;
time=1;
while(time){
k=so[i]+7*so[i+1]・1;//第4个是音符,第i+1个是第
儿个八度
timerOh=FREQH[k];//从数据表中读出频率数值
timerOl=FREQL[k];〃实际上,是定时的时间长度
time=so[i+2];//读出时间长度数值
i+=3;
song(time);//发出一个音符
}}
voidyin6(void)
{
unsignedchark,i,time;
TMOD=1;
ETO=1;
EA=1;
1=0;
time=1;
while(time){
k=la[i]+7*la[i+1]-1;//第i个是音符,第i+1个是第儿个八度
timerOh=FREQH[k];//从数据表中读出频率数值
timerOl=FREQL[k];//实际上,是定时的时间长度
time=la[i+2];//读出时间长度数值
i+=3;
song(time);//发出一个音符
}}
voidyin7(void)
unsignedchark,i,time;
TMOD=1;
ETO=1;
EA=1;
1=0;
time=1;
while(time){
k=xi[i]+7*xi[i+1]・1;//第4个是音符,第i+1个是第儿个八度
timerOh=FREQH[k];//从数据表中读出频率数值
timerOl=FREQL[k];〃实际上,是定时的时间长度
time=xi[i+2];//读出时间长度数值
i+=3;
song(time);//发出一个音符
}}
voidyin8(void)
{
unsignedchark,i,time;
TMOD=1;
ETO=1;
EA=1;
1=0;
time=1;
while(time){
k=doo[i]+7*doo[i+1]・:
L;〃第i个是音符,第i+1个
是第儿个八度
timerOh=FREQH[k];//从数据表中读出频率数值timerOl=FREQL[k];〃实际上,是定时的时间长度time=doo[i+2];//读出时间长度数值
i+=3;
song(time);//发出一个音符
}}
//音乐
//
voidmusicl(void)
{
unsignedchark,i,time;
TMOD=1;
ETO=1;
EA=1;
1=0;
time=1;
while(time){
k=songbie[i]+7*songbie[i+1]-1;//第i个是音符,第i+1个是第儿个八度
timerOh=FREQH[k];//从数据表中读出频率数值timerOl=FREQL[k];//实际上,是定时的时间长度time=songbie[i+2];//读出时间长度数值
1+=3;
song(time);//发出一个音符
}}
voidmusic2(void)
{
unsignedchark,i,time;
ETO=1;
EA=1;
1=0;
time=1;
while(time){
k=dfc[i]+7*dfc[i+1]・:
L;〃第i个是音符,第i+1个是第儿个八度
timerOh=FREQH[k];//从数据表中读出频率数值
timerOl=FREQL[k];//实际上,是定时的时间长度
time=dfc[i+2];//读出时间长度数值
i+=3;
song(time);〃发出一个音符
}}
voidmusic3(void)
{
unsignedchark,i,time;
TMOD=1;
ETO=1;
EA=1;
1=0;
time=1;
while(time){
k=sheng[i]+7*sheng[i+1]・1;//第i个是音符,第4+1个是第儿个八度
timerOh=FREQH[k];//从数据表中读出频率数值
timerOl=FREQL[k];//实际上,是定时的时间长度
tim