做的是一个简陋的按键电子琴模块,并且用的笨办法,很笨蛋,但有效。
一、来看看实验要求:
通过按键控制蜂鸣器鸣响,编写Verilog程序,对基本时钟源(20MHz)进行分频,以产生如表1所示的各个频率信号。表1中列出的频率共有21个,分别与低音、中音、高音3个频段下的不同音调相对应(每个频段下有7个音调)。通过按键产生相应的序列频率信号,并发送到蜂鸣器,可演奏出音乐。
使用主板上的8个白色独立按键(“键1”~“键8”)模拟琴键。通过“键8”来选择高、中、低音三个频段,并由其余7个按键选择该频段下7个不同的音阶。按下“按键8”实现频段由频段1到频段3的循环切换。数码管“数码8”即时显示当前频段值(1,2或3)。当按下不同的音阶时,7个数码管“数码1”至“数码7”也即时显示当前的音阶值。
二、在编写程序前需要明确的事情:
1.本次的代码实现用了分模块的思路,虽然分得很烂且分了跟分得太散,但是也算是分模块吧。
2.本次实际分了6个模块,分别是顶层模块piano,分模块5个,每个分模块的作用在下面有注释,且所有代码都注释标好了,纯傻瓜式注释,看不懂打我(bushi)。
3,分模块中有需要注意的一个点就是PLL锁相环模块,需要读者自己使用IP核进行设置,需要把核心板时钟20MHZ分频成5MHZ引出来作为音阶频率的计算基准源,分频的21个系数如下:
//算出高中低音对应7个音阶各自的分频系数,
parameter MIN_DO = 15'd19080,//(5_000_000/262)
MIN_RE = 15'd17006,//(5_000_000/294)
MIN_MI = 15'd15150,//(5_000_000/330)
MIN_FA = 15'd14326,//(5_000_000/349)
MIN_SO = 15'd12756,//(5_000_000/392)
MIN_LA = 15'd11360,//(5_000_000/440)
MIN_XI = 15'd10120;//(5_000_000/494)
//中音
parameter MID_DO = 15'd9560,//(5_000_000/523)
MID_RE = 15'd8516,//(5_000_000/587)
MID_MI = 15'd7586,//(5_000_000/659)
MID_FA = 15'd7160,//(5_000_000/698)
MID_SO = 15'd6376,//(5_000_000/784)
MID_LA = 15'd5680,//(5_000_000/880)
MID_XI = 15'd5060;//(5_000_000/988)
//高音
parameter MAX_DO = 15'd4776,//(5_000_000/1047)
MAX_RE = 15'd4256,//(5_000_000/1175)
MAX_MI = 15'd3790,//(5_000_000/1319)
MAX_FA = 15'd3580,//(5_000_000/1397)
MAX_SO = 15'd3190,//(5_000_000/1568)
MAX_LA = 15'd2842,//(5_000_000/1760)
MAX_XI = 15'd2542;//(5_000_000/1967)
4.分模块中音阶频率的实现我使用了傻瓜式代码操作,就是重复了21次同个分频模块,浪费了21个寄存器计数的笨方法。
clk_5m_o就是锁相环输出的5mhz频率计算基准源
always@(posedge clk_5m_o or negedge rst)
begin
if(!rst)//复位信号,预置0
begin
count1<=0;
//count1是第一个用来计数的寄存器,同样作用的寄存器还有20个,
clk_h0<=0; //clk_h0是最低音的第一个音阶频率输出;
//(本来以h命名该是最高音的,但我不小心一开始编程的时候就把h和l搞反了
end
else begin //MIN_DO是低音第一个音阶的分频系数,
if(count1==((MIN_DO>>1)-1'b1))begin count1<=0;clk_h0<=~clk_h0;end
else count1 <=count1+1;//
end
end
//同样的模块还有20个,每次要把count1和clk_h0,还有MIN_DO都替换成对应的正确内容
三、以下是全部代码:文章来源:https://www.toymoban.com/news/detail-755686.html
注:引脚绑定看用什么板,需要自己查阅试验箱说明书。文章来源地址https://www.toymoban.com/news/detail-755686.html
/*
时间:2023/5/14
作者:黄
作用:制作一个电子琴,通过按键输入演奏谱曲;
*/
module piano(//顶层模块
clk_i,
rst,
p8_i,
ps_i,//输入
speaker_o,
bt1,
bt2,
led8,
seg
);
input clk_i,//时钟信号
rst,//复位信号
p8_i;//第八个按键输入
input[6:0] ps_i;//7个按键输入
//需要注意的是,按键输入都是上升沿触发,
output speaker_o;//蜂鸣器输出
output [6:0] bt2;
output bt1;
output [3:0]led8;//duanxuan
output[27:0] seg;
wire[1:0] to_o;//第八个数码管显示显示高中低音
wire clk_5m_o;
wire clk_h0,//h代表高音,m代表中音,l代表低音,0-7代表音阶
clk_h1,//要注意的是,我一开始编程的时候搞反了,所以代码实际实现的时候h代表低音,l代表高音
clk_h2,
clk_h3,
clk_h4,
clk_h5,
clk_h6,
clk_m0,
clk_m1,
clk_m2,
clk_m3,
clk_m4,
clk_m5,
clk_m6,
clk_l0,
clk_l1,
clk_l2,
clk_l3,
clk_l4,
clk_l5,
clk_l6;
clk_div clk_div(
.clk_i(clk_i),
.rst(rst),
.clk_h0(clk_h0),//h代表高音,m代表中音,l代表低音,0-7代表音阶
.clk_h1(clk_h1),//要注意的是,我一开始编程的时候搞反了,所以代码实际实现的时候h代表低音,l代表高音
.clk_h2(clk_h2),
.clk_h3(clk_h3),
.clk_h4(clk_h4),
.clk_h5(clk_h5),
.clk_h6(clk_h6),
.clk_m0(clk_m0),
.clk_m1(clk_m1),
.clk_m2(clk_m2),
.clk_m3(clk_m3),
.clk_m4(clk_m4),
.clk_m5(clk_m5),
.clk_m6(clk_m6),
.clk_l0(clk_l0),
.clk_l1(clk_l1),
.clk_l2(clk_l2),
.clk_l3(clk_l3),
.clk_l4(clk_l4),
.clk_l5(clk_l5),
.clk_l6(clk_l6),
.clk_5m_o(clk_5m_o)
);
//第二个模块,获取第八个按键输入,并输出
board_input board_input(
.rst(rst),//复位
.p8_i(p8_i),//第八个按键,用来制作选择高中低
.to_o(to_o),//输出数据
);
//第三个模块,输出8个数码管的内容并显示在实验板上
seg_display seg_display(
.to_o(to_o),//数码管八的内容
.ps_i(ps_i),//七个按键输入
.led8(led8),//单独输出第八个数码管内容
.bt1(bt1),//bt1 bt2是位选,选择输出哪个数码管
.bt2(bt2),//
.seg(seg)//同步输出七个数码管内容,同一时间只能按一个
);
selct_speaker selct_speaker_U5(
.clk_5m_o(clk_5m_o),//5m时钟信号输入,来源于第一个时钟模块
.rst(rst),
.to_o(to_o),//输入高中低音选择
.ps_i(ps_i),//循环扫描p1-p7这7个按键;
.clk_h0(clk_h0),//输入所有音阶分频频率,h代表高音,m代表中音,l代表低音,0-7代表音阶
.clk_h1(clk_h1),//要注意的是,我一开始编程的时候搞反了,所以代码实际实现的时候h代表低音,l代表高音
.clk_h2(clk_h2),
.clk_h3(clk_h3),
.clk_h4(clk_h4),
.clk_h5(clk_h5),
.clk_h6(clk_h6),
.clk_m0(clk_m0),
.clk_m1(clk_m1),
.clk_m2(clk_m2),
.clk_m3(clk_m3),
.clk_m4(clk_m4),
.clk_m5(clk_m5),
.clk_m6(clk_m6),
.clk_l0(clk_l0),
.clk_l1(clk_l1),
.clk_l2(clk_l2),
.clk_l3(clk_l3),
.clk_l4(clk_l4),
.clk_l5(clk_l5),
.clk_l6(clk_l6),
.speaker_o(speaker_o)//蜂鸣器输出
);
endmodule
module clk_div(
clk_i,
rst,
clk_h0,//h代表高音,m代表中音,l代表低音,0-7代表音阶
clk_h1,//要注意的是,我一开始编程的时候搞反了,所以代码实际实现的时候h代表低音,l代表高音
clk_h2,
clk_h3,
clk_h4,
clk_h5,
clk_h6,
clk_m0,
clk_m1,
clk_m2,
clk_m3,
clk_m4,
clk_m5,
clk_m6,
clk_l0,
clk_l1,
clk_l2,
clk_l3,
clk_l4,
clk_l5,
clk_l6,
clk_5m_o//5m输出
);
input clk_i,rst;
output clk_5m_o;
output reg clk_h0,//h代表高音,m代表中音,l代表低音,0-7代表音阶
clk_h1,//要注意的是,我一开始编程的时候搞反了,所以代码实际实现的时候h代表低音,l代表高音
clk_h3,
clk_h4,
clk_h5,
clk_h6,
clk_m0,
clk_m1,
clk_m2,
clk_m3,
clk_m4,
clk_m5,
clk_m6,
clk_l0,
clk_l1,
clk_l2,
clk_l3,
clk_l4,
clk_l5,
clk_l6;
//算出高中低音对应7个音阶各自的分频系数,
parameter MIN_DO = 15'd19080,//(5_000_000/262)
MIN_RE = 15'd17006,//(5_000_000/294)
MIN_MI = 15'd15150,//(5_000_000/330)
MIN_FA = 15'd14326,//(5_000_000/349)
MIN_SO = 15'd12756,//(5_000_000/392)
MIN_LA = 15'd11360,//(5_000_000/440)
MIN_XI = 15'd10120;//(5_000_000/494)
//中音
parameter MID_DO = 15'd9560,//(5_000_000/523)
MID_RE = 15'd8516,//(5_000_000/587)
MID_MI = 15'd7586,//(5_000_000/659)
MID_FA = 15'd7160,//(5_000_000/698)
MID_SO = 15'd6376,//(5_000_000/784)
MID_LA = 15'd5680,//(5_000_000/880)
MID_XI = 15'd5060;//(5_000_000/988)
//高音
parameter MAX_DO = 15'd4776,//(5_000_000/1047)
MAX_RE = 15'd4256,//(5_000_000/1175)
MAX_MI = 15'd3790,//(5_000_000/1319)
MAX_FA = 15'd3580,//(5_000_000/1397)
MAX_SO = 15'd3190,//(5_000_000/1568)
MAX_LA = 15'd2842,//(5_000_000/1760)
MAX_XI = 15'd2542;//(5_000_000/1967)
//使用21个寄存器计数
reg [14:0]count1;
reg [14:0]count2;
reg [14:0]count3 ;
reg [14:0]count4 ;
reg [14:0]count5 ;
reg [14:0]count6 ;
reg [14:0]count7 ;
reg [14:0]count11 ;
reg [14:0]count12;
reg [14:0]count13;
reg [14:0]count14 ;
reg [14:0]count15 ;
reg [14:0]count16 ;
reg [14:0]count17 ;
reg [14:0]count21 ;
reg [14:0]count22 ;
reg [14:0]count23 ;
reg [14:0]count24 ;
reg [14:0]count25 ;
reg [14:0]count26 ;
reg [14:0]count27 ;
//引用锁相环模块,做出5mhz输出
PLL PLL_U1(
.inclk0 (clk_i),
.c0 ( clk_5m_o),
.locked ( locked_sig )
);
//分频模块,重复21次,替换上每个模块对应的分频系数和计数寄存器count
always@(posedge clk_5m_o or negedge rst)
begin
if(!rst)
begin
count1<=0;
clk_h0<=0;
end
else begin
if(count1==((MIN_DO>>1)-1'b1))begin count1<=0;clk_h0<=~clk_h0;end
else count1 <=count1+1;
end
end
always@(posedge clk_5m_o or negedge rst)
begin
if(!rst)
begin
count2<=0;
clk_h1<=0;
end
else begin
if(count2==((MIN_RE>>1)-1'b1))begin count2<=0;clk_h1<=~clk_h1;end
else count2 <=count2+1;
end
end
always@(posedge clk_5m_o or negedge rst)
begin
if(!rst)
begin
count3<=0;
clk_h2<=0;
end
else begin
if(count3==((MIN_MI>>1)-1'b1))begin count3<=0;clk_h2=~clk_h2;end
else count3 <=count3+1;
end
end
always@(posedge clk_5m_o or negedge rst)
begin
if(!rst)
begin
count4<=0;
clk_h3<=0;
end
else begin
if(count4==((MIN_FA>>1)-1'b1))begin count4<=0;clk_h3=~clk_h3;end
else count4 <=count4+1;
end
end
always@(posedge clk_5m_o or negedge rst)
begin
if(!rst)
begin
count5<=0;
clk_h4<=0;
end
else begin
if(count5==((MIN_SO>>1)-1'b1))begin count5<=0;clk_h4=~clk_h4;end
else count5 <=count5+1;
end
end
always@(posedge clk_5m_o or negedge rst)
begin
if(!rst)
begin
count6<=0;
clk_h5<=0;
end
else begin
if(count6==((MIN_LA>>1)-1'b1))begin count6<=0;clk_h5=~clk_h5;end
else count6 <=count6+1;
end
end
always@(posedge clk_5m_o or negedge rst)
begin
if(!rst)
begin
count7<=0;
clk_h6<=0;
end
else begin
if(count7==((MIN_XI>>1)-1'b1))begin count7<=0;clk_h6=~clk_h6;end
else count7 <=count7+1;
end
end
always@(posedge clk_5m_o or negedge rst)
begin
if(!rst)
begin
count11<=0;
clk_m0<=0;
end
else begin
if(count11==((MID_DO>>1)-1'b1))begin count11<=0;clk_m0=~clk_m0;end
else count11 <=count11+1;
end
end
always@(posedge clk_5m_o or negedge rst)
begin
if(!rst)
begin
count12<=0;
clk_m1<=0;
end
else begin
if(count12==((MID_RE>>1)-1'b1))begin count12<=0;clk_m1=~clk_m1;end
else count12 <=count12+1;
end
end
always@(posedge clk_5m_o or negedge rst)
begin
if(!rst)
begin
count13<=0;
clk_m2<=0;
end
else begin
if(count13==((MID_MI>>1)-1'b1))begin count13<=0;clk_m2=~clk_m2;end
else count13 <=count13+1;
end
end
always@(posedge clk_5m_o or negedge rst)
begin
if(!rst)
begin
count14<=0;
clk_m3<=0;
end
else begin
if(count14==((MID_FA>>1)-1'b1))begin count14<=0;clk_m3=~clk_m3;end
else count14 <=count14+1;
end
end
always@(posedge clk_5m_o or negedge rst)
begin
if(!rst)
begin
count15<=0;
clk_m4<=0;
end
else begin
if(count15==((MID_SO>>1)-1'b1))begin count15<=0;clk_m4=~clk_m4;end
else count15 <=count15+1;
end
end
always@(posedge clk_5m_o or negedge rst)
begin
if(!rst)
begin
count16<=0;
clk_m5<=0;
end
else begin
if(count16==((MID_LA>>1)-1'b1))begin count16<=0;clk_m5=~clk_m5;end
else count16 <=count16+1;
end
end
always@(posedge clk_5m_o or negedge rst)
begin
if(!rst)
begin
count17<=0;
clk_m6<=0;
end
else begin
if(count17==((MID_XI>>1)-1'b1))begin count17<=0;clk_m6=~clk_m6;end
else count17 <=count17+1;
end
end
always@(posedge clk_5m_o or negedge rst)
begin
if(!rst)
begin
count21<=0;
clk_l0<=0;
end
else begin
if(count21==((MAX_DO>>1)-1'b1))begin count21<=0;clk_l0=~clk_l0;end
else count21 <=count21+1;
end
end
always@(posedge clk_5m_o or negedge rst)
begin
if(!rst)
begin
count22<=0;
clk_l1<=0;
end
else begin
if(count22==((MAX_RE>>1)-1'b1))begin count22<=0;clk_l1=~clk_l1;end
else count22 <=count22+1;
end
end
always@(posedge clk_5m_o or negedge rst)
begin
if(!rst)
begin
count23<=0;
clk_l2<=0;
end
else begin
if(count23==((MAX_MI>>1)-1'b1))begin count23<=0;clk_l2=~clk_l2;end
else count23 <=count23+1;
end
end
always@(posedge clk_5m_o or negedge rst)
begin
if(!rst)
begin
count24<=0;
clk_l3<=0;
end
else begin
if(count24==((MAX_FA>>1)-1'b1))begin count24<=0;clk_l3=~clk_l3;end
else count24 <=count24+1;
end
end
always@(posedge clk_5m_o or negedge rst)
begin
if(!rst)
begin
count25<=0;
clk_l4<=0;
end
else begin
if(count25==((MAX_SO>>1)-1'b1))begin count25<=0;clk_l4=~clk_l4;end
else count25 <=count25+1;
end
end
always@(posedge clk_5m_o or negedge rst)
begin
if(!rst)
begin
count26<=0;
clk_l5<=0;
end
else begin
if(count26==((MAX_LA>>1)-1'b1))begin count26<=0;clk_l5=~clk_l5;end
else count26 <=count26+1;
end
end
always@(posedge clk_5m_o or negedge rst)
begin
if(!rst)
begin
count27<=0;
clk_l6<=0;
end
else begin
if(count27==((MAX_XI>>1)-1'b1))begin count27<=0;clk_l6=~clk_l6;end
else count27 <=count27+1;
end
end
endmodule
module board_input(
rst,//复位
p8_i,//第八个按键,用来制作选择高中低
to_o,//输出第八位的数据
);
//
parameter s1=2'b01,//1
s2=2'b10,//2
s3=2'b11;//3
//input rst;
input rst;
input p8_i;
output reg [1:0] to_o;
always@(posedge p8_i or negedge rst)
begin
if(!rst)begin
to_o<=s1;
end
else
begin
if(to_o==s1)to_o<=s2;
else if(to_o==s2)to_o<=s3;
else to_o=s1;
end
end
endmodule
module seg_display(
rst,//复位
to_o,//第八个数码管显示内容
ps_i,//七个按键输入
led8,//单独输出第八个数码管内容
bt1,
bt2,//wei xuan
seg//同步输出七个数码管内容,同一时间只能按一个
);
//parameter定义s1-s3三个状态,转换成十进制是1 2 3,
parameter s0=2'b00,//0
s1=2'b01,//1
s2=2'b10,//2
s3=2'b11;
input rst;
input[1:0] to_o;
input[6:0] ps_i;
output reg [6:0] bt2;
output reg bt1;
output reg [3:0]led8;//duanxuan
output reg [27:0]seg;//duanxuan
//对七个数码管通过七个按键输入控制需要显示的数码管
always@(posedge ps_i or posedge rst )//always模块,同步复位
begin
if(rst)
begin
bt2<=7'b0000000;
bt1<=0;
end
else
begin
bt1<=1;
case(ps_i)
7'b0000001:begin bt2<=7'b0000001;end
7'b0000010:begin bt2<=7'b0000010;end
7'b0000100:begin bt2<=7'b0000100;end
7'b0001000:begin bt2<=7'b0001000;end
7'b0010000:begin bt2<=7'b0010000;end
7'b0100000:begin bt2<=7'b0100000;end
7'b1000000:begin bt2<=7'b1000000;end
default:begin bt2<=bt2;end
endcase
end
end
//对七个数码管通过七个按键输入控制需要显示的数码管的对应内容
always@(posedge ps_i or negedge rst )
begin
case(ps_i)
7'b0000001:begin seg[3:0]<=4'b0001;end
7'b0000010:begin seg[7:4]<=4'b0010;end
7'b0000100:begin seg[11:8]<=4'b0011;end
7'b0001000:begin seg[15:12]<=4'b0100;end
7'b0010000:begin seg[19:16]<=4'b0101;end
7'b0100000:begin seg[23:20]<=4'b0110;end
7'b1000000:begin seg[27:24]<=4'b0111;end
default:begin seg[27:0]<=0;end
endcase
end
//需要额外申明的是,数码管已经有了译码器,只需要输入4位二进制就能显示0-f的全部数字
//通过对to_o的判断,判断第八个数码管要输出什么内容
always@(posedge to_o or negedge rst)
begin
case(to_o)
s1:led8=4'b0001;
s2:led8=4'b0010;
s3:led8=4'b0011;
default :led8=4'b0001;
endcase
end
endmodule
module selct_speaker
(
clk_5m_o,
rst,
to_o,
ps_i,
clk_h0,//h代表高音,m代表中音,l代表低音,0-7代表音阶
clk_h1,//要注意的是,我一开始编程的时候搞反了,所以代码实际实现的时候h代表低音,l代表高音
clk_h2,
clk_h3,
clk_h4,
clk_h5,
clk_h6,
clk_m0,
clk_m1,
clk_m2,
clk_m3,
clk_m4,
clk_m5,
clk_m6,
clk_l0,
clk_l1,
clk_l2,
clk_l3,
clk_l4,
clk_l5,
clk_l6,
speaker_o//蜂鸣器输出
);
input clk_5m_o,rst;
input[1:0] to_o;
input[6:0] ps_i;
reg [6:0]be_o;
input clk_h0,//h代表高音,m代表中音,l代表低音,0-7代表音阶
clk_h1,//要注意的是,我一开始编程的时候搞反了,所以代码实际实现的时候h代表低音,l代表高音
clk_h2,
clk_h3,
clk_h4,
clk_h5,
clk_h6,
clk_m0,
clk_m1,
clk_m2,
clk_m3,
clk_m4,
clk_m5,
clk_m6,
clk_l0,
clk_l1,
clk_l2,
clk_l3,
clk_l4,
clk_l5,
clk_l6;
output reg speaker_o;
parameter s0=2'b00,//0
s1=2'b01,//1
s2=2'b10,//2
s3=2'b11;
//always模块通过判断to_o判断高中低音,再通过每个case判断选择输出什么频率
always@(posedge ps_i or posedge rst)
begin
if(rst)speaker_o<=0;
else begin
if(to_o==s1)begin
case(ps_i)
7'b0000001:speaker_o<=clk_h0;
7'b0000010:speaker_o<=clk_h1;
7'b0000100:speaker_o<=clk_h2;
7'b0001000:speaker_o<=clk_h3;
7'b0010000:speaker_o<=clk_h4;
7'b0100000:speaker_o<=clk_h5;
7'b1000000:speaker_o<=clk_h6;
default:speaker_o<=speaker_o;
endcase
end
else if(to_o==s2)begin
case(ps_i)
7'b0000001:speaker_o<=clk_m0;
7'b0000010:speaker_o<=clk_m1;
7'b0000100:speaker_o<=clk_m2;
7'b0001000:speaker_o<=clk_m3;
7'b0010000:speaker_o<=clk_m4;
7'b0100000:speaker_o<=clk_m5;
7'b1000000:speaker_o<=clk_m6;
default:speaker_o<=speaker_o;
endcase
end
else if(to_o==s3)begin
case(ps_i)
7'b0000001:speaker_o<=clk_l0;
7'b0000010:speaker_o<=clk_l1;
7'b0000100:speaker_o<=clk_l2;
7'b0001000:speaker_o<=clk_l3;
7'b0010000:speaker_o<=clk_l4;
7'b0100000:speaker_o<=clk_l5;
7'b1000000:speaker_o<=clk_l6;
default:speaker_o<=speaker_o;
endcase
end
end
end
endmodule
到了这里,关于FPGA复杂数字系统设计1(数字电子琴)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!