基於FPGA的音樂蜂鳴器設計


      蜂鳴器是一種一體化結構的電子訊響器,采用直流電壓供電,廣泛應用於計算機、打印機、復印機、報警器、電子玩具、汽車電子設備、電話機、定時器等電子產品中作發聲器件。

 

 

 

 

圖1 :蜂鳴器實物圖

 

蜂鳴器主要分為壓電式蜂鳴器和電磁式蜂鳴器兩種類型。

 

    壓電式蜂鳴器 壓電式蜂鳴器主要由多諧振盪器、壓電蜂鳴片、阻抗匹配器及共鳴箱、外殼等組成。有的壓電式蜂鳴器外殼上還裝有發光二極管。多諧振盪器由晶體管或集成電路構成。當接通電源后(1.5~15V直流工作電壓),多諧振盪器起振,輸出1.5~2.5kHZ的音頻信號,阻抗匹配器推動壓電蜂鳴片發聲。

 

    電磁式蜂鳴器電磁式蜂鳴器由振盪器、電磁線圈、磁鐵、振動膜片及外殼等組成。接通電源后,振盪器產生的音頻信號電流通過電磁線圈,使電磁線圈產生磁場。振動膜片在電磁線圈和磁鐵的相互作用下,周期性地振動發聲。

 

    按照內部有無震盪源可以分為有源蜂鳴器和無源蜂鳴器。有源蜂鳴器內部帶震盪源,所以只要一通電就會發出聲音;而無源內部不帶震盪源,所以如果用直流信號無法令其鳴叫。必須用一定頻率的方波去驅動它。

 

    首先設計分頻器,設計一個1KHz的方波,驅動蜂鳴器,觀測蜂鳴器是否會有聲音產生。

 

    本小節研究如何利用蜂鳴器演唱一首曲子《世上只有媽媽好》。

 

    下圖為《世上只有媽媽好》的簡譜。

 

 

 

 

 

圖2:世上只有媽媽好的簡譜

 

    簡譜是一種比較簡單易學的音樂記譜法。據說簡譜是由法國思想家盧梭於1742年發明的。而最早把簡譜引進我國的是我國近代音樂教育家沈心工。簡譜應該說是一種比較簡單易學的音樂記譜法。它的最大好處是僅用7個阿拉伯數字----1234567,就能將萬千變化的音樂曲子記錄並表示出來.

 

    在簡譜中,用以表示音的高低及相互關系的基本符號為七個阿拉伯數字,即1、2、3、4、5、6、7,唱作do、re、mi、fa、sol、la、si,稱為唱名。

 

音符:1234567

 

唱名:do re mi fa sol la si

 

漢字:哆來米發梭拉西

 

顯然,單用以上七個音是無法表現眾多的音樂形象的。在實際作品中,還有一些更高或更低的音,如在基本音符上方加記一個"·",表示該音升高一個八度,稱為高音;加記兩個" :",則表示該音升高兩個八度,稱為倍高音。在基本音符下方加記一個"·",表示該音降低一個八度,稱為低音;加記兩個" :",則表示該音降低兩個八度,稱為倍低音。

 

在一般歌曲中,無論是在基本音符上方或下方加記兩個以上的"·"的音符都是很少見的。

 

    在簡譜中,1、2、3、4、5、6、7這七個基本音符,不僅表示音的高低,而且還是表示時值長短的基本單位,稱為四分音符,其他音符均是在四分音符的基礎上,用加記短橫線"-"和附點"·"表示。

 

    在基本音符右側加記一條短橫線,表示增長一個四分音符的時值。這類加記在音符右側、使音符時值增長的短橫線,稱為增時線。增時線越多,音符的時值越長。

 

    在基本音符下方加記一條短橫線,表示縮短原音符時值的一半。這類加記在音符下方、使音符時值縮短的短橫線,稱為減時線。減時線越多,音符的時值越短。

 

    在簡譜中,加記在單純音符的右側的、使音符時值增長的小圓點"·",稱為附點。加記附點的音符稱為附點音符。附點本身並無一定的長短,其長短由前面的單純音符來決定。附點的意義在於增長原音符時值的一半,常用於四分音符和小於四分音符的各種音符之后。

 

    在《世上只有媽媽好》的簡譜中,每兩個豎線之間為2秒鍾的時長。每兩個豎線之間有4個音符時長,但是其中有較多半個音符的長,本設計采用1/4秒為基本單位。

 

蜂鳴器給予不同的頻率是可以發出近似1、2、3、4、5、6、7這七個基本音符。

 

 

 

 

 

圖3 :各個音符所對應的頻率

 

此模塊命名為music_beep,clk為50MHz的時鍾,rst_n為低電平有效的復位,beep為蜂鳴器的驅動信號。

 

 

 

圖4 :music_beep的模型

 

在設計時,首先將簡譜中的音符存起來;利用計數器產生1/4秒為周期的脈沖,在此脈沖驅動下,將事先存好的音符一個個輸出;根據音符的值,計算出分頻比;根據分頻比,產生對應頻率的波形。將此波形輸出即可。

 

 

 

圖5 :架構圖

   

在進行多模塊設計時,可以對每個模塊只設計端口,將架構完成后。再分別設計每個模塊。

 

    《世上只有媽媽好》的簡譜中共有8個四拍,每個四拍我們用8個音符來表示,合計共64個音符。在speed_ctrl中,輸出的cnt為6位,正好可以表示64個狀態。

 

    在speed_ctrl中,每1/8秒讓cnt增加1即可。

 

module speed_ctrl (

 

  input   wire            clk,

  input   wire            rst_n,

 

  output  reg   [5:0]     cnt

);

 

  parameter T_250ms   =   12_500_000;

 

  reg           [25:0]    count;

  wire                    flag_250ms;

 

  always @ (posedge clk, negedge rst_n) begin

    if (rst_n == 1'b0)

      count <= 26'd0;

    else

      if (count < T_250ms - 1'b1)

        count <= count + 1'b1;

      else

        count <= 26'd0;

  end

 

  assign flag_250ms = (count == T_250ms - 1'b1) ? 1'b1 : 1'b0;

   

  always @ (posedge clk, negedge rst_n) begin

    if (rst_n == 1'b0)

      cnt <= 6'd0;

    else

      if (flag_250ms == 1'b1)

        cnt <= cnt + 1'b1;

      else

        cnt <= cnt;

  end

 

endmodule

圖6:speed_ctrl的設計代碼

 

    在music_mem中存儲音符,存儲方式為低音用1到7表示,中音用8到14表示,高音用15到21表示,music為5bit位寬。

 

module music_mem (

 

  input   wire            clk,

  input   wire            rst_n,

 

  input   wire    [5:0]   cnt,

 

  output  reg     [4:0]   music

);

// 1  2  3  4  5  6  7

// 8  9  10 11 12 13 14

// 15 16 17 18 19 20 21

 

  always @ (posedge clk, negedge rst_n) begin

    if (rst_n == 1'b0)

      music <= 5'd0;

    else

      case (cnt)

        6'd0    :   music <= 5'd13;

        6'd1    :   music <= 5'd13;

        6'd2    :   music <= 5'd13;

        6'd3    :   music <= 5'd12;

        6'd4    :   music <= 5'd10;

        6'd5    :   music <= 5'd10;

        6'd6    :   music <= 5'd12;

        6'd7    :   music <= 5'd12;

       

        6'd8    :   music <= 5'd15;

        6'd9    :   music <= 5'd15;

        6'd10   :   music <= 5'd13;

        6'd11   :   music <= 5'd12;

        6'd12   :   music <= 5'd13;

        6'd13   :   music <= 5'd13;

        6'd14   :   music <= 5'd13;

        6'd15   :   music <= 5'd13;

       

        6'd16   :   music <= 5'd10;

        6'd17   :   music <= 5'd10;

        6'd18   :   music <= 5'd12;

        6'd19   :   music <= 5'd13;

        6'd20   :   music <= 5'd12;

        6'd21   :   music <= 5'd12;

        6'd22   :   music <= 5'd10;

        6'd23   :   music <= 5'd10;

       

        6'd24   :   music <= 5'd8;

        6'd25   :   music <= 5'd6;

        6'd26   :   music <= 5'd12;

        6'd27   :   music <= 5'd10;

        6'd28   :   music <= 5'd9;

        6'd29   :   music <= 5'd9;

        6'd30   :   music <= 5'd9;

        6'd31   :   music <= 5'd9;

       

        6'd32   :   music <= 5'd9;

        6'd33   :   music <= 5'd9;

        6'd34   :   music <= 5'd9;

        6'd35   :   music <= 5'd10;

        6'd36   :   music <= 5'd12;

        6'd37   :   music <= 5'd12;

        6'd38   :   music <= 5'd13;

        6'd39   :   music <= 5'd13;

        

        6'd40   :   music <= 5'd10;

        6'd41   :   music <= 5'd10;

        6'd42   :   music <= 5'd10;

        6'd43   :   music <= 5'd9;

        6'd44   :   music <= 5'd8;

        6'd45   :   music <= 5'd8;

        6'd46   :   music <= 5'd8;

        6'd47   :   music <= 5'd8;

       

        6'd48   :   music <= 5'd12;

        6'd49   :   music <= 5'd12;

        6'd50   :   music <= 5'd12;

        6'd51   :   music <= 5'd10;

        6'd52   :   music <= 5'd9;

        6'd53   :   music <= 5'd8;

        6'd54   :   music <= 5'd6;

        6'd55   :   music <= 5'd8;

       

        6'd56   :   music <= 5'd5;

        6'd57   :   music <= 5'd5;

        6'd58   :   music <= 5'd5;

        6'd59   :   music <= 5'd5;

        6'd60   :   music <= 5'd5;

        6'd61   :   music <= 5'd5;

        6'd62   :   music <= 5'd5;

        6'd63   :   music <= 5'd5;

        default  :   music <= 5'd0;

      endcase

  end

 

endmodule

圖7 :music_mem的設計代碼

 

    根據頻率和音符的關系,將音符對應的頻率值取出來,根據頻率值算出分頻比。驅動時鍾為50MHz,所以分頻比為50M除以頻率。

 

module cal_divnum (

 

  input   wire              clk,

  input   wire              rst_n,

 

  input   wire    [4:0]     music,

 

  output  reg     [31:0]    divnum

);

 

  reg             [31:0]    freq;

 

  always @ * begin

    case (music)

      5'd1    : freq =    32'd262;

      5'd2    : freq =    32'd294;

      5'd3    : freq =    32'd330;

      5'd4    : freq =    32'd349;

      5'd5    : freq =    32'd392;

      5'd6    : freq =    32'd440;

      5'd7    : freq =    32'd494;

     

      5'd8    : freq =    32'd523;

      5'd9    : freq =    32'd587;

      5'd10   : freq =    32'd659;

      5'd11   : freq =    32'd699;

      5'd12   : freq =    32'd784;

      5'd13   : freq =    32'd880;

      5'd14   : freq =    32'd988;

     

      5'd15   : freq =    32'd1050;

      5'd16   : freq =    32'd1175;

      5'd17   : freq =    32'd1319;

      5'd18   : freq =    32'd1397;

      5'd19   : freq =    32'd1568;

      5'd20   : freq =    32'd1760;

      5'd21   : freq =    32'd1976;

      default : freq =    32'd1;

    endcase

  end

 

  always @ (posedge clk, negedge rst_n) begin

    if (rst_n == 1'b0)

      divnum <= 32'd50_000_000;

    else

      divnum <= 50_000_000/freq;

  end

 

endmodule

圖8 :cal_divmum的設計代碼

 

知道分頻數后,利用任意分頻的方式,產生對的波形即可。

 

module wave_gen (

 

  input   wire            clk,

  input   wire            rst_n,

 

  input   wire  [31:0]    divnum,

 

  output  reg             beep

);

 

  reg           [31:0]    cnt;

 

  always @ (posedge clk, negedge rst_n) begin

    if (rst_n == 1'b0)

      cnt <= 32'd0;

    else

      if (cnt < divnum - 1'b1)

        cnt <= cnt + 1'b1;

      else

        cnt <= 32'd0;

  end

 

  always @ (posedge clk, negedge rst_n) begin

    if (rst_n == 1'b0)

      beep <= 1'b0;

    else

      if (cnt < divnum[31:1])

        beep <= 1'b0;

      else

        beep <= 1'b1;

  end

 

endmodule

圖9 :wave_gen的設計代碼

 

    設計好上述四個模塊后,將它們之前設計架構的連接方式,連接起來。

 

module music_beep (

 

  input   wire              clk,

  input   wire              rst_n,

 

  output  wire              beep

);

 

  wire          [5:0]       cnt;

  wire          [4:0]       music;

  wire          [31:0]      divnum;

 

  speed_ctrl speed_ctrl_inst(

 

      .clk                  (clk),

      .rst_n                (rst_n),

     

      .cnt                  (cnt)

    );

   

  music_mem music_mem_inst(

 

      .clk                  (clk),

      .rst_n                (rst_n),

     

      .cnt                  (cnt),

     

      .music                (music)

    );

   

  cal_divnum cal_divnum_inst(

 

      .clk                  (clk),

      .rst_n                (rst_n),

     

      .music                (music),

     

      .divnum               (divnum)

    );

 

  wave_gen wave_gen_inst(

 

      .clk                  (clk),

      .rst_n                (rst_n),

     

      .divnum               (divnum),

     

      .beep                 (beep)

    );

   

endmodule

圖10 :music_beep的設計代碼

 

    RTL視圖如下,和所設計架構相同。

 

 

 

圖11 :RTL視圖

 

在testbench中,將speed_ctrl_inst模塊中的T_250ms改成10。

 

defparam可以重新定義參數。

 

`timescale 1ns/1ps

 

module music_beep_tb;

 

  reg           clk;

  reg           rst_n;

 

  wire          beep;

 

  defparam music_beep_inst.speed_ctrl_inst.T_250ms = 10;

 

  music_beep music_beep_inst(

 

      .clk        (clk),

      .rst_n      (rst_n),

     

      .beep       (beep)

    );

 

  initial clk = 1'b0;

  always # 10 clk = ~clk;

 

  initial begin

    rst_n = 1'b0;

    # 200

    @ (posedge clk);

    # 2;

    rst_n = 1'b1;

   

    # 20000;

    $stop;

  end

 

endmodule

圖12 :testbench代碼

 

由於輸出的頻率都較低,所以仿真時間都很長。

 

將參數改小,也只是加快切換輸出音符的頻率。由於wave_gen模塊和分頻模塊相同,故而不在驗證。只看RTL視圖中,分頻數是不是正確即可。

 

 

 

 

圖13 :RTL視圖

 

在RTL視圖中,也看到cnt每10個周期增長1,然后對應輸出音符。音符得出頻率,根據頻率得出分頻數。經過驗證,數據都是正確的。

 

分配管腳,全編譯形成下載文件,下板后就可以聽到《世上只有媽媽好》的歌曲了。

 

通過更改speed_crtl中的控制音符前進的速度,可以控制播放的速度。如果將速度控制到1/2秒的話,那么聽到的歌曲將會變慢。如果將速度控制到1/8秒的話,那么聽到的歌曲將會變快。

 

設計者:郝旭帥         QQ:746833924     QQ交流群: 173560979                                    

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM