赞
踩
做的是一个简陋的按键电子琴模块,并且用的笨办法,很笨蛋,但有效。
一、来看看实验要求:
通过按键控制蜂鸣器鸣响,编写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都替换成对应的正确内容

三、以下是全部代码:
注:引脚绑定看用什么板,需要自己查阅试验箱说明书。
/* 时间: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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。