音乐发生器的设计与实现
- 格式:docx
- 大小:460.62 KB
- 文档页数:18
实验课程名称:监测控制系统应用实验五实验项目名称:音乐发生器的设计与实现实验成绩:实验者:专业班级:电信130 班同组者:实验日期:周四3~4节课一.实验内容掌握单片机片内定时器应用设计技巧,会确定定时器的时间常数,能够进行定时器的初始化编程。
掌握利用微处理器设计音乐发生器的方法二.探究内容1.利用定时器产生特定的频率信号,设计一个音乐发生器,可以循环播放音乐,候选乐曲3首。
2.用按键选择播放哪一首音乐。
有音乐播放的启停键。
3.用8个LED灯随节拍闪烁(选做)4.扩展内容(选做),用7个按键分别产生音阶1、2、3、4、5、6、7,按一下键,即产生一个节拍的相应音符,也就是设计电子琴。
三.实验设计:1.设计要求具体任务:(1)设计一个音乐发生器,候选音乐有三种,K1可启动停止音乐,K2用于选择音乐段;(2)用proteous仿真电路,保证设计的正确性;(3)基于开发板完成实物制作。
2. 探究内容:(1)如何利用音阶计算定时器的时间常数?(2)音乐声音的洪亮稳定如何保证?(3)节拍如何实现?三、实验设计:1.设计音乐发生器的基本任务的基本任务:(1)研究产生音阶1、2、3、4、5、6、7的方法,计算7个音阶对应的时间常数 (2)设计产生单首乐曲循环的软件,调试后下载到开发板(3) 设计产生3首乐曲的音乐发生器,用按键选择循环的单曲,调试后下载到开发板,运行。
2.相关知识介绍2.3 音阶对应频率计数初值的计算单片机的振荡频率为f osc =12MHz ,通过定时器T0溢出后对P3.0口取反产生方波,故定时器溢出时间为1/2f 。
由:n osc n f f X ⨯=⨯-2112)2(16 , 则定时初值为:16224osc n nf X f =- 以音阶“1”为例:f =523 Hz ,则T =1/f定时初值:6161612102264580442424523osc n n f X FC H f ⨯=-=-==⨯用同一方法可求出其它音阶所对应的频率定时初值,将其制表放在程序中,通过查表向定时器T0装入所要求的定时初值,即可产生某一音阶所对应的频率的方波信号。
C 调的音阶及其频率、在单片机中的定时初值对应如下:四.原理图1.音乐播放器原理图因为开发板上面的蜂鸣器为无源蜂鸣器,所以设计时要适应开发板的情况进行设计。
K1、K2按键用来进行上一曲和下一曲的选择,K3为暂停/开始按键,P2.0接蜂鸣器,P1口接LED灯,用LED灯显示节拍。
2.电子琴原理图上面七个键按下分别发出哆嘞咪发嗦啦嘻的音,第八个按键选择音调的高低。
最下面的按键用来控制使用和暂停。
五.实验结果音乐播放器仿真和实物都能正常的播放音乐,并且能够实现上一曲、下一曲的选择,并且有暂停和播放的功能,电子琴也能够正常发音,可以演奏音乐。
六、结果分析及总结(1)如何利用音阶计算定时器的时间常数?单片机的振荡频率为f osc =12MHz ,通过定时器T0溢出后对P3.0口取反产生方波,故定时器溢出时间为1/2f 。
由:n osc n f f X ⨯=⨯-2112)2(16 , 则定时初值为:16224osc n nf X f =- 以音阶“1”为例:f =523 Hz ,则T =1/f定时初值:6161612102264580442424523osc n n f X FC H f ⨯=-=-==⨯用同一方法可求出其它音阶所对应的频率定时初值,将其制表放在程序中,通过查表向定时器T0装入所要求的定时初值,即可产生某一音阶所对应的频率的方波信号。
(2)节拍如何实现?取乐曲节拍的长度为0.4s ,即1拍=0.4s ,由定时器T1控制延时。
设置定时器T1的定时中断T =50ms ,采用定时方式1。
由:T f X osc=⨯-12)2(16,则定时初值X =3C0BH因此1拍=0.4s =8*50ms ,即通过定时器T1的定时中断8次,产生8*50ms 的定时,就可以满足1节拍的定时要求。
以此类推,通过定时中断N 次,可产生N*50ms 的定时以满足1/4拍、1/2拍、1拍、2拍等不同节拍的定时要求。
由此可知,节拍值只能是整数值。
(3)音乐声音的洪亮稳定如何保证?这里的“源”不是指电源。
而是指震荡源。
也就是说,有源蜂鸣器内部带震荡源,所以只要一通电就会叫。
而无源内部不带震荡源,所以如果用直流信号无法令其鸣叫。
必须用2K~5K的方波去驱动它。
此次设计初,由于没意识到蜂鸣器驱动分为有源驱动和无源驱动,在使用网上的乐谱和相应的函数设计音乐播放器时,不能正确产生音乐,后来查资料才发现有无源和有源之分,有源和无源蜂鸣器的发声原理不同,故其乐谱会有所不同。
乐谱可以放在一个乐谱表中,通过查表来实现音乐的播放,要想循环播放一首歌,可以加一句查询语句,因为一条语句的执行时间非常短,对乐曲的演奏没有影响,在判断乐曲播放完后,继续回到乐曲开始,便可实现循环播放。
附录:音乐播放器程序#include<reg52.h>/*------------------------------------------------硬件端口定义------------------------------------------------*/sbit SPK=P2^0; //定义音乐输出端口sbit key1=P3^2; //定义按键用于暂停sbit key2=P3^3; //定义按键用于切换unsigned char W=0,T;//用于选择音乐unsigned char Timer0_H,Timer0_L,Time;void delay5ms(void) //误差0us{unsigned char a,b;for(b=185;b>0;b--)for(a=12;a>0;a--);}void delay500ms(void) //误差0us{unsigned char a,b,c;for(c=205;c>0;c--)for(b=116;b>0;b--)for(a=9;a>0;a--);}//世上只有妈妈好数据表音谱,高中低音,音长code unsigned char MUSIC[102]={6,2,3, 5,2,1, 3,2,2, 5,2,2, 1,3,2, 6,2,1, 5,2,1,6,2,4, 3,2,2, 5,2,1, 6,2,1, 5,2,2, 3,2,2, 1,2,1,6,1,1, 5,2,1, 3,2,1, 2,2,4, 2,2,3, 3,2,1, 5,2,2,5,2,1, 6,2,1, 3,2,2, 2,2,2, 1,2,4, 5,2,3, 3,2,1,2,2,1, 1,2,1, 6,1,1, 1,2,1, 5,1,6, 0,0,0 };//送别数据表音谱,高中低音,音长code unsigned char MUSIC1[174]={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,8, 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,1,1, 1,2,8,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,8, 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,1,1, 1,2,8 };//新年好数据表音谱,高中低音,音长code unsigned char MUSIC2[90]={1,2,1,1,2,1,1,2,2,5,1,2,3,2,1,3,2,1,3,2,2,1,2,2,1,2,1,3,2,1,5,2,2,5,2,2,4,2,1,3,2,1,2,2,4, 2,2,1,3,2,1,4,2,2,4,2,2, 3,2,1,2,2,1,3,2,2,1,2,2, 1,2,1,3,2,1,2,2,2,5,1,2, 7,1,1,2,2,1,1,2,4 };//童年数据表音谱,高中低音,音长code unsigned char MUSIC3[423]={5,1,1,5,1,1,3,1,1,5,1,2,3,1,1,5,1,2, 6,1,1, 6,1,1,1,2,1,6,1,2,6,1,1,1,2,2, 2,2,1,2,2,1,3,2,1,2,2,2,5,1,1,6,1,1,5,1,1, 1,2,1,1,2,1, 5,1,1,1,2,1,1,2,1,1,2,2,1,2,1, 1,2,1,5,1,2, 1,2,1,6,1,1,5,1,1,3,1,1,2,1,1, 1,1,2,3,1,1, 5,1,1,5,1,2,5,1,1,3,1,1, 6,1,1,6,1,1,1,2,1, 6,1,1,6,1,1,6,1,1,6,1,1,5,1,1, 1,2,2,1,2,1, 1,2,1,1,2,1,6,1,1,1,2,1,6,1,1, 5,1,2,0,2,1, 3,1,1,6,1,1,1,2,1,6,1,1,5,1,1, 3,1,1,2,1,1, 3,1,1,5,1,1,5,1,2,5,1,1,3,1,1, 6,1,1,6,1,1, 1,2,1,6,1,1,6,1,1,6,1,1,6,1,1,5,1,1, 1,2,2, 1,2,1,1,2,1,1,2,1,6,1,1,6,1,1,1,2,1, 2,2,2, 0,2,1,5,1,1,2,2,1,5,2,1,2,2,2, 5,2,1,5,2,2, 5,2,1,5,2,1,5,2,1,3,2,1,2,2,1, 1,2,1,1,2,2, 6,1,1,6,1,1,1,2,1,6,1,1,1,2,1, 2,2,1, 2,2,1, 2,2,1,2,2,1,2,2,1,1,2,1,3,2,1,2,2,1, 2,2,8, 3,2,1,3,2,2,3,2,1,3,2,2,2,2,2, 1,2,1,1,2,2, 1,2,1,2,2,1,1,2,1,6,1,1,5,1,1, 5,1,1,5,1,2, 5,1,1,6,1,1,5,1,1,2,2,1,3,2,1, 1,2,8, 1,2,1, 5,1,2,1,2,1,6,1,1,5,1,1,3,1,1,2,1,1, 1,2,6, 0,2,2 };// 音阶频率表高八位code unsigned char FREQH[]={0xF2,0xF3,0xF5,0xF5,0xF6,0xF7,0xF8, 0xF9,0xF9,0xFA,0xFA,0xFB,0xFB,0xFC,0xFC, 0xFC,0xFD,0xFD,0xFD,0xFD,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFF,} ; //1,2,3,4,5,6,7,8,i// 音阶频率表低八位code unsigned char FREQL[]={0x42,0xC1,0x17,0xB6,0xD0,0xD1,0xB6,0x21,0xE1,0x8C,0xD8,0x68,0xE9,0x5B,0x8F,0xEE,0x44,0x6B,0xB4,0xF4,0x2D,0x47,0x77,0xA2,0xB6,0xDA,0xFA,0x16,};//1,2,3,4,5,6,7,8,i/*------------------------------------------------uS延时函数,含有输入参数unsigned char t,无返回值unsigned char 是定义无符号字符变量,其值的范围是0~255 这里使用晶振12M,精确延时请使用汇编,大致延时长度如下T=tx2+5 uS------------------------------------------------*/void DelayUs2x(unsigned char t){while(--t);/*------------------------------------------------mS延时函数,含有输入参数unsigned char t,无返回值unsigned char 是定义无符号字符变量,其值的范围是0~255 这里使用晶振12M,精确延时请使用汇编------------------------------------------------*/void DelayMs(unsigned char t){while(t--){//大致延时1mSDelayUs2x(245);DelayUs2x(245);}}/*------------------------------------------------节拍延时函数各调1/4节拍时间:调4/4 125ms调2/4 250ms调3/4 187ms------------------------------------------------*/void delay(unsigned char t){unsigned char i;for(i=0;i<t;i++)DelayMs(250);TR0=0;}/*------------------------------------------------定时器0中断------------------------------------------------*/void TIM0_ISR() interrupt 1{TR0=0;SPK=!SPK;TH0=Timer0_H;TL0=Timer0_L;TR0=1;}/*------------------------------------------------歌曲处理函数------------------------------------------------*/void Song()TH0=Timer0_H;//赋值定时器时间,决定频率TL0=Timer0_L;TR0=1; //打开定时器delay(Time); //延时所需要的节拍}/*------------------------------------------------主函数------------------------------------------------*/void led(unsigned char a);void main(void){unsigned int i;unsigned char k;TMOD|=0x01; //置定时器0工作方式1EA=1; //打开全局中断ET0=1; //打开定时器0 中断EX0=1;//打开外部中断0 中断IT0=1;//设置为下降沿中断EX1=1;//打开外部中断1 中断IT1=1;//设置为下降沿中断PX1=1;//设置外部中断1 为高优先级while(1){//i=0;if(W==0){T=W;for(i=0;i<100;i=i+3){ //音乐数组长度,唱完从头再来led(MUSIC[i]);k=MUSIC[i]+7*MUSIC[i+1]-1;//去音符振荡频率所需数据Timer0_H=FREQH[k];Timer0_L=FREQL[k];Time=MUSIC[i+2]; //节拍时长// i=i+3;Song();if(T!=W)break;}}if(W==1){T=W;for(i=0;i<174;i=i+3){ //音乐数组长度,唱完从头再来led(MUSIC1[i]);k=MUSIC1[i]+7*MUSIC1[i+1]-1;//去音符振荡频率所需数据Timer0_H=FREQH[k];Timer0_L=FREQL[k];Time=MUSIC1[i+2]; //节拍时长// i=i+3;Song();if(T!=W)break;}}if(W==2){T=W;for(i=0;i<90;i=i+3){ //音乐数组长度,唱完从头再来led(MUSIC2[i]);k=MUSIC2[i]+7*MUSIC2[i+1]-1;//去音符振荡频率所需数据Timer0_H=FREQH[k];Timer0_L=FREQL[k];Time=MUSIC2[i+2]; //节拍时长// i=i+3;Song();if(T!=W)break;}}if(W==3){T=W;for(i=0;i<423;i=i+3){ //音乐数组长度,唱完从头再来led(MUSIC3[i]);k=MUSIC3[i]+7*MUSIC3[i+1]-1;//去音符振荡频率所需数据Timer0_H=FREQH[k];Timer0_L=FREQL[k];Time=MUSIC3[i+2]; //节拍时长// i=i+3;Song();if(T!=W)break;}}}}// 频谱函数void led(unsigned char a){switch(a){case 0:P1=0xff;break;case 1:P1=0x7f;break;case 2:P1=0x7e;break;case 3:P1=0x7c;break;case 4:P1=0x78;break;case 5:P1=0x70;break;case 6:P1=0x60;break;case 7:P1=0x40;break;case 8:P1=0x00;break;}}//外部中断0 中断服务程序用于暂停void zhongduan0() interrupt 0{delay5ms();//按键去抖if(!key1){while(!key1);//松手检测while(key1&&T==W);//检测按键是否按下delay5ms();//按键去抖while(key1&&T==W);//检测按键是否按下while(!key1&&T==W);//松手检测}}//外部中断1 中断服务程序void zhongduan1() interrupt 2{delay5ms();//按键去抖if(!key2){if(W==3)W=0;elseW++;while(!key2);//松手检测delay500ms();}}电子琴程序#include <reg52.h>#include <intrins.h>#define uchar unsigned char#define uint unsigned int//*******子函数声明*************//void Run();void PlayKey();void KeyScan();void KeyScan1();//********手动弹奏按键定义****************// sbit Beep=P1^0;sbit key1=P2^0;sbit key2=P2^1;sbit key3=P2^2;sbit key4=P2^3;sbit key5=P2^4;sbit key6=P2^5;sbit key7=P2^6;sbit key8=P2^7;//*******播放模式下按键定义***********//sbit s3=P3^5;//暂停/播放键uchar Th0,Tl0;uchar key=0;//按键代号uchar yinfu,jiepai,i,j,m;uchar gaodi=0;uchar flag=0;//暂停、开始uchar num;//全局变量uchar s1num;//暂停/播放键按下的次数//歌曲音符/*uchar code MusicCode[]={0xfc,0x0b,0xfc,0x43,0xfc,0xab,0xfd,0x08,0xfd,0x33,0xfd,0x81,0xfd,0xc7,0xfe,0x05,0xfe,0x21,0xfb,0x8f,0xfb,0x03};*/uchar code MusicCode[]={0xfc,0x44,0xfc,0xac,0xfd,0x09,0xfd,0x34,0xfd,0x82,0xfd,0xc8,0xfe,0x06,0xfb,0x04,0xfb,0x90,0xfc,0x0c,0xfe,0x22,0xf9,0x5b,0xfa,0x15}; //按键音符uchar code KeyCode[]={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 interrupt0() interrupt 0 //外部终端0{flag=~flag;}//************曲目选择外部中断**********************//void interrupt1() interrupt 2//外部终端1{flag1=1;}//********延时子程序*******************//void delay_1ms(uint i){uint k,x;for(x=0;x<i;x++){for(k=0;k<110;k++);}}//*******手动弹奏按键扫描子程序***********************// void KeyScan(){if(key8==0)//如果P2.2=0{delay_1ms(5);if(key8==0){//while(!key8)gaodi++;if(gaodi>2){gaodi=0;}}}else if(key1==0){delay_1ms(5);if(key1==0)while(!key1);key=1;}else if(key2==0){delay_1ms(5);if(key2==0)while(!key2);key=2;}else if(key3==0){delay_1ms(5);if(key3==0)while(!key3);key=3;}else if(key4==0)delay_1ms(5);if(key4==0)while(!key4);key=4;}else if(key5==0){delay_1ms(5);if(key5==0)while(!key5);key=5;}else if(key6==0){delay_1ms(5);if(key6==0)while(!key6);key=6;}else if(key7==0){delay_1ms(5);if(key7==0)while(!key7);key=7;}elsereturn;}//**********手动弹奏按键发声子程序**************// void PlayKey(){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;}//***********音乐发生定时中断************************// void time0()interrupt 1//定时器0中断{TH0=Th0;TL0=Tl0;Beep=~Beep;}//********暂停/播放按键扫描子程序***************//void KeyScan1(){if(s3==0){delay_1ms(5);if(s3==0){s1num++;while(!s3);if(s1num==1){EA=0;TR0=0;}if(s1num==2){s1num=0;EA=1;TR0=1;}}}elseTR0=1;}void Run()//功能程序{if(flag==1)while(1);}else{KeyScan();PlayKey();}}//********主程序*********//void main(){key1=1;key2=1;key3=1;key4=1;key5=1;key6=1;key7=1;key8=1;TMOD=0x01;EA=1;//开放总中断ET0=1;//T0中断允许TR0=0;//关闭定时器0IT0=1;//置外部中断为边沿(下降沿)触发方式EX0=1;//开放外部中断0允许位EX1=1;IT1=1;init();while(1){Run();}}。