单片机课程设计报告电子琴.docx
《单片机课程设计报告电子琴.docx》由会员分享,可在线阅读,更多相关《单片机课程设计报告电子琴.docx(32页珍藏版)》请在冰豆网上搜索。
单片机课程设计报告电子琴
电子音调发生器
一、实验目的
1.了解计算机发声原理。
2.熟悉定时器和键盘扫描电路的工作原理及编程方法。
二、实验完成的功能
1.利用键盘1~7进行音调选择,即按下音符产生对应音调。
2.事先存储三首歌曲,并可进行选择播放。
3.谱曲功能:
通过按键对LCD菜单选项进行选择,进入谱曲界面,通过按键1~7分别输入音高与几分音符类型,由按键输入若干数据完成谱曲。
4.在播放存储歌曲与谱曲播放时,对应音符及其节奏LCD显示对应频谱。
5.在播放音乐时按“返回”键出现返回界面,由键盘按“确认”键选择返回主菜单或循环播放。
三、实验原理
1.音节由不同频率的方波产生,音节与频率的关系如表
(1)所示。
要产生音频方波,只要计算出某一音频的周期(1/频率),然后将此周期除以2,即为半周期的时间。
利用计时器计时此半周期时间,每当计时到后就将输出方波的I/O(P1.7)反相,然后重复计时此半周期时间再对I/O反相,就可在P1.7脚得到此频率的方波。
将P1.7经过驱动电路与蜂鸣器相连,随着P1.7口输出不同频率的方波,蜂鸣器便会发出不同的声音。
音乐的节拍是由延时实现的,如果1拍的时间为0.4秒,1/4拍是0.1秒。
只要设定延时时间,就可得到节拍的时间。
延时实现基本延时时间,节拍值只能是它的整数倍。
每个音节相应的定时器初值计算公式如下:
(1/2)*(1/f)=(12/fose)*(216-x)
即x=216-(fose/24f)
其中,f是音调频率,当晶振fosc=11.0592MHz时,音节“1”相应的定时器初值为x,则可得到x=63777D=F921H,其它的可同样得到。
表
(1)音节与频率的关系
音调
频率(Hz)
X(HEX)
1
262
F921
2
294
F9E1
3
330
FA8C
4
349
FAD8
5
392
FB68
6
440
FBE9
7
494
FC5B
i
523
FC8F
在编写歌曲代码过程中,音高由三位数字组成:
个位是表示1~7这七个音符;十位是表示音符所在的音区:
1-低音,-中音,-高音;百位表示这个音符是否要升半音:
0-不升,-升半音。
音长最多由三位数字组成:
个位表示音符的时值,其对应关系是:
|数值(n):
|0|1|2|3|4|5|6
|几分音符:
|1|2|4|8|16|32|64音符=2^n
十位表示音符的演奏效果(0-2):
0-普通,-连音,-顿音,百位是符点位:
0-无符点,1-有符点。
2.键盘扫描
将单片机P1.0~P1.7(引脚1~8)与键盘上对应引脚相连,其中P1.0~P1.3控制键盘的行,P1.4~P1.7控制键盘的列,这次使用的键盘是4行4列,键盘上的第一行到第四行由8位十六进制代码的低四位控制,即第一行若有键按下,则8位十六进制代码的后四位为0x8,第二行有按键按下,则后四位代码为0x4,依此规律类推;键盘上的第一列到第四列由8位十六进制代码的高四位控制,即第一列若有键按下,则8位十六进制代码的前四位为0x8,第二列有按键按下,则前四位代码为0x4,依此规律类推。
综上所述,可以根据8位十六进制的高四位和低四位数据,分别确定按下的键处于第几行第几列,从而确定按键的位置,实现键盘扫描的功能。
3.LCD显示
根据写命令函数wrcomd和写数据函数wrdata分别控制LCD的命令写入和数据写入。
具体操作方法是由写命令函数wrcomd编程控制数据输入的行列,再由写数据函数wrdata编程写入需要在LCD上显示的数据或应执行的操作,结合两个命令一起控制LCD显示。
四.产品展示
五.实验程序
#ifndef_DRIVEFUTION_H_
#define_DRIVEFUTION_H_
/*********************EEPROM函数*******************/
/**************************************************/
/******************关闭ISP,IAP功能**************/
voidISP_IAP_disable(void)
{
ISP_CONTR=0X00;
ISP_CMD=0X00;
ISP_TRIG=0x00;
}
/*******************字节读***************************/
ucharByte_read(uintbyte_addr)
{
EA=0;//关中断
ISP_CONTR=En_Wait_TIME;//开启ISP/IAP;并送等待时间
ISP_CMD=Read_COM;//送字节读命令字
ISP_ADDRH=(uchar)(byte_addr>>8);//送地址高字节
ISP_ADDRL=(uchar)(byte_addr&0X00FF);//送地址低字节
ISP_TRIG=0X46;//送触发命令字0X46、0XB9
ISP_TRIG=0XB9;
_nop_();
ISP_IAP_disable();//关闭ISP/IAP功能
EA=1;//开中断
return(ISP_DATA);
}
/********************字节编程***********************/
voidByte_program(uintbyte_addr,ucharisp_iap_data)
{
EA=0;//关中断
ISP_CONTR=En_Wait_TIME;//开启ISP/IAP;并送等待时间
ISP_CMD=Prog_COM;//送字节编程命令字
ISP_ADDRH=(uchar)(byte_addr>>8);//送地址高字节
ISP_ADDRL=(uchar)(byte_addr&0X00FF);//送地址低字节
ISP_DATA=isp_iap_data;//送数据进ISP_DATA
ISP_TRIG=0X46;//送触发命令字0X46、0XB9
ISP_TRIG=0XB9;
_nop_();
ISP_IAP_disable();//关闭ISP/IAP功能
EA=1;//开中断
}
/*********************扇区擦除*********************/
voidSector_erase(uintsector_addr)
{
EA=0;//关中断
ISP_CONTR=En_Wait_TIME;//开启ISP/IAP;并送等待时间
ISP_CMD=Dele_COM;//送扇区擦除命令字
ISP_ADDRH=(uchar)(sector_addr>>8);//送地址高字节
ISP_ADDRL=(uchar)(sector_addr&0X00FF);//送地址低字节
ISP_TRIG=0X46;//送触发命令字0X46、0XB9
ISP_TRIG=0XB9;
_nop_();
ISP_IAP_disable();//关闭ISP/IAP功能
EA=1;
}
/*********************写入函数**********************/
voidEEPROM_write(uintaddr,ucharin_data)
{
Sector_erase(addr);
Byte_program(addr,in_data);
}
/*********************EEPROM函数*******************/
/**************************************************/
/***************音乐播放相关子函数*******************/
/****************************************************/
voidInitialSound(void)//初始化定时器
{
BeepIO=0;
Sound_Temp_TH1=(65535-(1/1200)*SYSTEM_OSC)/256;//计算TL1应装入的初值(10ms的初装值)
Sound_Temp_TL1=(65535-(1/1200)*SYSTEM_OSC)%256;//计算TH1应装入的初值
TH1=Sound_Temp_TH1;
TL1=Sound_Temp_TL1;
TMOD|=0x11;
ET0=1;
ET1=0;
TR0=0;
TR1=0;
EA=1;
}
voidBeepTimer0(void)interrupt1//音符发生中断函数
{
BeepIO=!
BeepIO;
TH0=Sound_Temp_TH0;
TL0=Sound_Temp_TL0;
}
/*********************END****************************/
/****************************************************/
/*****************LCD操作子函数**********************/
/****************************************************/
voidwrcomd(charcomd)//写入命令
{
cs=0;_nop_();
rs=0;_nop_();
e=1;_nop_();
rw=0;_nop_();
P0=comd;_nop_();
rw=0;_nop_();_nop_();
rw=1;_nop_();_nop_();_nop_();_nop_();
}
voidwrdata(chardata_wri)//往液晶中写入数据
{
cs=0;_nop_();
rs=1;_nop_();
e=1;_nop_();
rw=0;_nop_();
P0=data_wri;_nop_();
rw=0;_nop_();_nop_();
rw=1;_nop_();_nop_();_nop_();_nop_();
}
voidbmp(chardata_write,introw_start,introw_end,ucharcolumn_start,ucharcolumn_end)//刷屏子函数
//初始化设置,对屏幕清零,默认值,为0,0,8,2,132
{
unsignedchari,j;
intnum_3;
charnum_0=0xb0;
//ucharcolumn_h,column_l;
num_0+=row_start;
for(i=row_start;i{
num_3=column_end;
wrcomd(num_0);//写入命令设置页,从0页开始,0xb0=10110000;delay(time_def);//设置页
wrcomd(0x10|(0xf0&column_start)>>4);//设置列,先写入高4位再写入低四位
wrcomd(0x00|(0x0f&column_start));
for(j=column_start;j{
wrdata(data_write);
}
num_0++;
}
}
voiddelay(longinttime_delay)//延时子程序,执行time_delay个_nop_()延时
{
longinti;
for(i=0;i_nop_();
}
voidwrite_word(char*p_word,intnum,intpos_x,intpos_y,intwidth,intheight)//写汉字/字符子函数
//对应参数数据数组名,数组中位置,行位置,列位置,宽,高
{
charpage;//页号
unsignedinti,j,k,p;
p=(unsignedint)num*2*width;//p确定汉字字模数据初始位置
i=height%8;
k=(i)?
(height/8+1):
(height/8);
page=0xb0+pos_y;
for(i=0;i<2;i++)
{
wrcomd(page);
wrcomd(0x10|(0xf0&pos_x)>>4);
wrcomd(0x00|(0x0f&pos_x));
for(j=0;j{
wrdata(p_word[j+p]);
}
p=p+width;
page++;
if(page-(unsignedchar)0xb0>7||page-(unsignedchar)0xb0<0)
return;
}
}
/**************************END***********************/
/****************************************************/
/******************键盘扫描函数*********************/
/****************************************************/
/****************判断是否有键盘输入*********************/
bitCheckState(void)//键盘状态判断子函数
{
bitstate=0;
P1=0x0F;//键盘扫描
if(P1!
=0x0F)state=1;//检测是否有按键输入
elsestate=0;
return(state);
}
/******************获取键盘输入值*****************/
ucharGetKeys(void)//键盘值获取子函数
{
ucharcolumn,line;
scan=0xFE;
while((scan&0x10)!
=0)
{
P1=scan;
if((P1&0xF0)!
=0xF0)//line1havekeypressed
{
column=~(P1|0x0F);
line=~(scan|0xF0);
key=column+line;
return(key);
}
elsescan=(scan<<1)|0x01;
}
return(0);
}
/******************应用子函数*****************/
/*********************************************/
voidcsh(void)//初始化,清屏
{
inti;
res=0;
delay(time_def);
delay(time_def);
res=1;
delay(time_def);
cs=0;//片选一直有效
delay(time_def);
delay(time_def);
for(i=0;i<12;i++)//初始命令写入
{
wrcomd(tab1[i]);
delay(time_def);
}
bmp(0,0,8,0,132);
}
voidstart_print(void)//开机显示画面子函数
{
inti,j;
for(i=0;i<4;i++)
{
write_word(star_welcome,i,36+i*16,2,16,16);
if((i+4)<7)
{
write_word(star_welcome,i+4,44+i*16,4,16,16);
if((i+7)<10)
{
write_word(star_welcome,i+7,36+i*16,6,16,16);
}
}
}
write_word(number,10,4+5*16,6,8,16);
for(j=5;j>=0;j--)
{
write_word(number,j,12+5*16,6,8,16);
delay(5000);
}
}
voidmenu_first_symbol(intchoose)//菜单选择符刷新子函数,即“打钩项”
{
bmp(0,0,8,4,18);
write_word(menu_one,20,4,choose*2,16,16);
}
/*********************音乐播放显示函数******************/
/*******************************************************/
voidmusic_display_one(unsignedchars,unsignedintx)//显示最底层子函数
{
inti;
bmp(0,2,8,18+x*16,34+x*16);
for(i=3;i>0;i--)
{
if(s!
=0)
{
write_word(display,1,20+16*x,2*i,16,16);
s=s-1;
}
else
{
write_word(display,0,20+16*x,2*i,16,16);
//delay(200);
}
}
}
voidmusic_display_two(unsignedintp_x)//显示次底层子函数
{
intsign;
sign=mc_size[p_x][0];
if(sign)
{
if(!
size[p_x])
{
sign=0;
mc_size[p_x][0]=sign;
mc_size[p_x][1]=0;
}
else
{
music_display_one(size[p_x],p_x);
mc_size[p_x][1]+=1;
if(mc_size[p_x][1]>3)
size[p_x]-=1;
else
size[p_x]+=1;
}
}
/*************************************/
else
{
bmp(0,2,8,18+p_x*16,34+p_x*16);
write_word(display,2,20+16*p_x,6,16,16);
//delay(500);
}
/************************************/
}
voidmusic_display_three(unsignedintmc)//音符状态置位函数
{
mc_size[mc-1][0]=1;
mc_size[mc-1][1]=1;
size[mc-1]=1;
}
voidmusic_display_four(void)//显示次顶层子函数
{
inti;
for(i=0;i<7;i++)
{
music_display_two(i);
}
}
voidmusic_display_five()//显示顶层子函数
{
inti;
allsize=0;
for(i=0;i<7;i++)
allsize+=mc_size[i][0];
music_display_four();
}
/*
voidmusic_display(void)//显示主函数
{
inti;
for(i=0;i<10;i++)
{
music_display_three(music_code[i]);
music_display_five();
}
while(allsize)
music_display_five();
}*/
voiddisplay_between_musicplay(unsignedinttone)//播放音乐同时显示音符显示界面
{
music_display_three(tone);
music_display_five();
}
/****************************************************/
/*********************播放子程序*********************/
voidmusic_play_one(uint*NewFreTab,ucharTone,ucharLength,uintLDiv0,uintLDiv1)
//频率表,音符,音长,12000/Speed,((12000/Speed)/4)-((12000/Speed)/4)*SOUND_SPACE
{
unsignedcharSL,SH,SM,SLen,XG,FD;
uinti,Temp_T,CurrentFre,LDiv,LDiv2,LDiv3;
SL=Tone%10;//计算出音符
SM=Tone/10%10;//计算出高低音
SH=Tone/100;//计算出是否升半
CurrentFre=NewFreTab[SignTab[SL-1]+SH];//查出对应音符的频率
if(SL!
=0)
{
if(SM==1)CurrentFre>>=2;//低音
if(SM==3)CurrentFre<<=2;//高音
Temp_T=65536-(50000/CurrentFre)*10/(12000000/SYSTEM_OSC);//计算计数器初值
Sound_Temp_TH0=Temp_T/256;
Sound_Temp_TL0=Temp_T%2