北京交大单片机课程设计电子音调发生器实验报告.docx
《北京交大单片机课程设计电子音调发生器实验报告.docx》由会员分享,可在线阅读,更多相关《北京交大单片机课程设计电子音调发生器实验报告.docx(16页珍藏版)》请在冰豆网上搜索。
北京交大单片机课程设计电子音调发生器实验报告
实
验
报
告
指导老师:
付文秀
实验课程:
单片机实验
班级:
通信0607班
姓名:
电子音调发生器
一、实验目的
1.了解计算机发声原理。
2.熟悉定时器和键盘扫描电路的工作原理及编程方法。
二、设计任务及要求
利用实验平台上的开关k0-k7和蜂鸣器设计电子音调发生器,要求:
1.利用实验平台上开关k0-k7进行音调选择,即拨动不同的开关产生不同的音调,依次拨动K0~K7,蜂鸣器发出1234567i八个音调。
2.编写2支歌曲,并可进行选择播放。
三、工作原理及设计思路
音节由不同频率的方波产生,音节与频率的关系如表1所示。
要产生音频方波,只要算出某一音频的周期(1/频率),然后将此周期除以2,即为半周期的时间。
利用计时器计时此半周期时间,每当计时到后就将输出方波的I/0反相,然后重复计时此半周期时间再对I/O反相,就可在I/O脚得到此频率的方波。
在ZKS-03实验仪上,产生方波的I/O脚选用P1.7,通过跳线选择器JP1将单片机的P1.7与蜂鸣器的驱动电路相连。
这样P1.7输出不同频率的方波,蜂鸣器便会发出不同的声音。
另外,音乐的节拍是由延时实现的,如果1拍为0.4秒,1/4拍是0.1秒。
只要设定延时时间,就可求得节拍的时间。
延时作为基本延时时间,节拍值只能是它的整数倍,
每个音节相应的定时器初值X可按下法计算:
(1/2)*(1/f)=(12/fose)*(216-x)
即x=216-(fose/24f)
其中f:
音调频率,当晶振fose=11.0592MHz时,音节“1”相应的定时器初值为x,依次可以求得。
音调
频率(Hz)
X(HEX)
1
220
EFA9
1#
233
F093
2
247
F173
2#
262
F249
3
277
F307
4
294
F3C8
4#
311
F473
5
330
F51E
5#
349
F5B6
6
370
F64C
6#
392
F6D7
7
415
F75A
1
440
F7D8
1#
466
F84D
2
494
F8BD
2#
523
F924
3
554
F987
4
587
F9E4
4#
622
FA3D
5
659
FA90
5#
698
FADE
6
740
FB29
6#
784
FB6F
7
831
FBB1
`1
880
FBEF
`1#
932
FC2A
`2
988
FC62
`2#
1046
FC95
`3
1109
FCC7
`4
1175
FCF6
`4#
1244
FD22
`5
1318
FD4B
`5#
1397
FD73
`6
1480
FD98
`6#
1568
FDBB
`7
1661
FDDC
硬件连线电路图
系统功能说明:
可以通过开关K0闭合依次令蜂鸣器发出相应的1234567i这8个音调;可以通过同时按下K1或K2选择播放两首歌曲,并通过闭合K3选择暂停,打开K3继续播放,K4结束一首歌,再次按下K1或K2将重新播放歌曲。
(由于最初是在开发板上进行实验的,开发板上一共只有四个开关,所以我就设置成了一个开关控制播放8个音阶)
1、总体设计
系统总体设计框架
五键键盘控制P3.2、P3.3、P3.4、P3.1、P3.6状态
系统功能模块图
2、硬件电路设计
电路设计思路
在ZKS-03单片机实验仪中,通过5个开关用导线分别与P1和P3口5位直接相连。
设计原理
利用51单片机的P1和P3口读取开关键盘状态,CPU不断查询P1和P3口状态,并以P1和P3口不同状态跳转到不同的程序段中执行,具体执行为1.定时器预置定时初值,工作在工作方式1,发出不同频率的方波;2.播放歌曲,程序段中存储了两个表,分别是音符表和节拍表。
音符表中存放的是蜂鸣器发出音调的对应的频率所需的定时器的频率定时初值的高低位;节拍表存放的是发出每一个音调所需要持续的时间的双层循环中的内循环次数值。
通过查表法在一次循环中通过累加器A连续取两个数据,分别送到定时器的定时初值寄存器TL0,TH0中,接着从节拍表中将一个对应节拍取出放入内循环的计数器(R2)中,并控制定时器发出对应方波并循环对应次数达到合适的节拍效果。
程序流程图
将K0-K4开关状态送P3.2、P3.3、P3.4、P3.1、P3.6
查询键盘按键状态
跳转相应子程序
令P3.5口发出相应频率的方波
返回键盘查询
数据分析
一拍的时长大约为400ms,则当以四分音符为节拍时,四分音符的时长就为400ms,八分音符的时长就为200ms,十六分音符的时长就为100ms。
在单片机上控制一个音符唱多长可采用循环延时的方法来实现。
首先,确定一个基本时长的延时程序,若以十六分音符的时长为基本延时时间,那么对于一个音符,如果它为十六分音符,则只需调用一次延时程序,如果它为八分音符,则只需调用二次延时程序,如果它为四分音符,则只需调用四次延时程序,依次类推。
由于用的是C语言。
仅仅需要通过程序中的定义,“,”为低音,“`”为高音,“_”是1/2拍,“—”是2拍,“.”是一拍半,空格就是空拍。
按照这个简单的转换规则,就可以轻松的将普通的简谱转换为程序中需要的程序语句进行播放。
3、实验过程中遇到问题
设计问题
其次是歌曲播放的问题,起初不清楚歌曲应该如何播放,如何存储,但是仔细复习了教材的汇编程序设计有关章节,我掌握了查表法。
C语言在编写乐曲方面比汇编语言要简单很多。
硬件问题
熟悉了试验箱后,应该先测试开关的好坏,我在实验过程中曾遇到开关坏掉的情况。
还要注意在下载完程序后,将下载开关拨到RUN的位置后还要按一下复位键,这样才能产生现象。
并且还要注意P1口的跳线状态不能错。
软件问题
软件方面是我在实验过程中遇到的最多的问题。
在制作音符高低位初值表时,起初我将音符对应的频率定时初值高低位作为一个数,即多个16位的数存放在表中,但是发现16位的寄存器只有DPTR,这是不够用的。
于是改成了将每个初值拆分成高8位和低8位的两个8位数存在表里,每个循环取两次,这样就解决了这个问题。
K3起初在按下去后时而会发出“嘀嘀嘀”的声音,后来发现是因为程序中忘记设TRO=0,所以总在判断按键处循环,才导致这样的情况。
为了使开关更好的工作,在程序中进行判断,我还在每个开关判断都增加了防抖动。
K3、K4刚开始还出现了相互干扰的现象,经过调试程序,分清楚了K4、K5不同的功能和它们的优先级,逻辑更加明确。
4、学习体会与收获:
在这次课程设计中,我学会了独立设计一个完整的、具有一定综合性的汇编程序的编写和硬件实现的基本方法。
更深刻的理解了51单片机的设计思路和概念。
掌握了定时器输出方波,查表法查找数据,中断的应用和多分支语句的应用和编写。
更重要的是学会了不断学习的态度,和以前不扎实的调试状态下的操作方式。
遇到问题,不再是毫无头绪无从下手,而是由了清楚的调试思路,一步一步的运行来检查,提高了我的实验效率。
最后感谢老师的指导。
5、程序清单:
#defineucharunsignedchar//定义一下方便使用
#defineuintunsignedint
#defineulongunsignedlong
#include//包括一个52标准内核的头文件
charcodedx516[3]_at_0x003b;//这是为了仿真设置的
sbitBEEP=P1^7;//喇叭输出脚
sbitP10=P1^0;
sbitK1=P3^2;
sbitK2=P3^3;
sbitK3=P3^4;
sbitK4=P3^1;
sbitK5=P3^6;
ucharth0_f;//在中断中装载的T0的值高8位
uchartl0_f;//在中断中装载的T0的值低8位
//T0的值,及输出频率对照表
ucharcodefreq[36*2]={
0xA9,0xEF,//00220HZ,1//0
0x93,0xF0,//00233HZ,1#
0x73,0xF1,//00247HZ,2
0x49,0xF2,//00262HZ,2#
0x07,0xF3,//00277HZ,3
0xC8,0xF3,//00294HZ,4
0x73,0xF4,//00311HZ,4#
0x1E,0xF5,//00330HZ,5
0xB6,0xF5,//00349HZ,5#
0x4C,0xF6,//00370HZ,6
0xD7,0xF6,//00392HZ,6#
0x5A,0xF7,//00415HZ,7
0xD8,0xF7,//00440HZ1//12
0x4D,0xF8,//00466HZ1#//13
0xBD,0xF8,//00494HZ2//14
0x24,0xF9,//00523HZ2#//15
0x87,0xF9,//00554HZ3//16
0xE4,0xF9,//00587HZ4//17
0x3D,0xFA,//00622HZ4#//18
0x90,0xFA,//00659HZ5//19
0xDE,0xFA,//00698HZ5#//20
0x29,0xFB,//00740HZ6//21
0x6F,0xFB,//00784HZ6#//22
0xB1,0xFB,//00831HZ7//23
0xEF,0xFB,//00880HZ`1
0x2A,0xFC,//00932HZ`1#
0x62,0xFC,//00988HZ`2
0x95,0xFC,//01046HZ`2#
0xC7,0xFC,//01109HZ`3
0xF6,0xFC,//01175HZ`4
0x22,0xFD,//01244HZ`4#
0x4B,0xFD,//01318HZ`5
0x73,0xFD,//01397HZ`5#
0x98,0xFD,//01480HZ`6
0xBB,0xFD,//01568HZ`6#
0xDC,0xFD,//01661HZ`7//35
};
//定时中断0,用于产生唱歌频率
timer0()interrupt1
{
TL0=tl0_f;TH0=th0_f;//调入预定时值
BEEP=~BEEP;//取反音乐输出IO
}
//******************************
//音乐符号串解释函数
//入口:
要解释的音乐符号串,输出的音调串,输出的时长串
changedata(uchar*song,uchar*diao,uchar*jie)
{
uchari,i1,j;
chargaodi;//高低+/-12音阶
ucharbanyin;//有没有半个升音阶
ucharyinchang;//音长
ucharcodejie7[8]={0,12,14,16,17,19,21,23};//C调的7个值
*diao=*song;
for(i=0,i1=0;;)
{
gaodi=0;//高低=0
banyin=0;//半音=0
yinchang=4;//音长1拍
if((*(song+i)=='|')||(*(song+i)==''))i++;
//拍子间隔和一个空格过滤
switch(*(song+i))
{
case',':
gaodi=-12;i++;//低音
break;
case'`':
gaodi=12;i++;//高音
break;
}
if(*(song+i)==0)//遇到0结束
{
*(diao+i1)=0;//加入结束标志0
*(jie+i1)=0;
return;
}
j=*(song+i)-0x30;i++;//取出基准音
j=jie7[j]+gaodi;//加上高低音
yinc:
switch(*(song+i))
{
case'#':
//有半音j加一个音阶
i++;j++;
gotoyinc;
case'-':
//有一个音节加长
yinchang+=4;
i++;
gotoyinc;
case'_':
//有一个音节缩短
yinchang/=2;
i++;
gotoyinc;
case'.':
//有一个加半拍
yinchang=yinchang+yinchang/2;
i++;
gotoyinc;
}
*(diao+i1)=j;//记录音符
*(jie+i1)=yinchang;//记录音长
i1++;
}
}
//******************************************
//奏乐函数
//入口:
要演奏的音乐符号串
voidplay(uchar*songdata)
{
uchari,c,j=0;
uintn;
ucharxdatadiaodata[112];//音调缓冲
ucharxdatajiedata[112];//音长缓冲
changedata(songdata,diaodata,jiedata);//解释音乐符号串
if(K4)
{TR0=1;}
for(i=0;diaodata[i]!
=0;i++)//逐个符号演奏
{
tl0_f=freq[diaodata[i]*2];//取出对应的定时值送给T0
th0_f=freq[diaodata[i]*2+1];
for(c=0;c{
for(n=0;n<32000;n++);
if(!
K5)//发现按键,立即退出播放
{
TR0=0;
}
while(!
K5);
TR0=1;
if(!
K4)//发现按键,立即退出播放
{
TR0=0;
return;
}
}
for(n=0;n<500;n++);//音符间延时
if(K4&&K5)
{TR0=1;}
}
}
//送别
ucharcodesongbie[]={
"53_5_`1-|6`15-|51_2_32_1_|2---|"
"53_5_`1.7_|6`15-|52_3_4.,7_|1---|"
"6`1`1-|76_7_`1-|6_7_`1_6_6_5_3_1_|2---|"
"53_5_`1.7_|6`15-|52_3_4.7_|1---"
};
//喀秋莎
ucharcodekaqiusha[]={
"6.7_|`1.6_|`17_6_|73|7.1_|`2.7_|`2`_2_`1_7_|6-|"
"`3`6|`5`6_`5_|`4`3_`2_|`36|`4`2|3.1|7_3_`1_7_|6-"
};
//两个按键选择播放,,任意一个按键停止播放
voidmain(void)//主程序
{
ulongn;
uchari;
ucharcodejie8[8]={12,14,16,17,19,21,23,24};//1234567`1八个音符在频率表中的位置
TMOD=0x01;//使用定时器0的16位工作模式
TR0=0;
ET0=1;//定时器0中断
EA=1;//打开总中断
while
(1)
{
if(!
K1)//按键K1
{
for(n=0;n<300;n++);//去抖延时
if(!
K1)
{
tl0_f=freq[jie8[i]*2];//置一个音符的值
th0_f=freq[jie8[i]*2+1];
if(K4)
{TR0=1;}
for(n=0;n<10000;n++);//声音延时
TR0=0;
BEEP=1;
i++;//循环下一个音符
if(i==8)i=0;
}
}
if(!
K2)
{
for(n=0;n<300;n++);//去抖延时
if(!
K2)
{
play(kaqiusha);//播放音乐
}
}
if(!
K3)
{
for(n=0;n<300;n++);//去抖延时
if(!
K3)
{
play(songbie);//播放音乐
}
}
}
}
四、思考题
如何通过程序编写音乐。
通过程序中的定义,“,”为低音,“`”为高音,“_”是1/2拍,“—”是2拍,“.”是一拍半,空格就是空拍。
按照这个简单的转换规则,就可以轻松的将普通的简谱转换为程序中需要的程序语句进行播放。
就这点来说,C语言远远优越于汇编语言。
五、拓展
实验要求的是一个八度的音阶,即1、2、3、4、5、6、7、i。
为了使演奏的音乐音阶更加丰富,以便演奏更多的曲目,我加入了半音即1#、2#、4#、5#、6#,并且把音宽扩展为3个八度,这样演奏出的歌曲旋律更加丰富动听。
另外添加了控制音乐播放的暂停,停止功能,完善了八音盒功能。