学习设计硬件乐曲演奏电路以及相关的控制电路。
组成乐曲的每个音符的发音频率值及其持续的时间是乐曲能连续演奏所需要的两个基本要素,问题是如何来获取这两个要素所对应的数值以及通过纯硬件的手段来利用这些数值实现乐曲的演奏效果。
如图1所示为乐曲硬件演奏的电路原理图。其中rom_liangzhu为歌曲“梁祝”部分音符数据产生器,cnt_1为地址发生器,decoder_1为初始值设置译码器,dvf为数控分频器,PLL2和PLL3为分频器,其中PLL2将10MHz的信号分频成为1MHz和2KHz的信号clk1M和clk2K,而PLL3将2KHz的信号分频成为4Hz的信号clk4Hz。
图1 实现乐曲演奏的电路原理图当一个4Hz的时钟脉冲来到时,rom_liangzhu模块输出一个音符数据q[3..0]给decoder_1模块,decoder_1模块就会输出此音符相应的计数初值到数控分频器模块dvf,当一个1MHz的时钟脉冲来到时,数控分频器模块就根据计数初值输出相应的频率给扬声器,扬声器就发出对应音符的声音来。连续的4Hz时钟脉冲就这样一个一个地把乐谱发生器中的音符传送到扬声器。
音符的频率由数控分频模块dvf获得,其由clk输入一具有较高频率(1MHz)的时钟,通过dvf分频由spks输出。Dvf首先根据输入的11位预置数tone[10..0]对1MHz信号分频,由于直接分频得出的信号是脉宽极窄的信号,为了有利于驱动扬声器,在dvf中另加一个2分频器以均衡占空比,但这时的频率将是原来的1/2。Spks的输出频率将决定每一音符的音调;这样decoder_1的预置数tone[10..0]与输出频率就有了对应关系,而输出的频率又与音乐音符的发声有对应关系。例如decoder_1模块中若取tone[10..0]=11’H305,将由spks发出音符为“5”音的信号频率392Hz。其中预置数tone[10..0]可由下面的公式来表示:
音乐频率对应列表如表1和图2所示,根据表1和公式1,可以算出不同音符的预置数。
音频 |
C(1) |
D(2) |
E(3) |
F(4) |
G(5) |
A(6) |
B(7) |
低音 |
131 |
147 |
165 |
175 |
196 |
220 |
247 |
中央C区 |
262 |
296 |
330 |
349 |
392 |
440 |
494 |
高音 |
523 |
587 |
659 |
698 |
784 |
880 |
988 |
高二度 |
1047 |
1175 |
1319 |
1397 |
1568 |
1760 |
1976 |
音符的持续时间需根据乐曲的速度及每个音符的节拍数来确定。模块decoder_1的功能首先是为模块dvf提供所发音符的分频预置数,而此数在模块dvf输入口停留的时间即为此音符的节拍周期。模块decoder_1是乐曲简谱码对应的分频预置数查表电路,decoder_1的分频预置数是根据公式(1)和表1得到的,程序中设置了乐曲全部音符所对应的分频预置数,每一音符的停留时间则由音乐节拍和音频发生查表模块rom_liangzhu中简谱码和工作时钟clk4Hz的频率(在此为4Hz)决定。
译码器decoder_1的主要功能是为每个音符设定初始值。如下为其部分代码,其中注释部分第一列为初始值代表的十进制数;第二列代表的是其音符简码值,从中央C区的sol音开始;第三列代表的是该音符相应的频率值。
module decoder_4(index,code,high,tone);//音符频率译码器
input [3:0] index;//输入音符
output [3:0] code;//输出当前音调
output high;//输出高电平,即高音
output [10:0] tone;//输出音频的频率
reg [10:0] tone;//音频的预制数的寄存器
reg [3:0] code;
reg high;
always@(index)
begin
case(index)//判断,音调从中央C区的sol音到高音区
4'd0:begin tone<=11'h7ff;code<=4'd0;high<=1'b0;end //2047-音频预制数
4'd1:begin tone<=11'h305;code<=4'd1;high<=1'b0;end //773-音频预制数 5-音调 392-音调频率
4'd2:begin tone<=11'h390;code<=4'd2;high<=1'b0;end //912 6 440
4'd3:begin tone<=11'h40c;code<=4'd3;high<=1'b0;end //1036 7 494
4'd4:begin tone<=11'h443;code<=4'd4;high<=1'b0;end //1091 1 523
4'd5:begin tone<=11'h4ac;code<=4'd5;high<=1'b0;end //1196 2 587
4'd6:begin tone<=11'h50a;code<=4'd6;high<=1'b0;end //1290 3 659
4'd7:begin tone<=11'h55c;code<=4'd7;high<=1'b0;end //1332 4 698
4'd8:begin tone<=11'h582;code<=4'd1;high<=1'b1;end //1410 5 784
4'd9:begin tone<=11'h5c8;code<=4'd2;high<=1'b1;end //1480 6 880
4'd10:begin tone<=11'h606;code<=4'd3;high<=1'b1;end //1542 7 988
4'd11:begin tone<=11'h622;code<=4'd4;high<=1'b1;end //1570 1 1047
4'd12:begin tone<=11'h656;code<=4'd5;high<=1'b1;end //1622 2 1175
4'd13:begin tone<=11'h684;code<=4'd6;high<=1'b1;end //1668 3 1319
4'd14:begin tone<=11'h699;code<=4'd7;high<=1'b1;end //1689 4 1397
4'd15:begin tone<=11'h6c0;code<=4'd1;high<=1'b1;end //1728 5 1568
default:begin tone<=11'h7ff;code<=4'd0;high<=1'b0;end //2074
endcase
end
endmodule
模块counter_1是一个8位二进制计数器,内部设置计数最大值为139,作为music的地址发生器,这个计数器的计数时钟即为4Hz。即每一计数的停留时间为0.25秒,恰为当全音符设为1秒时,四四拍的4分音符的持续时间。例如,“梁祝”乐曲的第一个音符为“3”,此音在逻辑中停留了4个时钟节拍,即1秒时间,相应地,“3”音符在ROM中占4个存储位置,预置数为11’H40C,在dvf的输入端停留了1秒。随着计数器counter_1按4Hz的时钟频率作加法计数,即随着地址值递增时,music中ROM_liangzhu模块的音符数据通过q[3..0]端口输向decoder_1模块,“梁祝”乐曲就开始自然地演奏起来了。counter_1的节拍是139,正好等于ROM中的简谱码数,所以可以确保循环演奏。对于其他乐曲,此计数最大值要根据情况更改。
module cnt_3(clk4hz,clk6hz,q,d);//计数器模块
input clk4hz;//输入4hz时钟
input clk6hz;//输入6hz时钟
output [7:0] q;//输出4hz的计数值
output[7:0] d;//输出6hz的计数值
reg [7:0] count;//
reg [7:0] i;//
always@(posedge clk4hz)//梁祝模块
begin
if(count==8'b10001010) //计数到138,然后进行清零
count<=0;
else
count<=count+1;
end
always@(posedge clk6hz)//其它模块
begin
if(i==8'b11111111) //计数到255,然后进行清零
i<=0;
else
i<=i+1;
end
assign q=count;//输出计数值,方便观察计数变化
assign d=i;//
endmodule
Music中音符数据rom_liangzhu中的内容根据演奏乐曲而定,下面图3所示ROM中内容是“梁祝”中的一段。
图3 根据梁祝乐曲的ROM数据数控分频模块dvf的主要功能是根据预置数值,得出其对应的输出频率。因为该例子在数控分频器中实践过,不做过多分析。
module dvf_5(tone,clk1m,spks,Full);//数控分频器
input clk1m;//输入1Mhz时钟
input [10:0] tone;//输入音频的预制数
output reg spks;//发声音调
reg [10:0] reg11;//11位寄存器
reg full;//二分频寄存器
output Full;
always@(posedge clk1m)//上升沿有效
begin
if(reg11==11'h7ff) begin reg11<=tone;full<=1'b1;end//根据不同的预制数开始计数
else begin reg11<=reg11+11'b1;full<=1'b0;end//若未计数到2047,则继续计数,并将full取0
end
always@(posedge full)//二分频器,均衡其占空比,即频率变为原来的1/2
begin
spks<=~spks;//取反,输出乐曲音频
end
assign Full=full;//输出full的变化
endmodule
分频模块PLL1将10MHz的信号分频成为1MHz和2KHz的信号clk1M和clk2K,而PLL2将2KHz的信号分频成为4Hz的信号clk4Hz。因为分频器的设计在数控分频器中实践过,不做过多分析。
module PLL_1(clk10m,clk2k,clk1m,M,K);//fenpinqi
input clk10m;
output clk2k,clk1m;
reg clk2k,clk1m;
reg [11:0] i;//2k_couter
reg [3:0] j;//1M_counter
output [11:0] K;//2k_couter_out
output [3:0] M;//1M_counter_out
always@(posedge clk10m )//2KHz,5000_fenpin
begin
if(i==12'b100111000011) begin i<=0;clk2k<=~clk2k;end//from_0_count_to_2499
else i<=i+1;
end
always@(posedge clk10m )//1MHz,10_fenpin
begin
if(j==4'b0100)begin j<=0;clk1m<=~clk1m;end//from_0_count_to_4
else j<=j+1;
end
assign K=i;
assign M=j;
endmodule
module PLL_2(clk2k,clk4hz,clk6hz,F);//2kHz->4Hz,500_fenpin
input clk2k;
output clk4hz;
output clk6hz;
reg [7:0] j;//4Hz_count
reg [7:0] i;
reg clk4hz;
reg clk6hz;
output [7:0] F;//4Hz_count_out
always@(posedge clk2k )//4Hz,500_fenpin
begin
if(j==8'b11111001)begin j<=0;clk4hz<=~clk4hz;end//from_0_count_to_249
else j<=j+1;
end
always@(posedge clk2k )//5Hz,yuedeng333_fenpin
begin
if(i==8'b10100101)begin i<=0;clk6hz<=~clk6hz;end//from_0_count_to_165
else i<=i+1;
end
assign F=j;
endmodule
定制音符数据ROM music。该ROM数据如图3所示。
写出cnt_1、decoder_1和dvf三个模块并进行仿真,进一步确认decoder_1中音符预置数的精确性,因为这些数据决定了音准。
完成系统仿真调试和硬件验证。
模式选择,主要选有数码管、按键、led灯的模式即可。
引脚锁定:
在模块music中填入新的乐曲。针对新乐曲的曲长和节拍情况改变模块counter_1的计数长度(注意,一个计数值就是一个1/4拍)。
【其余乐曲文件资源已上传,需要自行到主页下载】
争取增加多个ROM模块,分别装上不同的歌曲,手动或自动选择歌曲,如下图4所示为可实现四首歌演唱的音乐盒顶层设计。
【如图1】