一、實驗任務
利用FPGA進行代碼開發,使蜂鳴器演奏出樂曲《生日快樂》,將音調顯示在數碼管。原理為蜂鳴器為交流源蜂鳴器,在引腳上加一定頻率的方波就可以發聲,而且發聲的頻率由所加方波決定。這樣我們就可以根據無源蜂鳴器的原理進行發聲練習了。
二、代碼實現
由於需要蜂鳴器發聲且數碼管顯示音調,所以我們將代碼分為兩部分。
第一部分用於產生音調的方波。第二部分為數碼管顯示。
(一)產生音調
(1)PreDiv 預置分頻數模塊
將48M晶振分頻12M,再計算得出各個音調的頻率,公式為12M÷音調頻率÷2,所得即為預置分頻數。程序中只編寫了低音和中音的14個音。
module prediv( input [3:0]Index, input clk, input Reset_n, output reg[15:0] PreDiv ); always @ (negedge Reset_n or posedge clk) if(!Reset_n) begin PreDiv<=16'h5997; end else begin case(Index) 4'd1:PreDiv<=16'h5997; 4'd2:PreDiv<=16'h4FCD; 4'd3:PreDiv<=16'h471B; 4'd4:PreDiv<=16'h431E; 4'd5:PreDiv<=16'h3BCA; 4'd6:PreDiv<=16'h3544; 4'd7:PreDiv<=16'h2F74; 4'd8:PreDiv<=16'h2CCA; 4'd9:PreDiv<=16'h27E8; 4'd10:PreDiv<=16'h238D; 4'd11:PreDiv<=16'h218E; 4'd12:PreDiv<=16'h1DE5; 4'd13:PreDiv<=16'h1AA2; 4'd14:PreDiv<=16'h17BA; endcase end endmodule
(2)Index 索引數模塊
為了便於代碼書寫,需要引用索引數(其實加一個ROM更為方便),即使用“5“為低音”so",如簡譜一樣,可以更為方便的編寫樂曲。畢竟樂曲有很多音符,如果每次都用預置數編寫程序,程序書寫和查錯會非常不方便。當然這也是verilog語言的魅力之處。
module index( input clk, input reset_n, output reg[3:0]index ); reg[5:0]cnt; wire clk2m; wire clk2000; wire clk2; defparam clk_rhythm1.divdWIDTH=3,clk_rhythm1.divdFACTOR=12;//24分頻2M div clk_rhythm1( .reset(reset_n), .clkin(clk), .clkout(clk2m) ); defparam clk_rhythm2.divdWIDTH=8,clk_rhythm2.divdFACTOR=500;//1000分頻2000hz div clk_rhythm2( .reset(reset_n), .clkin(clk2m), .clkout(clk2000) ); defparam clk_rhythm3.divdWIDTH=8,clk_rhythm3.divdFACTOR=500;//1000分頻2hz div clk_rhythm3( .reset(reset_n), .clkin(clk2000), .clkout(clk2) ); always @ (negedge reset_n or posedge clk2) if(!reset_n) begin index<=4'd0; cnt<=6'h0; end else begin if(cnt==6'd42) cnt<=6'h0; else cnt<=cnt+1'b1; case(cnt) 6'd1:index<=4'd5; 6'd2:index<=4'd5; 6'd3:index<=4'd6; 6'd4:index<=4'd6; 6'd5:index<=4'd5; 6'd6:index<=4'd5; 6'd7:index<=4'd8; 6'd8:index<=4'd8; 6'd9:index<=4'd7; 6'd10:index<=4'd7; 6'd11:index<=4'd5; 6'd12:index<=4'd5; 6'd13:index<=4'd6; 6'd14:index<=4'd6; 6'd15:index<=4'd5; 6'd16:index<=4'd5; 6'd17:index<=4'd9; 6'd18:index<=4'd9; 6'd19:index<=4'd8; 6'd20:index<=4'd8; 6'd21:index<=4'd5; 6'd22:index<=4'd5; 6'd23:index<=4'd12; 6'd24:index<=4'd12; 6'd25:index<=4'd10; 6'd26:index<=4'd10; 6'd27:index<=4'd8; 6'd28:index<=4'd8; 6'd29:index<=4'd7; 6'd30:index<=4'd7; 6'd31:index<=4'd6; 6'd32:index<=4'd6; 6'd33:index<=4'd11; 6'd34:index<=4'd11; 6'd35:index<=4'd10; 6'd36:index<=4'd10; 6'd37:index<=4'd8; 6'd38:index<=4'd8; 6'd39:index<=4'd9; 6'd40:index<=4'd9; 6'd41:index<=4'd8; 6'd42:index<=4'd8; endcase end endmodule
(3)節拍
歌曲中不僅有音調還要有快慢,這個快慢即為節拍。所以我們還是分頻分出節拍為0.5s一拍。具體代碼見上一部分。
(二)數碼管顯示
數碼管顯示的原理是利用人眼的視覺暫留0.05s,數碼管動態掃描時間小於等於0.05s,人眼看到數碼管就是一直在顯示,根據這個原理,我們將音調顯示在數碼管上。音調即為上文提到的索引數,所以我們編寫代碼的思路就為將索引數輸入,進行一些運算,在數碼管上顯示。道理十分簡單。
module smdisplay(clk,rst,index,dataout,en); input clk,rst; input [3:0]index; output[7:0] dataout; output[3:0] en;//COM使能輸出 reg[7:0] dataout;//各段數據輸出 reg[3:0] en; reg[15:0] cnt_scan;//掃描頻率計數器 reg[3:0] dataout_buf; always@(posedge clk or negedge rst) begin if(!rst) begin //低電平復位 cnt_scan<=0; end else begin cnt_scan<=cnt_scan+1; end end always @(cnt_scan)//段碼掃描頻率 begin case(cnt_scan[15:14]) 2'b00 : en = 4'b1110; 2'b01 : en = 4'b1101; 2'b10 : en = 4'b1011; 2'b11 : en = 4'b0111; default : en = 4'b1110; endcase end always@(en or index) //對應COM信號給出各段數據,段碼 begin if(index>=1 && index<=7) case(en) 4'b1110: dataout_buf<=index; 4'b1101: dataout_buf<=0; 4'b1011: dataout_buf<=0; 4'b0111: dataout_buf<=0; default: dataout_buf<=8; endcase else if(index >=8 && index <=14) case(en) 4'b1110: dataout_buf<=0; 4'b1101: dataout_buf<=index - 4'd7; 4'b1011: dataout_buf<=0; 4'b0111: dataout_buf<=0; default: dataout_buf<=8; endcase else case(en) 4'b1110: dataout_buf<=0; 4'b1101: dataout_buf<=0; 4'b1011: dataout_buf<=index - 4'd14; 4'b0111: dataout_buf<=0; default: dataout_buf<=8; endcase end always@(dataout_buf) begin case(dataout_buf) //將要顯示的數字譯成段碼 4'b0000://0 dataout=8'b0000_0011; 4'b0001://1 dataout=8'b1001_1111; 4'b0010://2 dataout=8'b0010_0101; 4'b0011://3 dataout=8'b0000_1101; 4'b0100://4 dataout=8'b1001_1001; 4'b0101://5 dataout=8'b0100_1001; 4'b0110://6 dataout=8'b0100_0001; 4'b0111://7 dataout=8'b0001_1111; 4'b1000://8 dataout=8'b0000_0001; 4'b1001://9 dataout=8'b0000_1001; default://這里僅編譯了0-9這幾個數字 dataout=8'b1111_1111;//全滅 endcase end endmodule
之后,我們在頂層編寫,將各個模塊連接起來。輸入輸出分清楚。再進行管腳的綁定,我們就可以進行程序的燒寫了!燒寫如我們所料,可以演奏音樂,並且在數碼管上顯示音調。
三、感悟
這是在一年前大學里學了EDA課程后,再一次拾起。年輕時候由於EDA是選修課程,盡管很感興趣也很喜歡講授課程的老師,但是還是沒有很重視起來,覺得這些課程像高數大物之類般看看書完成完成課后作業便可以了,可是在每一次完成老師布置的大作業時的吃力,至今想想還是不容小覷的。那時的我不會查資料,不會從網上看別人發的經驗帖,更是看代碼也十分粗糙,也不會用仿真軟件。總結起來就是不知道這些與時俱進的技術怎么學。現在很高興的是,我又一次的拾起來這些大概還沒忘記的知識。由於實習,我又一次接觸了FPGA,並且將其作為我的畢設題目。這次,我一定要吸取經驗,努力提高自己。如果不知道該努力什么,那么就把自己現在所面臨的每一件事情做好。加油。