蜂鸣器音乐发生器实验报告.docx
《蜂鸣器音乐发生器实验报告.docx》由会员分享,可在线阅读,更多相关《蜂鸣器音乐发生器实验报告.docx(28页珍藏版)》请在冰豆网上搜索。
蜂鸣器音乐发生器实验报告
蜂鸣器音乐发生器实验报告
1、实验目的
(1)学习用数控分频器设计蜂鸣器音乐发生电路。
(2)了解乐谱的基本知识,可以将乐谱转换为QuartusII文件,掌握其演奏的原理。
(3)掌握设计中各模块的功能,能够填入并演奏新的曲子。
2、实验设备与器件
QuartusII软件、EP2C8Q208C8实验箱
3、实验方案设计
1.实验可实现的功能
(1)蜂鸣器可以演奏四首音乐,四首音乐通过两个拨码开关控制,可以随意更改想听的曲目。
(2)在播放音乐的同时,用一位数码管显示当前音乐的简谱,并且用两个发光二极管显示高、中、低不同的音调。
(3)在用拨码开关选择曲目的同时,可以在LCD1602液晶屏上看到当前音乐的名称。
2.音频方案设计
蜂鸣器音乐发生器的基本原理:
组成乐曲的每个音调的频率值以及音长所延续的时间是乐曲能够连续演奏的两个基本数据,所以只要控制输出到蜂鸣器的时钟信号频率的高低和持续的时间,就可以使蜂鸣器发出连续的乐曲声。
(1)音调频率值的控制
简谱中音调与音频的对应关系如表3.2.1所示,表中的低、中、高音的频率遵循二倍规则,就是说中音1是低音1频率的2倍,高音1是中音1频率的2倍,以此类推。
已知低音的频率,可以通过如下的MATLAB程序计算出中、高音的频率,并且可以得出各音调的分频值与频率预直数,其中预置数是用11位计数器来表示的。
计算中、高音及各音调分频值与频率预置数的MATLAB程序:
clc;
f=50000000;%50MHz
bilv=2^(1/12);%相邻音调频率之间的比率
a(6)=440.0;%低音6的频率为440Hz
a(7)=a(6)*bilv*bilv;%低音7的频率
a(5)=a(6)/bilv/bilv;%低音5的频率
a(4)=a(5)/bilv/bilv;%低音4的频率
a(3)=a(4)/bilv;%低音3的频率
a
(2)=a(3)/bilv/bilv;%低音2的频率
a
(1)=a
(2)/bilv/bilv;%低音1的频率
b=a*2;%中音的频率
c=b*2;%高音的频率
counter=2^11;%分频值对应的位数为11位
f=f/50/2;%50MHz,50分频,再2分频
fori=1:
7
zhia(i)=counter-f/a(i);%低音的分频预置数
zhib(i)=counter-f/b(i);%中音的分频预置数
zhic(i)=counter-f/c(i);%高音的分频预置数
end
音调、分频值及频率预置数的表格如下:
表3.2.1音符、音频及其预置数
低音
音符
1
2
3
4
5
6
7
音频/Hz
262
294
330
349
392
440
494
预置数
137
345
531
616
772
912
1036
中音
音符
1
2
3
4
5
6
7
音频/Hz
523
587
659
698
784
880
988
预置数
1092
1197
1290
1332
1410
1480
1542
高音
音符
1
2
3
4
5
6
7
音频/Hz
1047
1175
1319
1397
1568
1760
1976
预置数
1570
1622
1669
1690
1729
1764
1795
(2)音调持续时间的控制
音乐中的银除了有高低音之分外,还有长短之分。
简谱中用一条横线“—”在音符的右面或者下面来标注音的长短。
表3.2.2列出了常用音符及其长度标记。
表3.2.2常用音符及其长度标记
音符名称
写法
时值
全音符
1———
四拍
二分音符
1—
二拍
四分音符
1
一拍
八分音符
1
半拍
十六分音符
1
四分之一拍
从表3.2.2可以看出横线有记在音符后面的,也有记在音符下面的,横线标记的位置不同,被标记音符的时值也不同。
要使音符时值延长,在四分音符右边加横线“—”,这时的横线叫做延时线。
延时线越多,音调持续的时间就越长。
音乐中除了有音的高低、长短之外,也有音的休止。
表示音的休止的符号叫休止符,用“0”标记。
每增加一个0,就增加一个四分休止符的时值。
3.系统工作原理
本系统共分为4个模块组成,其中Songer.v是顶层设计文件,其内部有4个功能模块:
jianpu.v、zhuanhuan.v、fenpin.v、lcd1602.v,功能模块如图3.3.1所示。
图3.3.1系统功能模块电路
(1)音符的频率可以由图3.3.1中的模块fenpin获得。
这是一个数控分频器,由其clk端输入50MHz的时钟信号,通过fenpin分频之后由spkout输出。
fenpin对clk输入信号的分频比由11位预置数tone决定。
Spkout的输出频率将决定每一音符的音调。
这样,分频预置数tone与spkout的输出频率就对应起来了。
(2)音符的持续时间是根据乐曲的速度及每个音符的节拍数来确定的,图3.3.1中模块zhuanhuan的功能首先是为fenpin提供决定所发音符分频预置数,而此数在fenpin输入口停留的时间即为此音符的节拍值。
模块zhuanhuan是乐曲简谱与相应的分频预置数之间的转换电路,其中设置了高、中、低音全部音符所对应的分频预置数,共21个,每一音符的停留时间由音乐节拍和音调发生器模块jianpu的clk的输入频率决定,这里为4Hz。
这21个值的输入由对应于zhuanhuan的5位输入值index确定,而index最多有32种可选值。
Toneindex输向zhuanhuan中的index,其值与持续时间由模块jianpu决定。
(3)在jianpu中设置了,一个9位二进制计数器,作为简谱数据ROM的地址指针。
这个计数器的计数频率选为4Hz,即每一计数值的停留时间为0.25s,恰为当全音符设为1s时,四四拍的4分音符的持续时间。
ROM中的低音用0~7表示,中音在低音的基础上再加7,用8~14表示,同理高音用15~21表示。
当jianpu中的计数器按4Hz的时钟速率做加法计数时,ROM中的简谱通过toneindex端口输向zhuanhuan模块,乐曲就可以连续的演奏了。
此外还设置了一个两位的乐曲选择端sel,当sel为00时,可演奏高、中、低音的循环演奏;当sel为01时,演奏《两只老虎》;当sel为10时,演奏《天空之城》;当sel为11时,演奏《快点告诉你》。
(4)在lcd1602模块中设置了歌曲名称显示功能,由sel作为选择端口,当sel为00时,可以在lcd1602液晶显示屏的第一行显示“gaozhongdiyin”;当sel为01时,显示“liangzhilaohu”;当sel为10时,显示“tiankongzhicheng”;当sel为11时,显示“kuaidiangaosuni”。
4.各模块程序设计
(1)蜂鸣器音乐发生器顶层程序设计
moduleSonger(song_sel,clk,ledpos,ledneg,spkout,sms,smb,rst_n,lcd_data,lcd_e,
lcd_rs,lcd_rw,SEL0,SEL1,SEL2);
input[1:
0]song_sel;//对四首乐曲进行选择
inputclk;//音调频率信号
inputrst_n;//节拍频率信号
output[1:
0]ledpos;//发光二极管的阳极
output[3:
0]ledneg;//发光二极管的阴极
output[7:
0]sms;//数码管的段选
output[7:
0]smb;//数码管的位选
outputspkout;//蜂鸣器输出
output[7:
0]lcd_data;//数据总线
outputlcd_e;//使能信号
outputlcd_rs;//指令、数据选择
outputlcd_rw;//读、写选择
outputSEL0;//LCD1602读写选择
outputSEL1;//LCD1602读写选择
outputSEL2;//LCD1602读写选择
wire[10:
0]tone;//分频预置数--跟音调相匹
wire[4:
0]toneindex;//音符
regclk4hz;
reg[24:
0]cnt1;//计数器
reg[24:
0]cnt2;//计数器
always@(posedgeclk)
begin
if(cnt2>=25'b101111101011110000100000)
begin
clk4hz=~clk4hz;//对50MHz进行4Hz分频
cnt2<=25'b0;
end
else
begin
cnt2<=cnt2+1'b1;
end
end
jianpuu1(.sel(song_sel),.clk(clk4hz),.toneindex(toneindex));
zhuanhuanu2(.index(toneindex),.tone(tone),.ledpos(ledpos),.ledneg(ledneg),
.sms(sms),.smb(smb));
fenpinu3(.clk(clk),.tone(tone),.spks(spkout));
lcd1602u4(.clk(clk),.rst_n(rst_n),.lcd_data(lcd_data),.lcd_e(lcd_e),
.lcd_rs(lcd_rs),.lcd_rw(lcd_rw),.SEL0(SEL0),.SEL1(SEL1),.SEL2(SEL2),.sel(song_sel));
endmodule
(2)fenpin模块程序设计
modulefenpin(clk,tone,spks);
inputclk;
input[10:
0]tone;//分频预置数--跟音调相匹配
outputregspks;//声音输出
regpreclk,fullspks;
always@(posedgeclk)
begin
reg[3:
0]count4;
preclk<=0;
if(count4>49)
begin
preclk<=1;//将clk进行50分频
count4=0;
end
else
count4=count4+1;
end
always@(posedgepreclk)
begin
reg[10:
0]count11;//11位可预置计数器
if(count11==11'h7FF)
begin
count11=tone;
fullspks<=1;//按照预置数进行分频
end
else
begin
count11=count11+1;
fullspks<=0;
end
end
always@(posedgefullspks)
begin
regcount2;
count2=~count2;
if(count2==1)
spks<=1;//将输出再次2分频,展宽脉冲,使
扬声器有足够功率发音
else
spks<=0;
end
endmodule
(3)zhuanhuan模块程序设计
modulezhuanhuan(index,sms,smb,ledpos,ledneg,tone);
input[4:
0]index;//音符
outputreg[7:
0]sms;//数码管段选,用于显示简谱
outputreg[7:
0]smb;//数码管位选,用于显示简谱
outputreg[1:
0]ledpos;//发光二极管阳极,用于区分低、中、
高音
outputreg[3:
0]ledneg;//发光二极管阴极,用于区分低、中、
高音
outputreg[10:
0]tone;//分频预置数--跟音调相匹配
reg[3:
0]code;
always@(index)
begin
case(index)//将简谱音符与分频预置数相匹配
5'b00000:
tone<=11'b11111111111;
5'b00001:
tone<=11'd137;
5'b00010:
tone<=11'd345;
5'b00011:
tone<=11'd531;
5'b00100:
tone<=11'd616;
5'b00101:
tone<=11'd773;
5'b00110:
tone<=11'd912;
5'b00111:
tone<=11'd1036;
5'b01000:
tone<=11'd1092;
5'b01001:
tone<=11'd1197;
5'b01010:
tone<=11'd1290;
5'b01011:
tone<=11'd1332;
5'b01100:
tone<=11'd1410;
5'b01101:
tone<=11'd1480;
5'b01110:
tone<=11'd1542;
5'b01111:
tone<=11'd1570;
5'b10000:
tone<=11'd1622;
5'b10001:
tone<=11'd1668;
5'b10010:
tone<=11'd1690;
5'b10011:
tone<=11'd1728;
5'b10100:
tone<=11'd1764;
5'b10101:
tone<=11'd1795;
default:
;
endcase
end
always@(index)
begin
reg[4:
0]temp;
if(index>=15)
begin
temp<=index+2;
code<={1'b0,temp[2:
0]};
ledpos<=temp[4:
3];//高音显示
end
elseif(index>=8)
begin
temp<=index+1;
code<={1'b0,temp[2:
0]};
ledpos<=temp[4:
3];//中音显示
end
else
begin
temp<=index;
code<={1'b0,temp[2:
0]};
ledpos<=temp[4:
3];//低音显示
end
end
always@(code)
begin
case(code)//八段译码程序
4'd0:
sms=8'hC0;
4'd1:
sms=8'hF9;
4'd2:
sms=8'hA4;
4'd3:
sms=8'hB0;
4'd4:
sms=8'h99;
4'd5:
sms=8'h92;
4'd6:
sms=8'h82;
4'd7:
sms=8'hF8;
default:
sms=7'hC0;
endcase
end
always@(*)
begin
smb=8'b10000000;//选通第八位数码管
ledneg=4'b0111;//选通二极管
end
endmodule
(4)jianpu模块程序设计
modulejianpu(sel,clk,toneindex);
input[1:
0]sel;//乐曲选择信号
inputclk;
outputreg[4:
0]toneindex;//乐曲中的音符输出
wire[4:
0]toneindex1,toneindex2,toneindex3,toneindex4;
reg[9:
0]counter;//计数器,用于读取ROM中的简
谱
always@(posedgeclk)
begin
if(sel==2'b00)
begin
if(counter>=88)
counter<=8'd0;
else
counter<=counter+1;
end
elseif(sel==2'b01)
begin
if(counter>=128)
counter<=8'd0;
else
counter<=counter+1;
end
elseif(sel==2'b10)
begin
if(counter>=190)
counter<=8'd0;
else
counter<=counter+1;
end
elseif(sel==2'b11)
begin
if(counter>=416)
counter<=8'd0;
else
counter<=counter+1;
end
end
musicu1(.address(counter),.q(toneindex1),.clock(clk));
laohuu2(.address(counter),.q(toneindex2),.clock(clk));
tiankongu3(.address(counter),.q(toneindex3),.clock(clk));
gaosuniu4(.address(counter),.q(toneindex4),.clock(clk));
always@(*)
case(sel)
2'b00:
toneindex=toneindex1;//选择第一首歌--低、中、高音依
次循环
2'b01:
toneindex=toneindex2;//选择第二首歌--两只老虎
2'b10:
toneindex=toneindex3;//选择第三首歌--天空之城
2'b11:
toneindex=toneindex4;//选择第四首歌--快点告诉你
default:
;
endcase
endmodule
(5)lcd1602模块程序设计
modulelcd1602(clk,rst_n,lcd_data,lcd_e,lcd_rs,lcd_rw,SEL0,SEL1,SEL2,sel);
inputclk;//50MHz时钟
inputrst_n;//复位信号
input[1:
0]sel;
outputreg[7:
0]lcd_data;//数据总线
outputlcd_e;//使能信号
outputreglcd_rs;//指令、数据选择
outputlcd_rw;//读、写选择
outputSEL0;//LCD1602读写选择
outputSEL1;//LCD1602读写选择
outputSEL2;//LCD1602读写选择
reg[127:
0]row1_val;
always@(sel)
begin
case(sel)
2'b00:
row1_val="gaozhongdiyin";
2'b01:
row1_val="liangzhilaohu";
2'b10:
row1_val="tiankongzhicheng";
2'b11:
row1_val="kuaidiangaosuni";
default:
;
endcase
end
assignSEL0=1'b0;
assignSEL1=1'b0;
assignSEL2=1'b1;
//分频模块开始
reg[15:
0]cnt;//计数子
always@(posedgeclk,negedgerst_n)
if(!
rst_n)
cnt<=0;
else
cnt<=cnt+1'b1;
wirelcd_clk=cnt[15];
//分频模块结束
//LCD1602驱动模块开始
parameterIDLE=8'h00;
//写指令,初始化
parameterDISP_SET=8'h01;//显示模式设置
parameterDISP_OFF=8'h03;//显示关闭
parameterCLR_SCR=8'h02;//显示清屏
parameterCURSOR_SET1=8'h06;//显示光标移动设置
parameterCURSOR_SET2=8'h07;//显示开及光标设置
//显示第一行
parameterROW1_ADDR=8'h05;//写第1行起始地址
parameterROW1_0=8'h04;
parameterROW1_1=8'h0C;
parameterROW1_2=8'h0D;
parameterROW1_3=8'h0F;
parameterROW1_4=8'h0E;
parameterROW1_5=8'h0A;
parameterROW1_6=8'h0B;
parameterROW1_7=8'h09;
parameterROW1_8=8'h08;
parameterROW1_9=8'h18;
parameterROW1_A=8'h19;
parameterROW1_B=8'h1B;
parameterROW1_C=8'h1A;
parameterROW1_D=8'h1E;
parameterROW1_E=8'h1F;
parameterROW1_F=8'h1D;
//显示第二行
reg[5:
0]current_state,next_state;//现态、次态
//FSM:
always1
always@(posedgelcd_clk,negedgerst_n)
if(!
rst_n)current_state<=IDLE;
elsecurrent_state<=next_state;
//FSM:
always2
always
begin
case(current_state)
IDL