• 正文
    • 設(shè)計原理
    • 設(shè)計架構(gòu)
    • 設(shè)計代碼
  • 相關(guān)推薦
申請入駐 產(chǎn)業(yè)圖譜

源碼系列:基于FPGA的電子琴設(shè)計(附源工程)

01/20 14:47
1789
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

大俠好,歡迎來到FPGA技術(shù)江湖,江湖偌大,相見即是緣分。大俠可以關(guān)注FPGA技術(shù)江湖,在“闖蕩江湖”、"行俠仗義"欄里獲取其他感興趣的資源,或者一起煮酒言歡。

今天給大俠帶來基于FPGA的電子琴設(shè)計,附源碼,獲取源碼,請在“FPGA技術(shù)江湖”公眾號內(nèi)回復“?電子琴設(shè)計源碼”,可獲取源碼文件。話不多說,上貨。

設(shè)計原理

在之前也出了幾篇源碼系列,基本上都是一些小設(shè)計,源碼系列主要就會想通過實操訓練讓各位學習者,尤其是初學者去更好的理解學習FPGA,或者給要的學生提供一些源碼,之前設(shè)計過各個芯片的配置等,之后筆者會通過簡單的例子來讓大家去系統(tǒng)的學習和認識FPGA。本次的電子琴設(shè)計也算是一次簡單的各個模塊的聯(lián)系調(diào)用的一個過程,也可以幫助各位去加深理解,多動手,熟練掌握會有意想不到的效果。

本次的設(shè)計主要是通過控制ps2鍵盤來使蜂鳴器發(fā)出哆來咪法嗦拉西7種音,音符又有高低音之分等,本次只選擇發(fā)出高音的多來咪發(fā)嗦啦西。本設(shè)計中還用到了VGA的設(shè)計,通過VGA來在顯示屏上畫出如下圖的黑白的電子琴鍵:

當按下多來咪發(fā)嗦啦西時,對應(yīng)的鍵值變顏色表示按下,不變色表示不按下,顏色自己可以調(diào)節(jié),但是琴的按鍵必須為黑白色來顯示出來。

當按下按鍵的時候,蜂鳴器來鳴響對應(yīng)時間的音符,本設(shè)計蜂鳴器響的時間為0.25S一個音符持續(xù)的時間。

本次設(shè)計用到的PS2和VGA的設(shè)計原理筆者在這里就不過多的介紹了,不明白的可以翻看前面發(fā)的文檔內(nèi)容。

在本設(shè)計中介紹蜂鳴器的使用和各音符發(fā)聲的頻率大小。本設(shè)計用的是無源蜂鳴器,原理圖如下:

由于FPGA的驅(qū)動能力不夠,我們添加了一個三極管來驅(qū)動這個無源蜂鳴器,而無源蜂鳴器的主要特點是內(nèi)部不帶振蕩源,所以如果使用直流信號是無法使無源蜂鳴器鳴叫的,必須使用方波去驅(qū)動它。

現(xiàn)在我們明白了,只要往蜂鳴器發(fā)送一定頻率的方波,就可以使得蜂鳴器發(fā)出聲音,然后現(xiàn)在的問題是,我們究竟要往蜂鳴器發(fā)送什么頻率的方波信號呢?具體的頻率可以查看下圖:

現(xiàn)在我們知道如何讓蜂鳴器響起,又知道發(fā)送什么頻率可以讓蜂鳴器響起什么的聲音,所以我相信我們已經(jīng)有能力讓蜂鳴器響起我們需要的音樂了。

設(shè)計架構(gòu)

設(shè)計架構(gòu)圖:

在這里沒有去畫設(shè)計框架圖,就直接給大家展示RTL級視圖,各位也可以通過RTL級視圖看到設(shè)計的總框架。

設(shè)計代碼

頂層模塊music_ps2代碼:

module music_ps2(clk, rst_n, hs, vs, r_g_b, ps2_clk, ps2_data, beep);    input clk;  input rst_n;
  output  hs;  output  vs;  output  [7:0]r_g_b;  output  beep;    input ps2_clk;  input ps2_data;    wire flag;  wire [7:0] data, data_n;  wire clk_1M;      frenp frep_dut(    .clk(clk),    .rst_n(rst_n),    .clk_1M(clk_1M)    );      ps2_rec rec_dut(    .clk(clk_1M),    .rst_n(rst_n),    .ps2_clk(ps2_clk),    .ps2_data(ps2_data),    .flag(flag),    .data(data)  );    decode decode_dut(    .clk(clk_1M),    .rst_n(rst_n),    .flag(flag),    .data(data),    .data_n(data_n)  );    music music_dut(    .clk(clk_1M),    .rst_n(rst_n),    .data_n(data_n),    .beep(beep)  );    vga vga_dut(    .clk(clk),    .rst_n(rst_n),    .hs(hs),    .vs(vs),    .r_g_b(r_g_b),    .data_n(data_n)  );endmodule 

蜂鳴器music模塊代碼:

module music(clk, rst_n, data_n, beep); 端口列表
  input clk;  input rst_n;  input [7:0] data_n;  //輸入的鍵值  output reg beep;     //蜂鳴器    reg [10:0] music_data;  wire [10:0] data;
  always @ (posedge clk)    if(!rst_n)      begin        music_data <= 0;      end    else      case (data_n)        1  :  music_data <= 478;  //蜂鳴器的高音1        2  :  music_data <= 425;  //蜂鳴器的高音2        3  :  music_data <= 379;  //蜂鳴器的高音3        4  :  music_data <= 358;  //蜂鳴器的高音4                5  :  music_data <= 319;  //蜂鳴器的高音5        6  :  music_data <= 284;  //蜂鳴器的高音6        7  :  music_data <= 253;  //蜂鳴器的高音7        default: music_data <= 0;      endcase      reg [20:0] count, cnt;
  always @ (posedge clk)    if(!rst_n && !data_n)      begin        count <= 0;      end    else      if(count < 250_000 - 1)        begin          count <= count + 1;        end      else        begin          count <= 0;        end    //計數(shù)0.25S的時間  assign data = (count == 250_000 - 1) ? music_data : data;     always @ (posedge clk)    if(!rst_n)      begin        cnt <= 1;        beep <= 0;      end    else      if(data == 0)    //控制蜂鳴器不響        begin          cnt <= 1;          beep <= 0;        end      else if(cnt < data)    //計數(shù)對應(yīng)的頻率        begin          cnt <= cnt + 1;        end      else        begin          cnt <= 1;     //蜂鳴器響          beep <= ~beep;        end


endmodule 

frenp模塊代碼:

module frenp(clk,rst_n,clk_1M);
  input clk;  input rst_n;    output reg clk_1M;    reg [4:0] count;    always @(posedge clk or negedge rst_n)    if(!rst_n)      begin        count <= 5'd0;        clk_1M <= 1'b1;      end    else      begin        if(count == 50_000_000 / 1000_000 / 2 - 1)          begin            count <= 5'd0;            clk_1M <= ~clk_1M;          end        else          begin            count <= count + 1'b1;          end      end
endmodule

VGA模塊代碼:

module vga(      clk,      rst_n,      hs,      vs,      r_g_b,      data_n      );  input clk;  input rst_n;  input [7:0] data_n;
  output reg hs;  output reg vs;  output reg [7:0]r_g_b;
  reg [10:0] count_hs;  //列計數(shù)  reg [10:0] count_vs; //行計數(shù)  parameter h_a=96,h_b=48,h_c=640,h_d=16,h_e=800,         v_a=2,v_b=33,v_c=480,v_d=10,v_e=525;

  reg clk_25M;  always @ (posedge clk)    if(!rst_n)      clk_25M <= 1;    else      clk_25M <= ~ clk_25M;  /*================列掃描=================*/
  always@(posedge clk_25M or negedge rst_n )    begin      if(!rst_n)        begin        count_hs<=0;        end       else        begin          if(count_hs==h_e)            count_hs<=0;          else            count_hs<=count_hs+11'd1;        end     end   /*================行掃描=================*/  always@(posedge clk_25M or negedge rst_n )    begin      if(!rst_n)        begin      count_vs<=0;        end       else        begin          if(count_vs==v_e)            count_vs<=0;          else            begin              if(count_hs==h_e)                count_vs<=count_vs+11'd1;              else                count_vs<=count_vs;            end        end     end   /*================列同步=================*/  always@(posedge clk_25M or negedge rst_n )    begin      if(!rst_n)        begin        hs<=1;        end       else        begin          if(count_hs>=h_a)            hs<=1;          else            hs<=0;          end     end   /*================行同步=================*/  always@(posedge clk_25M or negedge rst_n )    begin      if(!rst_n)        begin        vs<=1;        end       else        begin          if(count_vs>=v_a)            vs<=1;          else            vs<=0;        end     end     /*=============有效區(qū)域顯示====================*/  reg yes;  always@(posedge clk_25M or negedge rst_n)    begin      if(!rst_n)        begin          yes <= 0;        end       else        begin          if((count_hs>h_a+h_b)&&(count_hs<h_e-h_d)&&(count_vs>v_a+v_b)&&(count_vs<v_e-v_d))             yes <= 1;            else            yes <= 0;              end    end
  reg [4:0] flag;  always @ (*)    if(!rst_n)      begin        flag <= 0;      end    else if (yes)      begin        if ((count_hs - (144 + 30 * 0)) <= 30  && count_vs < 400 && count_vs > 100 )          flag <= 1;           else if ((count_hs - (144 + 30 * 1)) <= 30  && count_vs < 400 && count_vs > 100 )          flag <= 0;   // 黑        else if ((count_hs - (144 + 30 * 2)) <= 30  && count_vs < 400 && count_vs > 100 )          flag <= 2;             else if ((count_hs - (144 + 30 * 3)) <= 30  && count_vs < 400 && count_vs > 100 )          flag <= 0;         else if ((count_hs - (144 + 30 * 4)) <= 30  && count_vs < 400 && count_vs > 100 )          flag <= 3;           else if ((count_hs - (145 + 30 * 5)) <= 4  && count_vs > 100 )          flag <= 0;                 else if ((count_hs - (149 + 30 * 5)) <= 30  && count_vs < 400 && count_vs > 100 )          flag <= 4;   // 白        else if ((count_hs - (149 + 30 * 6)) <= 30  && count_vs < 400 && count_vs > 100 )          flag <= 0;   // 黑        else if ((count_hs - (149 + 30 * 7)) <= 30  && count_vs < 400 && count_vs > 100 )          flag <= 5;             else if ((count_hs - (149 + 30 * 8)) <= 30  && count_vs < 400 && count_vs > 100 )          flag <= 0;         else if ((count_hs - (149 + 30 * 9)) <= 30  && count_vs < 400 && count_vs > 100 )          flag <= 6;           else if ((count_hs - (149 + 30 * 10)) <= 30  && count_vs < 400 && count_vs > 100 )          flag <= 0;          else if ((count_hs - (149 + 30 * 11)) <= 30  && count_vs < 400 && count_vs > 100 )          flag <= 7;             else if ((count_hs - (150 + 30 * 12))  <= 4  && count_vs > 100 )          flag <= 0;                          else if (count_hs < 186 && count_vs >= 400)            flag <= 1;         else if (count_hs < 190 && count_vs >= 400)            flag <= 0;         else if (count_hs < 234 + 12 && count_vs >= 400)            flag <= 2;          else if (count_hs < 250 && count_vs >= 400)            flag <= 0;           else if (count_hs < 295 && count_vs >= 400)            flag <= 3;                          else if (count_hs < 329 + 12 && count_vs >= 400)            flag <= 4;         else if (count_hs < 329 + 16 && count_vs >= 400)            flag <= 0;          else if (count_hs < 389+12 && count_vs >= 400)            flag <= 5;           else if (count_hs < 389 + 16 && count_vs >= 400)            flag <= 0;          else if (count_hs < 449 + 12   && count_vs >= 400)            flag <= 6;         else if (count_hs < 449 + 16&& count_vs >= 400)            flag <= 0;         else if (count_hs < 510 && count_vs >= 400)            flag <= 7;         else          flag <= 8; ;              end    else      flag<=0;
  reg [1:0] state;      always @ (posedge clk)    if(!rst_n)      begin        state <= 0;        r_g_b<=8'b000_000_00;      end    else       if(data_n)        begin          if(flag == data_n)            r_g_b<=8'b111_100_11;          else if(flag != data_n && flag !=8 && flag !=0)            r_g_b<=8'b111_111_111;          else if (flag == 0)            r_g_b<=8'b000_000_000;          else if(flag == 8)            r_g_b<=8'b000_111_00;        end      else        begin          if(flag !=8 && flag !=0)            r_g_b<=8'b111_111_111;          else if (flag == 0)            r_g_b<=8'b000_000_000;          else if(flag == 8)            r_g_b<=8'b000_111_00;                    end        endmodule

ps_2rec模塊代碼:

module ps2_rec(clk,rst_n,ps2_clk,ps2_data,flag,data);
  input clk;  input rst_n;  input ps2_clk;  input ps2_data;    output reg flag;  output  [7:0] data;    wire  nege_dge;    reg  [1:0] signle_s;  always @ (posedge clk or negedge rst_n)    if(!rst_n)      begin        signle_s <= 2'b11;      end    else      begin        signle_s[0] <= ps2_clk;        signle_s[1] <= signle_s[0];      end    assign nege_dge = ~signle_s[0] && signle_s[1]; 
    reg [3:0] count;  reg [10:0] temp ;    assign data = temp[8:1];    always @  (posedge clk or negedge rst_n)    if(!rst_n)      begin        count <= 4'd0;        flag <= 1'b0;        temp <= 11'd0;      end    else      begin        if(nege_dge && count < 11)          begin            count <= count + 1'd1;            temp[count] <= ps2_data;          end        else          begin            if(count == 11)              begin                count <= 4'd0;                flag <= 1'b1;              end            else              begin                flag <= 1'b0;              end          end      end??????endmodule 

代碼驗證正確無誤,筆者在這邊就不過多的驗證,源工程已提供給各位大俠,如有需要,可以自行獲取,供大家參考學習。

相關(guān)推薦

登錄即可解鎖
  • 海量技術(shù)文章
  • 設(shè)計資源下載
  • 產(chǎn)業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄

任何技術(shù)的學習就好比一個江湖,對于每一位俠客都需要不斷的歷練,從初入江湖的小白到歸隱山林的隱世高人,需要不斷的自我感悟自己修煉,讓我們一起仗劍闖FPGA乃至更大的江湖。