1 設計要求
以蜂鳴器演奏《世上只有媽媽好》的片段為例,用FPGA設計一個樂曲演奏系統。
2 設計原理
2.1 蜂鳴器的結構原理
蜂鳴器是一種一體化結構的電子訊響器,采用直流電壓供電,廣泛應用於計算機、打印機、復印機、報警器、電子玩具、汽車電子設備、電話機、定時器等電子產品中作發聲器件。蜂鳴器主要分為壓電式蜂鳴器和電磁式蜂鳴器兩種類型。
按照內部有無振盪源可以為有源蜂鳴器和無源蜂鳴器。有源蜂鳴器內部帶振盪源,所以只要一通電就會發生聲音;而無源內部不帶振盪源,所以如果用直流信號無法令其鳴叫。必須用一定頻率的方波去驅動它,如下圖所示。
蜂鳴器“鳴叫”需要的電流較大,故而采用三極管進行驅動, FPGA 控制三極管是否導通。
2.2 簡譜分析
樂曲能連續演奏所需要的兩個基本數據是:組成樂曲的每個音符的頻率值(音調)和每個音符持續的時間(音長)。要演奏樂曲必須考慮兩個方面,一個音符或音調,另一個是音長或節拍。本項目研究如何利用蜂鳴器演奏歌曲《世上只有媽媽好》。下圖為《世上只有媽媽好》的簡譜。
簡譜用來記錄、傳播音樂的工具。在簡譜中,用以表示音的高低及相互關系的基本符號為七個阿拉伯數字,即1、2、3、4、5、6、7,唱作do、re、mi、fa、sol、la、si,稱為唱名。
(1)音的高低(音調)
單用以上七個音是無法表現眾多的音樂形象的。在實際作品中,還有一些更高或更低的音,如在基本音符上方加記一個"·",表示該音升高一個八度,稱為高音;加記兩個" :",則表示該音升高兩個八度,稱為倍高音。在基本音符下方加記一個"·",表示該音降低一個八度,稱為低音;加記兩個" :",則表示該音降低兩個八度,稱為倍低音。在一般歌曲中,無論是在基本音符上方或下方加記兩個以上的"·"的音符都是很少見的。
(2)音的長短(音長)
在簡譜中,1、2、3、4、5、6、7這七個基本音符,不僅表示音的高低,而且還是表示時值長短的基本單位,稱為四分音符,其他音符均是在四分音符的基礎上,用加記短橫線"-"和附點"·"表示。簡譜中用線(減時線、增時線、連音線)、點(符點音符)休止符來表示音的長短。
在基本音符右側加記一條短橫線,表示增長一個四分音符的時值。這類加記在音符右側、使音符時值增長的短橫線,稱為增時線。增時線越多,音符的時值越長。
在基本音符下方加記一條短橫線,表示縮短原音符時值的一半。這類加記在音符下方、使音符時值縮短的短橫線,稱為減時線。減時線越多,音符的時值越短。
在簡譜中,加記在單純音符的右側的、使音符時值增長的小圓點"·",稱為附點。加記附點的音符稱為附點音符。附點本身並無一定的長短,其長短由前面的單純音符來決定。附點的意義在於增長原音符時值的一半,常用於四分音符和小於四分音符的各種音符之后。
休止符,用來表示音樂停頓的符號。
(3)音的強弱
曲譜中有規則的豎線,稱小節線。作用是划分小節內的拍數,明確節拍音的強弱規律。 拍子是節拍的時值單位,如2/4、3/4、3/8、6/8 等,表示每小節有幾拍/幾分音符為一拍。如二拍子: 強、弱 | 強、弱 |……,三拍子: (強、弱、弱 | 強、弱、弱|),四拍子: (強、弱、次強、弱);等等。
(4)調號與定調
1234567是do、re、mi、fa、so、la、xi。這是音的唱名。那么音名是,1是C,2是D,3是E,4是F,5是G,6是A,7是B。比如說D調,就會表明,1=D,而D是do re mi的re的音名。所以1=D的意思就是這個調的do就是re的位置。以此類推,所有的音符都會隨着do的變化而變化。C調就是沒有升降號 12345671。 那G調為一個升號升4 為567123#45 1=G. D調兩個#號為23#4567#12 1=D 其它調類似推出。
(5)各個音符對應的頻率
在音樂中有十二平均律的規定:每兩個八度音之間的頻率相差一倍,在兩個八度音之間又分為十二個半音,
2.3 架構設計
此設計共分4各模塊:
addr_ctrl模塊(地址控制模塊):每1/4秒讓地址進行加1,共有64個音符,故而輸出地址采用6位即可。本模塊中首先設計1/4秒的計時器。當到1/4秒時,讓輸出的addr進行變化:小於63時,進行加1操作;等於63時,進行清零操作。此時蜂鳴器將不斷的重復播放這個音樂。
music_mem模塊(音符存儲模塊):根據簡譜將64個音符存儲起來,然后根據外部的地址,將儲存的音符進行輸出。
music_freq模塊(音符轉換頻率模塊):根據輸入的音符以及不同音符所對應的頻率,輸出對應的頻率值。
wave_gen模塊(產生對應頻率的方波):根據輸入的頻率值,產生對應頻率的方波。產生方波的方法采用計時器計時半個周期,然后進行取反。利用時鍾的頻率(50MHz)除以想要的波形的頻率,得出分頻比,將分頻比除以2,得到半個周期的計數值。
信號 |
說明 |
端口/連線 |
FPGA引腳 |
clk |
系統時鍾,50MHz; |
輸入端口 |
|
rst_n |
復位信號,低電平有效; |
輸入端口 |
|
Addr[5:0] |
查找音符的地址,存儲器存有64個字符,地址線為2^6; |
內部連線 |
|
Music[8:0] |
音符,[8:6]為高音,[5:3]為中音,[2:0]為低音;如中音1,編碼為000_001_000; |
內部連線 |
|
Freq[10:0] |
音符所對應的頻率 |
內部連線 |
|
beep |
對應頻率的方波 |
輸出端口 |
|
3 設計與實現
3.1 地址控制模塊
1 module addr_ctrl( 2 input wire clk, 3 input wire rst_n, 4 output reg [5:0] addr 5 ); 6 7 parameter T_1S = 26'd50_000_000; 8 localparam SEC_1_4 = T_1S/4; 9 10 reg [25:0] cnt; 11 12 always@(posedge clk or negedge rst_n)begin 13 if(rst_n == 1'b0) 14 cnt <= 26'd0; 15 else begin 16 if(cnt < SEC_1_4 - 1'b1) 17 cnt <= cnt + 1'b1; 18 else 19 cnt <= 26'd0; 20 end 21 end 22 23 always@(posedge clk or negedge rst_n)begin 24 if(rst_n == 1'b0) 25 addr <= 6'd0; 26 else if(cnt == SEC_1_4 - 1'b1)begin 27 if(addr < 6'd63) 28 addr <= addr + 1'b1; 29 else 30 addr <= 6'd0; 31 end 32 else 33 addr <= addr; 34 end 35 36 endmodule
3.2 音符存儲模塊
1 module music_mem( 2 input wire clk, 3 input wire rst_n, 4 input wire [5:0] addr, 5 output reg [8:0] music 6 ); 7 8 always@(posedge clk or negedge rst_n)begin 9 if(rst_n == 1'b0) 10 music <= 9'b000_000_000; 11 else 12 case(addr) 13 6'd0: music <= 9'b000_110_000; 14 6'd1: music <= 9'b000_110_000; 15 6'd2: music <= 9'b000_110_000; 16 6'd3: music <= 9'b000_101_000; 17 6'd4: music <= 9'b000_011_000; 18 6'd5: music <= 9'b000_011_000; 19 6'd6: music <= 9'b000_101_000; 20 6'd7: music <= 9'b000_101_000; 21 22 6'd8: music <= 9'b001_000_000; 23 6'd9: music <= 9'b001_000_000; 24 6'd10: music <= 9'b000_110_000; 25 6'd11: music <= 9'b000_101_000; 26 6'd12: music <= 9'b000_110_000; 27 6'd13: music <= 9'b000_110_000; 28 6'd14: music <= 9'b000_110_000; 29 6'd15: music <= 9'b000_110_000; 30 31 6'd16: music <= 9'b000_011_000; 32 6'd17: music <= 9'b000_011_000; 33 6'd18: music <= 9'b000_101_000; 34 6'd19: music <= 9'b000_110_000; 35 6'd20: music <= 9'b000_101_000; 36 6'd21: music <= 9'b000_101_000; 37 6'd22: music <= 9'b000_011_000; 38 6'd23: music <= 9'b000_011_000; 39 40 6'd24: music <= 9'b000_001_000; 41 6'd25: music <= 9'b000_000_110; 42 6'd26: music <= 9'b000_101_000; 43 6'd27: music <= 9'b000_011_000; 44 6'd28: music <= 9'b000_010_000; 45 6'd29: music <= 9'b000_010_000; 46 6'd30: music <= 9'b000_010_000; 47 6'd31: music <= 9'b000_010_000; 48 49 6'd32: music <= 9'b000_010_000; 50 6'd33: music <= 9'b000_010_000; 51 6'd34: music <= 9'b000_010_000; 52 6'd35: music <= 9'b000_101_000; 53 6'd36: music <= 9'b000_110_000; 54 6'd37: music <= 9'b000_110_000; 55 6'd38: music <= 9'b000_110_000; 56 6'd39: music <= 9'b000_110_000; 57 58 6'd40: music <= 9'b000_011_000; 59 6'd41: music <= 9'b000_011_000; 60 6'd42: music <= 9'b000_011_000; 61 6'd43: music <= 9'b000_010_000; 62 6'd44: music <= 9'b000_001_000; 63 6'd45: music <= 9'b000_001_000; 64 6'd46: music <= 9'b000_001_000; 65 6'd47: music <= 9'b000_001_000; 66 67 6'd48: music <= 9'b000_101_000; 68 6'd49: music <= 9'b000_101_000; 69 6'd50: music <= 9'b000_101_000; 70 6'd51: music <= 9'b000_011_000; 71 6'd52: music <= 9'b000_010_000; 72 6'd53: music <= 9'b000_001_000; 73 6'd54: music <= 9'b000_000_110; 74 6'd55: music <= 9'b000_001_000; 75 76 6'd56: music <= 9'b000_000_101; 77 6'd57: music <= 9'b000_000_101; 78 6'd58: music <= 9'b000_000_101; 79 6'd59: music <= 9'b000_000_101; 80 6'd60: music <= 9'b000_000_101; 81 6'd61: music <= 9'b000_000_101; 82 6'd62: music <= 9'b000_000_101; 83 6'd63: music <= 9'b000_000_101; 84 default: music <= 9'b000_000_101; 85 endcase 86 end 87 88 endmodule
3.3 音符轉換頻率模塊
1 module music_freq( 2 input wire clk, 3 input wire rst_n, 4 input wire [8:0] music, 5 output reg [10:0] freq 6 ); 7 8 always@(posedge clk or negedge rst_n)begin 9 if(rst_n == 1'b0) 10 freq <= 11'd1; //div_freq=sys_clk/(2*freq),freq != 0 11 else 12 case(music) 13 9'b000_000_001 : freq <= 11'd262; 14 9'b000_000_010 : freq <= 11'd294; 15 9'b000_000_011 : freq <= 11'd330; 16 9'b000_000_100 : freq <= 11'd349; 17 9'b000_000_101 : freq <= 11'd392; 18 9'b000_000_110 : freq <= 11'd440; 19 9'b000_000_111 : freq <= 11'd494; 20 21 9'b000_001_000 : freq <= 11'd523; 22 9'b000_010_000 : freq <= 11'd587; 23 9'b000_011_000 : freq <= 11'd659; 24 9'b000_100_000 : freq <= 11'd699; 25 9'b000_101_000 : freq <= 11'd784; 26 9'b000_110_000 : freq <= 11'd880; 27 9'b000_111_000 : freq <= 11'd988; 28 29 9'b001_000_000 : freq <= 11'd1050; 30 9'b010_000_000 : freq <= 11'd1175; 31 9'b011_000_000 : freq <= 11'd1319; 32 9'b100_000_000 : freq <= 11'd1397; 33 9'b101_000_000 : freq <= 11'd1568; 34 9'b110_000_000 : freq <= 11'd1760; 35 9'b111_000_000 : freq <= 11'd1976; 36 default: freq <= 11'd1; 37 endcase 38 end 39 40 endmodule
3.4 波形產生模塊
1 module wave_gen( 2 input wire clk, 3 input wire rst_n, 4 input wire [10:0] freq, 5 output reg beep 6 ); 7 8 parameter F_CLK = 50_000_000; 9 10 wire [25:0] half; 11 reg [25:0] cnt; 12 13 assign half = F_CLK /(2*freq); 14 15 always@(posedge clk or negedge rst_n)begin 16 if(rst_n == 1'b0) 17 cnt <= 26'd0; 18 else 19 if(cnt < half - 1'b1) 20 cnt <= cnt + 1'b1; 21 else 22 cnt <= 26'd0; 23 end 24 25 always@(posedge clk or negedge rst_n)begin 26 if(rst_n == 1'b0) 27 beep <= 1'b0; 28 else 29 if(cnt == half - 1'b1) 30 beep <= ~beep; 31 else 32 beep <= beep; 33 end 34 35 endmodule
3.5 頂層模塊
1 module music_beep( 2 input wire clk, 3 input wire rst_n, 4 output wire beep 5 ); 6 7 wire [5:0] addr; 8 wire [8:0] music; 9 wire [10:0] freq; 10 11 addr_ctrl addr_ctrl_inst( 12 .clk (clk), 13 .rst_n (rst_n), 14 .addr (addr) 15 ); 16 17 music_mem music_mem_inst( 18 .clk (clk), 19 .rst_n (rst_n), 20 .addr (addr), 21 .music (music) 22 ); 23 24 music_freq music_freq_inst( 25 .clk (clk), 26 .rst_n (rst_n), 27 .music (music), 28 .freq (freq) 29 ); 30 31 wave_gen wave_gen_inst( 32 .clk (clk), 33 .rst_n (rst_n), 34 .freq (freq), 35 .beep (beep) 36 ); 37 38 endmodule
4 仿真與驗證
4.1 地址控制模塊的仿真
1 `timescale 1ns/1ps 2 3 module addr_ctrl_tb(); 4 5 reg clk; 6 reg rst_n; 7 wire [5:0] addr; 8 9 addr_ctrl addr_ctrl_inst( 10 .clk (clk), 11 .rst_n (rst_n), 12 .addr (addr) 13 ); 14 15 defparam addr_ctrl_inst.T_1S = 20; 16 17 initial clk = 1'b0; 18 always #10 clk = ~clk; 19 20 initial begin 21 rst_n = 1'b0; #51; 22 rst_n = 1'b1; 23 #(20*4*200); 24 $stop; 25 end 26 27 endmodule
4.2 頂層模塊仿真測試
1 `timescale 1ns/1ps 2 3 module music_beep_tb(); 4 5 reg clk; 6 reg rst_n; 7 wire beep; 8 9 music_beep music_beep_inst( 10 .clk (clk), 11 .rst_n (rst_n), 12 .beep (beep) 13 ); 14 15 initial clk = 1'b0; 16 always #10 clk = ~clk; 17 18 initial begin 19 rst_n = 1'b0; 20 #21; rst_n = 1'b1; 21 #1_000_000_000; 22 $stop; 23 end 24 25 endmodule
5 參考文獻