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

在之前也出了幾篇源碼系列,基本上都是一些小設計,源碼系列主要就會想通過實操訓練讓各位學習者,尤其是初學者去更好的理解學習FPGA,或者給要的學生提供一些源碼,之前設計過各個芯片的配置等,之后筆者會通過簡單的例子來讓大家去系統(tǒng)的學習和認識FPGA。本次的電子琴設計也算是一次簡單的各個模塊的聯(lián)系調用的一個過程,也可以幫助各位去加深理解,多動手,熟練掌握會有意想不到的效果。
本次的設計主要是通過控制ps2鍵盤來使蜂鳴器發(fā)出哆來咪法嗦拉西7種音,音符又有高低音之分等,本次只選擇發(fā)出高音的多來咪發(fā)嗦啦西。本設計中還用到了VGA的設計,通過VGA來在顯示屏上畫出如下圖的黑白的電子琴鍵:
?

當按下多來咪發(fā)嗦啦西時,對應的鍵值變顏色表示按下,不變色表示不按下,顏色自己可以調節(jié),但是琴的按鍵必須為黑白色來顯示出來。
當按下按鍵的時候,蜂鳴器來鳴響對應時間的音符,本設計蜂鳴器響的時間為0.25S一個音符持續(xù)的時間。
本次設計用到的PS2和VGA的設計原理筆者在這里就不過多的介紹了,不明白的可以翻看前面發(fā)的文檔內容。
在本設計中介紹蜂鳴器的使用和各音符發(fā)聲的頻率大小。本設計用的是無源蜂鳴器,原理圖如下:

由于FPGA的驅動能力不夠,我們添加了一個三極管來驅動這個無源蜂鳴器,而無源蜂鳴器的主要特點是內部不帶振蕩源,所以如果使用直流信號是無法使無源蜂鳴器鳴叫的,必須使用方波去驅動它。
?現(xiàn)在我們明白了,只要往蜂鳴器發(fā)送一定頻率的方波,就可以使得蜂鳴器發(fā)出聲音,然后現(xiàn)在的問題是,我們究竟要往蜂鳴器發(fā)送什么頻率的方波信號呢?具體的頻率可以查看下圖:

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

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


頂層模塊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)beginmusic_data <= 0;endelsecase (data_n)1 : music_data <= 478; //蜂鳴器的高音12 : music_data <= 425; //蜂鳴器的高音23 : music_data <= 379; //蜂鳴器的高音34 : music_data <= 358; //蜂鳴器的高音45 : music_data <= 319; //蜂鳴器的高音56 : music_data <= 284; //蜂鳴器的高音67 : music_data <= 253; //蜂鳴器的高音7default: music_data <= 0;endcasereg [20:0] count, cnt;always @ (posedge clk)&& !data_n)begincount <= 0;endelse< 250_000 - 1)begincount <= count + 1;endelsebegincount <= 0;end//計數(shù)0.25S的時間assign data = (count == 250_000 - 1) ? music_data : data;always @ (posedge clk)if(!rst_n)begincnt <= 1;beep <= 0;endelse== 0) //控制蜂鳴器不響begincnt <= 1;beep <= 0;endelse if(cnt < data) //計數(shù)對應的頻率begincnt <= cnt + 1;endelsebegincnt <= 1; //蜂鳴器響beep <= ~beep;endendmodule
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)begincount <= 5'd0;clk_1M <= 1'b1;endelsebeginif(count == 50_000_000 / 1000_000 / 2 - 1)begincount <= 5'd0;clk_1M <= ~clk_1M;endelsebegincount <= count + 1'b1;endendendmodule
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;elseclk_25M <= ~ clk_25M;/*================列掃描=================*/always@(posedge clk_25M or negedge rst_n )beginif(!rst_n)begincount_hs<=0;endelsebeginif(count_hs==h_e)count_hs<=0;elsecount_hs<=count_hs+11'd1;endend/*================行掃描=================*/always@(posedge clk_25M or negedge rst_n )beginif(!rst_n)begincount_vs<=0;endelsebeginif(count_vs==v_e)count_vs<=0;elsebeginif(count_hs==h_e)count_vs<=count_vs+11'd1;elsecount_vs<=count_vs;endendend/*================列同步=================*/always@(posedge clk_25M or negedge rst_n )beginif(!rst_n)beginhs<=1;endelsebeginif(count_hs>=h_a)hs<=1;elsehs<=0;endend/*================行同步=================*/always@(posedge clk_25M or negedge rst_n )beginif(!rst_n)beginvs<=1;endelsebeginif(count_vs>=v_a)vs<=1;elsevs<=0;endend/*=============有效區(qū)域顯示====================*/reg yes;always@(posedge clk_25M or negedge rst_n)beginif(!rst_n)beginyes <= 0;endelsebeginif((count_hs>h_a+h_b)&&(count_hsv_a+v_b)&&(count_vs yes <= 1;elseyes <= 0;endendreg [4:0] flag;always @ (*)if(!rst_n)beginflag <= 0;endelse if (yes)beginif ((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;elseflag <= 8; ;endelseflag<=0;reg [1:0] state;always @ (posedge clk)if(!rst_n)beginstate <= 0;r_g_b<=8'b000_000_00;endelseif(data_n)beginif(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;endelsebeginif(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;endendmodule
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)beginsignle_s <= 2'b11;endelsebegin<= ps2_clk;<= signle_s[0];endassign 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)begincount <= 4'd0;flag <= 1'b0;temp <= 11'd0;endelsebegin&& count < 11)begincount <= count + 1'd1;<= ps2_data;endelsebegin== 11)begincount <= 4'd0;flag <= 1'b1;endelsebeginflag <= 1'b0;endendend??????endmodule

END
往期精選?
?
?

FPGA技術江湖廣發(fā)江湖帖
無廣告純凈模式,給技術交流一片凈土,從初學小白到行業(yè)精英業(yè)界大佬等,從軍工領域到民用企業(yè)等,從通信、圖像處理到人工智能等各個方向應有盡有,QQ微信雙選,F(xiàn)PGA技術江湖打造最純凈最專業(yè)的技術交流學習平臺。
FPGA技術江湖微信交流群

加群主微信,備注姓名+學校/公司+專業(yè)/崗位進群
FPGA技術江湖QQ交流群

備注姓名+學校/公司+專業(yè)/崗位進群
