项目需求:
-
通过PWM产生不同的音调,并驱动板上蜂鸣器将音调输出
-
能够播放三首不同的曲子,每个曲子的时间长度为1分钟,可以切换播放
-
曲子的切换使用小脚丫核心板上的按键,需要有按键消抖的功能
-
播放的曲子的名字在OLED屏幕上显示出来(汉字显示)
设计思路:
管脚设计:
资源报告:
Total CPU Time: 1 secs
Total REAL Time: 2 secs
Peak Memory Usage: 46 MB
使用模块:
- OLED模块(v) (显示时间和汉字歌名)
- music顶层模块(v) (将各个模块联系起来,并输出音乐到蜂鸣器)
- 消抖模块 (v) (防止误判进行按键消抖,给予一个延时)
各个模块介绍:
OLED模块(v)
OLED模块是最重要的一个模块之一,也是我花了许久才完成的。这个模块也是我看了硬禾学堂给的例程之后想出来的,通过模仿硬禾学堂的程序,写出了我需要使用的OLED显示模块代码。
成果如下:
下面是核心代码:
MAIN:begin
case(qu)
2'd0:begin
if(cnt_main >= 5'd14) cnt_main <= 5'd15;
else cnt_main <= cnt_main + 1'b1;
case(cnt_main)
5'd0 : begin state <= INIT; end
5'd1 : begin y_p <= 8'hb0; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd2 : begin y_p <= 8'hb1; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd3 : begin y_p <= 8'hb2; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd4 : begin y_p <= 8'hb3; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd5 : begin y_p <= 8'hb4; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd6 : begin y_p <= 8'hb5; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd7 : begin y_p <= 8'hb6; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd8 : begin y_p <= 8'hb7; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd9 : begin y_p <= 8'hb0; x_ph <= 8'h10; x_pl <= 8'h00; mem_hanzi_num <= 8'd0; state <= CHINESE; end
5'd10: begin y_p <= 8'hb0; x_ph <= 8'h11; x_pl <= 8'h00; mem_hanzi_num <= 8'd2; state <= CHINESE; end
5'd11: begin y_p <= 8'hb0; x_ph <= 8'h12; x_pl <= 8'h00; mem_hanzi_num <= 8'd4; state <= CHINESE; end
5'd12: begin y_p <= 8'hb0; x_ph <= 8'h13; x_pl <= 8'h00; mem_hanzi_num <= 8'd6; state <= CHINESE; end
5'd13: begin y_p <= 8'hb0; x_ph <= 8'h14; x_pl <= 8'h00; mem_hanzi_num <= 8'd8; state <= CHINESE; end
5'd14: begin y_p <= 8'hb0; x_ph <= 8'h15; x_pl <= 8'h00; mem_hanzi_num <= 8'd10; state <= CHINESE; end
endcase
end
2'd1:begin
if(cnt_main >= 5'd12) cnt_main <= 5'd13;
else cnt_main <= cnt_main + 1'b1;
case(cnt_main)
5'd0 : begin state <= INIT; end
5'd1 : begin y_p <= 8'hb0; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd2 : begin y_p <= 8'hb1; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd3 : begin y_p <= 8'hb2; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd4 : begin y_p <= 8'hb3; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd5 : begin y_p <= 8'hb4; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd6 : begin y_p <= 8'hb5; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd7 : begin y_p <= 8'hb6; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd8 : begin y_p <= 8'hb7; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd9 : begin y_p <= 8'hb0; x_ph <= 8'h12; x_pl <= 8'h00; mem_hanzi_num <= 8'd12; state <= CHINESE; end
5'd10: begin y_p <= 8'hb0; x_ph <= 8'h13; x_pl <= 8'h00; mem_hanzi_num <= 8'd14; state <= CHINESE; end
5'd11: begin y_p <= 8'hb0; x_ph <= 8'h14; x_pl <= 8'h00; mem_hanzi_num <= 8'd16; state <= CHINESE; end
5'd12: begin y_p <= 8'hb0; x_ph <= 8'h15; x_pl <= 8'h00; mem_hanzi_num <= 8'd18; state <= CHINESE; end
endcase
end
2'd2:begin
if(cnt_main >= 5'd13) cnt_main <= 5'd14;
else cnt_main <= cnt_main + 1'b1;
case(cnt_main)
5'd0 : begin state <= INIT; end
5'd1 : begin y_p <= 8'hb0; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd2 : begin y_p <= 8'hb1; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd3 : begin y_p <= 8'hb2; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd4 : begin y_p <= 8'hb3; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd5 : begin y_p <= 8'hb4; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd6 : begin y_p <= 8'hb5; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd7 : begin y_p <= 8'hb6; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd8 : begin y_p <= 8'hb7; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd9 : begin y_p <= 8'hb0; x_ph <= 8'h11; x_pl <= 8'h00; mem_hanzi_num <= 8'd20; state <= CHINESE; end
5'd10: begin y_p <= 8'hb0; x_ph <= 8'h12; x_pl <= 8'h00; mem_hanzi_num <= 8'd22; state <= CHINESE; end
5'd11: begin y_p <= 8'hb0; x_ph <= 8'h13; x_pl <= 8'h00; mem_hanzi_num <= 8'd24; state <= CHINESE; end
5'd12: begin y_p <= 8'hb0; x_ph <= 8'h14; x_pl <= 8'h00; mem_hanzi_num <= 8'd26; state <= CHINESE; end
5'd13: begin y_p <= 8'hb0; x_ph <= 8'h15; x_pl <= 8'h00; mem_hanzi_num <= 8'd28; state <= CHINESE; end
endcase
end
//default: state <= IDLE; //如果你需要动态刷新一些信息,此行应该取消注释
endcase
end
要切换歌曲时,每按下一次按键,改变寄存器qu的值,从而使其更改显示歌曲
接下来就是字库,这里我输入了十五个汉字:友,谊,地,久,天,长,两,只,老,虎 ,金,森,狂,想,曲 .
mem_hanzi[ 0 ] = {8'h08,8'h08,8'h08,8'h08,8'hC8,8'h78,8'hCF,8'h48,8'h48,8'h48,8'h48,8'hC8,8'h08,8'h08,8'h08,8'h00};
mem_hanzi[ 1 ] = {8'h10,8'h88,8'h84,8'h43,8'h40,8'h20,8'h21,8'h16,8'h08,8'h14,8'h22,8'h41,8'h40,8'h80,8'h80,8'h00};/*"友",0*/
mem_hanzi[ 2 ] = {8'h40,8'h40,8'h42,8'hCC,8'h00,8'h10,8'h0C,8'hE4,8'h24,8'h25,8'h26,8'h24,8'hE4,8'h14,8'h0C,8'h00};
mem_hanzi[ 3 ] = {8'h00,8'h00,8'h00,8'h3F,8'h10,8'h48,8'h40,8'h7F,8'h49,8'h49,8'h49,8'h49,8'h7F,8'h40,8'h40,8'h00};/*"谊",1*/
mem_hanzi[ 4 ] = {8'h20,8'h20,8'h20,8'hFF,8'h20,8'h20,8'h80,8'hF8,8'h80,8'h40,8'hFF,8'h20,8'h10,8'hF0,8'h00,8'h00};
mem_hanzi[ 5 ] = {8'h10,8'h30,8'h10,8'h0F,8'h08,8'h08,8'h00,8'h3F,8'h40,8'h40,8'h5F,8'h42,8'h44,8'h43,8'h78,8'h00};/*"地",2*/
mem_hanzi[ 6 ] = {8'h00,8'h00,8'h80,8'h40,8'h30,8'h0F,8'h08,8'h08,8'h08,8'hC8,8'h38,8'h00,8'h00,8'h00,8'h00,8'h00};
mem_hanzi[ 7 ] = {8'h80,8'h81,8'h40,8'h40,8'h20,8'h10,8'h08,8'h04,8'h03,8'h0C,8'h10,8'h20,8'h40,8'h80,8'h80,8'h00};/*"久",3*/
mem_hanzi[ 8 ] = {8'h40,8'h40,8'h42,8'h42,8'h42,8'h42,8'h42,8'hFE,8'h42,8'h42,8'h42,8'h42,8'h42,8'h40,8'h40,8'h00};
mem_hanzi[ 9 ] = {8'h80,8'h80,8'h40,8'h20,8'h10,8'h0C,8'h03,8'h00,8'h03,8'h0C,8'h10,8'h20,8'h40,8'h80,8'h80,8'h00};/*"天",4*/
mem_hanzi[ 10 ] = {8'h80,8'h80,8'h80,8'h80,8'hFF,8'h80,8'h80,8'hA0,8'h90,8'h88,8'h84,8'h82,8'h80,8'h80,8'h80,8'h00};
mem_hanzi[ 11 ] = {8'h00,8'h00,8'h00,8'h00,8'hFF,8'h40,8'h21,8'h12,8'h04,8'h08,8'h10,8'h20,8'h20,8'h40,8'h40,8'h00};/*"长",5*/
mem_hanzi[ 12 ] = {8'h02,8'hE2,8'h22,8'h22,8'h22,8'hFE,8'h22,8'h22,8'h22,8'hFE,8'h22,8'h22,8'h22,8'hE2,8'h02,8'h00};
mem_hanzi[ 13 ] = {8'h00,8'hFF,8'h00,8'h08,8'h06,8'h01,8'h16,8'h08,8'h06,8'h01,8'h02,8'h4C,8'h80,8'h7F,8'h00,8'h00};/*"两",0*/
mem_hanzi[ 14 ] = {8'h00,8'h00,8'h00,8'hFE,8'h02,8'h02,8'h02,8'h02,8'h02,8'h02,8'h02,8'hFE,8'h00,8'h00,8'h00,8'h00};
mem_hanzi[ 15 ] = {8'h00,8'h80,8'h40,8'h23,8'h19,8'h01,8'h01,8'h01,8'h01,8'h01,8'h09,8'h13,8'h20,8'hC0,8'h00,8'h00};/*"只",1*/
mem_hanzi[ 16 ] = {8'h20,8'h20,8'h24,8'h24,8'h24,8'h24,8'hBF,8'h64,8'h24,8'h34,8'h28,8'h24,8'h22,8'h20,8'h20,8'h00};
mem_hanzi[ 17 ] = {8'h10,8'h08,8'h04,8'h02,8'h3F,8'h45,8'h44,8'h44,8'h42,8'h42,8'h42,8'h41,8'h78,8'h00,8'h00,8'h00};/*"老",2*/
mem_hanzi[ 18 ] = {8'h00,8'h00,8'hF8,8'h08,8'h48,8'h48,8'h48,8'hFF,8'h4A,8'h2A,8'h2A,8'h0A,8'hCA,8'h18,8'h00,8'h00};
mem_hanzi[ 19 ] = {8'h80,8'h60,8'h1F,8'h80,8'h40,8'h20,8'h1C,8'h04,8'h05,8'h05,8'h7D,8'h81,8'h81,8'hE0,8'h00,8'h00};/*"虎",3*/
mem_hanzi[ 20 ] = {8'h80,8'h80,8'h40,8'h20,8'h50,8'h48,8'h44,8'hC3,8'h44,8'h48,8'h50,8'h20,8'h40,8'h80,8'h80,8'h00};
mem_hanzi[ 21 ] = {8'h40,8'h40,8'h42,8'h4A,8'h72,8'h42,8'h42,8'h7F,8'h42,8'h42,8'h62,8'h5A,8'h42,8'h40,8'h40,8'h00};/*"金",0*/
mem_hanzi[ 22 ] = {8'h40,8'h44,8'h24,8'h24,8'h94,8'h0C,8'h04,8'h7F,8'h04,8'h0C,8'h94,8'h24,8'h24,8'h44,8'h40,8'h00};
mem_hanzi[ 23 ] = {8'h20,8'h22,8'h12,8'h0A,8'hFF,8'h0A,8'h12,8'h20,8'h12,8'h0A,8'hFF,8'h06,8'h0A,8'h12,8'h20,8'h00};/*"森",1*/
mem_hanzi[ 24 ] = {8'h40,8'h22,8'h14,8'h08,8'hF4,8'h02,8'h00,8'h04,8'h04,8'h04,8'hFC,8'h04,8'h04,8'h04,8'h00,8'h00};
mem_hanzi[ 25 ] = {8'h08,8'h44,8'h82,8'h41,8'h3F,8'h00,8'h40,8'h41,8'h41,8'h41,8'h7F,8'h41,8'h41,8'h41,8'h40,8'h00};/*"狂",2*/
mem_hanzi[ 26 ] = {8'h00,8'h08,8'h88,8'h68,8'hFF,8'h48,8'h88,8'h00,8'hFE,8'h52,8'h52,8'h52,8'hFE,8'h00,8'h00,8'h00};
mem_hanzi[ 27 ] = {8'h40,8'h31,8'h00,8'h00,8'h3B,8'h40,8'h40,8'h44,8'h5B,8'h42,8'h42,8'h72,8'h03,8'h08,8'h30,8'h00};/*"想",3*/
mem_hanzi[ 28 ] = {8'h00,8'hF0,8'h10,8'h10,8'h10,8'hFF,8'h10,8'h10,8'h10,8'hFF,8'h10,8'h10,8'h10,8'hF0,8'h00,8'h00};
mem_hanzi[ 29 ] = {8'h00,8'hFF,8'h42,8'h42,8'h42,8'h7F,8'h42,8'h42,8'h42,8'h7F,8'h42,8'h42,8'h42,8'hFF,8'h00,8'h00};/*"曲",4*/
我是通过PCtoLCD2002软件,输入汉字,然后得到的字模,再放到字库里面
消抖模块 (key_fliter.v)
我们可以看到,但按键按下的那一刻,存在一段时间的抖动,同时在释放按键的一段时间里也是存在抖动的,这就可能导致状态在识别的时候可能检测为多次的按键,因为运行过程中普通的检测一次状态key为1就执行一次按键操作。所以我们在使用按键时往往需要消抖。消抖方式有很多种,这里我提供一种相对而言比较简单容易理解的方式,通过延时来消抖。我们知道,抖动时间的长短由按键的机械特性决定,一般为5ms~10ms。大家看原理图,其实我们要做的就是,但按键按下去后,只在中间稳定的某一个时刻(10ms)取一个真正按键的使能值就好了(只不过为了更加准确,我取的是20ms)。
module key_fliter//按键消抖
#(
parameter CNT_MAX=17'd239_999
)
(
input wire clk,
input wire rst,
input wire key_in,
output reg key_flag
);
reg [16:0] cnt_20ms;
always@(posedge clk or negedge rst)
if(rst == 1'b0)
cnt_20ms<=17'd0;
else if(key_in == 1'b1)
cnt_20ms<=17'd0;
else if(cnt_20ms == CNT_MAX)
cnt_20ms<=CNT_MAX;
else
cnt_20ms<=cnt_20ms+17'd1;
always@(posedge clk or negedge rst)
if(rst == 1'b0)
key_flag<=1'b0;
else if(cnt_20ms == CNT_MAX-17'd1)
key_flag<=1'b1;
else
key_flag<=1'b0;
endmodule
music顶层模块(v)
在这个模块中,我将各模块的代码进行整合,同时将蜂鸣器的那部分代码一起写在了这里,主要就是设置每个音符的对应参数,以及按照乐谱写出音符的排列顺序。
每个音符最大计数:
parameter L_1=16'd45802,
parameter L_2=16'd40816,
parameter L_3=16'd36364,
parameter L_4=16'd34384,
parameter L_5=16'd30612,
parameter L_6=16'd27273,
parameter L_7=16'd24291,
parameter M_1=16'd22931,
parameter M_2=16'd20432,
parameter M_3=16'd18201,
parameter M_4=16'd17180,
parameter M_5=16'd15306,
parameter M_6=16'd13636,
parameter M_7=16'd12148,
parameter H_1=16'd11434,
parameter H_2=16'd10215,
parameter H_3=16'd9101,
parameter H_4=16'd8590,
parameter H_5=16'd7653,
parameter H_6=16'd6818,
parameter H_7=16'd6074
歌曲代码:
always@(posedge clk or negedge rst) begin
case(cnt_250ms)
//曲目1:友谊地久天长
8'd0,8'd1: freq_data <= L_5;
8'd2,8'd3,8'd4,8'd5,8'd6,8'd7,8'd8: freq_data <= M_1;
8'd9,8'd10: freq_data <= M_3;
8'd11,8'd12,8'd13,8'd14: freq_data <= M_2;
8'd15: freq_data <= M_1;
8'd16,8'd17: freq_data <= M_2;
8'd18,8'd19: freq_data <= M_3;
8'd20,8'd21,8'd22,8'd23,8'd24: freq_data <= M_1;
8'd25,8'd26: freq_data <= M_3;
8'd27,8'd28: freq_data <= M_5;
8'd29,8'd30,8'd31,8'd32,8'd33: freq_data <= M_6;
8'd34,8'd35,8'd36,8'd37,8'd38: freq_data <= M_6;
8'd39,8'd40,8'd41,8'd42: freq_data <= M_5;
8'd43,8'd44,8'd45: freq_data <= M_3;
8'd46,8'd47: freq_data <= M_1;
8'd48,8'd49,8'd50,8'd51: freq_data <= M_2;
8'd52: freq_data <= M_1;
8'd53,8'd54: freq_data <= M_2;
8'd55,8'd56: freq_data <= M_3;
8'd57,8'd58,8'd59,8'd60: freq_data <= M_1;
8'd61,8'd62,8'd63: freq_data <= L_6;
8'd64,8'd65: freq_data <= M_5;
8'd66,8'd67,8'd68,8'd69: freq_data <= M_1;
8'd70,8'd71,8'd72,8'd73: freq_data <= M_1;
8'd74,8'd75: freq_data <= M_6;
8'd76,8'd77,8'd78,8'd79: freq_data <= M_5;
8'd80,8'd81,8'd82: freq_data <= M_3;
8'd83,8'd84: freq_data <= M_1;
8'd85,8'd86,8'd87,8'd88: freq_data <= M_2;
8'd89: freq_data <= M_1;
8'd90,8'd91: freq_data <= M_2;
8'd92,8'd93: freq_data <= M_6;
8'd94,8'd95,8'd96,8'd97: freq_data <= M_5;
8'd98,8'd99,8'd100: freq_data <= M_3;
8'd101,8'd102: freq_data <= M_5;
8'd103,8'd104,8'd105,8'd106: freq_data <= M_6;
8'd107,8'd108,8'd109,8'd110: freq_data <= M_6;
8'd111,8'd112: freq_data <= H_1;
8'd113,8'd114,8'd115,8'd116: freq_data <= M_5;
8'd117,8'd118,8'd119: freq_data <= M_3;
8'd120,8'd121: freq_data <= M_1;
8'd122,8'd123,8'd124,8'd125: freq_data <= M_2;
8'd126: freq_data <= M_1;
8'd127,8'd128: freq_data <= M_2;
8'd129,8'd130: freq_data <= M_3;
8'd131,8'd132,8'd133,8'd134: freq_data <= M_1;
8'd135,8'd136,8'd137: freq_data <= L_6;
8'd138,8'd139: freq_data <= M_5;
8'd140,8'd141,8'd142,8'd143: freq_data <= M_1;
8'd144,8'd145,8'd146,8'd147: freq_data <= M_1;
//曲目2:两只老虎
8'd148: freq_data <= M_1;
8'd149: freq_data <= M_2;
8'd150: freq_data <= M_3;
8'd151: freq_data <= M_1;
8'd152: freq_data <= M_1;
8'd153: freq_data <= M_2;
8'd154: freq_data <= M_3;
8'd155: freq_data <= M_1;
8'd156: freq_data <= M_3;
8'd157: freq_data <= M_4;
8'd158: freq_data <= M_5;
8'd159: freq_data <= M_3;
8'd160: freq_data <= M_4;
8'd161: freq_data <= M_5;
8'd162: freq_data <= M_5;
8'd163: freq_data <= M_6;
8'd164: freq_data <= M_5;
8'd165: freq_data <= M_4;
8'd166: freq_data <= M_3;
8'd167: freq_data <= M_1;
8'd168: freq_data <= M_5;
8'd169: freq_data <= M_6;
8'd170: freq_data <= M_5;
8'd171: freq_data <= M_4;
8'd172: freq_data <= M_3;
8'd173: freq_data <= M_1;
8'd174: freq_data <= M_2;
8'd175: freq_data <= M_5;
8'd176: freq_data <= M_1;
8'd177: freq_data <= M_2;
8'd178: freq_data <= M_5;
8'd179: freq_data <= M_1;
//曲目3:金森狂想曲
8'd180: freq_data <= L_6;
8'd181: freq_data <= M_1;
8'd182: freq_data <= M_3;
8'D183: freq_data <= M_5;
8'D184,8'D185: freq_data <= M_3;
8'D186: freq_data <= M_3;
8'D187: freq_data <= M_2;
8'D188,8'D189: freq_data <= M_3;
8'D190: freq_data <= M_3;
8'D191: freq_data <= M_2;
8'D192,8'D193: freq_data <= M_3;
8'D194: freq_data <= L_6;
8'D195: freq_data <= L_7;
8'D196: freq_data <= M_1;
8'D197: freq_data <= M_3;
8'D198: freq_data <= M_2;
8'D199: freq_data <= M_1;
8'D200,8'D201: freq_data <= L_6;
8'D202,8'D203: freq_data <= L_5;
8'D204,8'D205,8'D206,8'D207,8'D208,8'D209,8'D210,8'D211:freq_data <= L_3;
8'd212: freq_data <= L_6;
8'd213: freq_data <= M_1;
8'd214: freq_data <= M_3;
8'D215: freq_data <= M_5;
8'D216,8'D217: freq_data <= M_3;
8'D218: freq_data <= M_3;
8'D219: freq_data <= M_2;
8'D220,8'D221: freq_data <= M_3;
8'D222: freq_data <= M_3;
8'D223: freq_data <= M_2;
8'D224,8'D225: freq_data <= M_3;
8'D226: freq_data <= L_6;
8'D227: freq_data <= L_7;
8'D228: freq_data <= M_1;
8'D229: freq_data <= M_3;
8'D230: freq_data <= M_2;
8'D231: freq_data <= M_1;
8'D232,8'D233: freq_data <= L_6;
8'D234,8'D235: freq_data <= L_5;
8'D236,8'D237,8'D238,8'D239,8'D240,8'D241: freq_data <= L_6;
8'D242: freq_data <= L_6;
8'D243: freq_data <= L_7;
default:freq_data=16'hffff;
endcase
end
切换歌曲代码:
always@(posedge clk or negedge rst)
if(!rst)
begin cnt_250ms_min=8'd0; cnt_250ms_max<=8'd147; end
else if(key_flag_1)
begin cnt_250ms_min=8'd148;cnt_250ms_max<=8'd179; end
else if(key_flag_2)
begin cnt_250ms_min=8'd180;cnt_250ms_max<=8'd243; end
else if(!f_1min)
begin cnt_250ms_min=8'd254;cnt_250ms_max<=8'd254; end
else
begin cnt_250ms_min=cnt_250ms_min;cnt_250ms_max<=cnt_250ms_max; end
一分钟计时代码:
always@(posedge clk or negedge rst)
if(rst == 1'b0)
f_1min<=1'b1;
else if(key_flag_1 || key_flag_2)
f_1min<=1'b1;
else if(cnt_1min == CNT_1MIN_MAX)
f_1min<=1'b0;
else
f_1min<=f_1min;
always@(posedge clk or negedge rst)
if(rst == 1'b0)
cnt_1min<=30'd0;
else if(key_flag_1 || key_flag_2)
cnt_1min<=30'd0;
else if(cnt_1min == CNT_1MIN_MAX-30'd1)
cnt_1min<=CNT_1MIN_MAX;
else if(f_1min)
cnt_1min<=cnt_1min+30'd1;
else
cnt_1min<=30'd0;
任意一个按键按下后,选择相应歌曲从头开始播放,同时1分钟计时从头开始,1分钟时间到后蜂鸣器停止发声,使得歌曲单曲循环持续时间达到1分钟。
固定每一个音节持续时间为250ms:
always@(posedge clk or negedge rst)
if(rst == 1'b0)
cnt<=0;
else if(cnt == CNT_MAX)
cnt<=0;
else
cnt<=cnt+23'b1;
always@(posedge clk or negedge rst) begin
if(!rst)
cnt_250ms<=8'd0;
else if(key_flag_1)
cnt_250ms<=8'd148;
else if(key_flag_2)
cnt_250ms<=8'd180;
else if((cnt_250ms == cnt_250ms_max) && (cnt == CNT_MAX))
cnt_250ms<=cnt_250ms_min;
else if(cnt == CNT_MAX)
cnt_250ms<=cnt_250ms+3'd1;
else
cnt_250ms<=cnt_250ms;
end
蜂鸣器代码:
assign duty_data=freq_data>>1;
always@(posedge clk or negedge rst)
if(rst == 1'b0)
beep<=1'b0;
else if((freq_cnt>=duty_data)&&(f_1min))
beep<=1'b1;
else
beep<=1'b0;
引例整合部分代码:
key_fliter key_fliter_u1
(
.clk(clk),
.rst(rst),
.key_in(key_in_1),
.key_flag(key_flag_1)
);
key_fliter key_fliter_u2
(
.clk(clk),
.rst(rst),
.key_in(key_in_2),
.key_flag(key_flag_2)
);
OLED12864 OLED12864_u1
(
.clk (clk) ,
.rst_n (rst),
.key_flag_1(key_flag_1),
.key_flag_2(key_flag_2),
//.oled_csn(oled_csn),
.oled_rst(oled_rst),
.oled_dcn(oled_dcn),
.oled_clk(oled_clk),
.oled_dat(oled_dat)
);
使占空比为50%,只需改变最大计数值,使频率计数大于占空比计数时蜂鸣器输出高电平,其余时刻输出低电平,产生周期不同,占空比相同的PWM。
以上就是代码部分的全部内容。
完成情况:
所有要求已全部完成,但某些方面还需要完善,比如说OLED显示部分用ROM,IP核来实现存储器可能效果更好,看到一些人将顶层写得很简单,模块化很多,所以我觉得我对模块化编程还不能应用自如,代码文件已经全部上传附件,里面还有一些不完美的代码,希望可以和大家互相交流学习。
遇到的问题:
最主要的是OLED显示的问题,一直没想好怎么切换歌曲名称,曾经想过采用刷新的方式,在刷新的时候检测按键状态来进行切换,后失败,改用一次显示,检测按键状态,根据其状态来一次显示应该显示的歌曲名称。
其次遇到的问题就是无论按下哪一个按键都是第一首歌播放,后面发现是初值没有改。
总结:
这次实践项目我完成了基本的内容,因为是初学FPGA,所以先花了一部分时间将基础的一些程序比如构建一些数字电路,点亮LED,使蜂鸣器响等等,等把那些基础程序都跑过之后才开始做的项目,所以花了不少时间来学习,虽然很累当时但确实学到了许多知识,也在网站上参考学习了很多别人的代码思路,对我做出这个任务有了很大的启发,当然这次也有很多是我没有找到相似思路的问题,但经过思考最终也得到了解决,这次的项目包含了几个小任务,需要一个个解决,也锻炼了我解决项目问题的能力。我觉得我模块化编程的能力还需要再加强,希望以后可以继续学习,把本次实践学到知识加以利用,更上一层楼!。