源码系列:基于FPGA的电子琴设计(附源工程)

原创 FPGA技术江湖 2025-01-20 12:04

大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分。大侠可以关注FPGA技术江湖,在“闯荡江湖”、"行侠仗义"栏里获取其他感兴趣的资源,或者一起煮酒言欢。


今天给大侠带来基于FPGA的电子琴设计,附源码,获取源码,请在“FPGA技术江湖”公众号内回复“ 电子琴设计源码”,可获取源码文件。话不多说,上货。


设计原理


在之前也出了几篇源码系列,基本上都是一些小设计,源码系列主要就会想通过实操训练让各位学习者,尤其是初学者去更好的理解学习FPGA,或者给要的学生提供一些源码,之前设计过各个芯片的配置等,之后笔者会通过简单的例子来让大家去系统的学习和认识FPGA。本次的电子琴设计也算是一次简单的各个模块的联系调用的一个过程,也可以帮助各位去加深理解,多动手,熟练掌握会有意想不到的效果。

本次的设计主要是通过控制ps2键盘来使蜂鸣器发出哆来咪法嗦拉西7种音,音符又有高低音之分等,本次只选择发出高音的多来咪发嗦啦西。本设计中还用到了VGA的设计,通过VGA来在显示屏上画出如下图的黑白的电子琴键:

 


当按下多来咪发嗦啦西时,对应的键值变颜色表示按下,不变色表示不按下,颜色自己可以调节,但是琴的按键必须为黑白色来显示出来。

当按下按键的时候,蜂鸣器来鸣响对应时间的音符,本设计蜂鸣器响的时间为0.25S一个音符持续的时间。

本次设计用到的PS2和VGA的设计原理笔者在这里就不过多的介绍了,不明白的可以翻看前面发的文档内容。

在本设计中介绍蜂鸣器的使用和各音符发声的频率大小。本设计用的是无源蜂鸣器,原理图如下:

由于FPGA的驱动能力不够,我们添加了一个三极管来驱动这个无源蜂鸣器,而无源蜂鸣器的主要特点是内部不带振荡源,所以如果使用直流信号是无法使无源蜂鸣器鸣叫的,必须使用方波去驱动它。

 现在我们明白了,只要往蜂鸣器发送一定频率的方波,就可以使得蜂鸣器发出声音,然后现在的问题是,我们究竟要往蜂鸣器发送什么频率的方波信号呢?具体的频率可以查看下图:


 现在我们知道如何让蜂鸣器响起,又知道发送什么频率可以让蜂鸣器响起什么的声音,所以我相信我们已经有能力让蜂鸣器响起我们需要的音乐了。



设计架构


设计架构图:

在这里没有去画设计框架图,就直接给大家展示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) 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 //计数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) //计数对应的频率 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; //列计数 reg [10:0] count_vs; //行计数 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 /*=============有效区域显示====================*/ 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_hsv_a+v_b)&&(count_vs 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

代码验证正确无误,笔者在这边就不过多的验证,源工程已提供给各位大侠,如有需要,可以自行获取,供大家参考学习。

END

福利】:QQ交流群173560979,进群备注名字+学校/企业。
淘宝店铺:https://shop588964188.taobao.com
论坛网址:www.sxznfpga.com
叁芯智能FPGA课程

往期精选 

 
 

【免费】FPGA工程师人才招聘平台

FPGA人才招聘,企业HR,看过来!

系统设计精选 | 基于FPGA的实时图像边缘检测系统设计(附代码)

基于原语的千兆以太网RGMII接口设计

时序分析理论和timequest使用_中文电子版

求职面试 | FPGA或IC面试题最新汇总篇

特惠 | FPGA图像处理专题课,Quartus、ISE、Vivado全涉及

特惠 | FPGA时序分析及约束专题课,Quartus、ISE、Vivado全涉及

资料汇总|FPGA软件安装包、书籍、源码、技术文档…(2024.11.14更新)

FPGA技术江湖广发江湖帖

无广告纯净模式,给技术交流一片净土,从初学小白到行业精英业界大佬等,从军工领域到民用企业等,从通信、图像处理到人工智能等各个方向应有尽有,QQ微信双选,FPGA技术江湖打造最纯净最专业的技术交流学习平台。


FPGA技术江湖微信交流群

加群主微信,备注姓名+学校/公司+专业/岗位进群


FPGA技术江湖QQ交流群

备注姓名+学校/公司+专业/岗位进群

FPGA技术江湖 任何技术的学习就好比一个江湖,对于每一位侠客都需要不断的历练,从初入江湖的小白到归隐山林的隐世高人,需要不断的自我感悟自己修炼,让我们一起仗剑闯FPGA乃至更大的江湖。
评论
  • Ubuntu20.04默认情况下为root账号自动登录,本文介绍如何取消root账号自动登录,改为通过输入账号密码登录,使用触觉智能EVB3568鸿蒙开发板演示,搭载瑞芯微RK3568,四核A55处理器,主频2.0Ghz,1T算力NPU;支持OpenHarmony5.0及Linux、Android等操作系统,接口丰富,开发评估快人一步!添加新账号1、使用adduser命令来添加新用户,用户名以industio为例,系统会提示设置密码以及其他信息,您可以根据需要填写或跳过,命令如下:root@id
    Industio_触觉智能 2025-01-17 14:14 54浏览
  • 电竞鼠标应用环境与客户需求电竞行业近年来发展迅速,「鼠标延迟」已成为决定游戏体验与比赛结果的关键因素。从技术角度来看,传统鼠标的延迟大约为20毫秒,入门级电竞鼠标通常为5毫秒,而高阶电竞鼠标的延迟可降低至仅2毫秒。这些差异看似微小,但在竞技激烈的游戏中,尤其在对反应和速度要求极高的场景中,每一毫秒的优化都可能带来致胜的优势。电竞比赛的普及促使玩家更加渴望降低鼠标延迟以提升竞技表现。他们希望通过精确的测试,了解不同操作系统与设定对延迟的具体影响,并寻求最佳配置方案来获得竞技优势。这样的需求推动市场
    百佳泰测试实验室 2025-01-16 15:45 261浏览
  • 百佳泰特为您整理2025年1月各大Logo的最新规格信息,本月有更新信息的logo有HDMI、Wi-Fi、Bluetooth、DisplayHDR、ClearMR、Intel EVO。HDMI®▶ 2025年1月6日,HDMI Forum, Inc. 宣布即将发布HDMI规范2.2版本。新规范将支持更高的分辨率和刷新率,并提供更多高质量选项。更快的96Gbps 带宽可满足数据密集型沉浸式和虚拟应用对传输的要求,如 AR/VR/MR、空间现实和光场显示,以及各种商业应用,如大型数字标牌、医疗成像和
    百佳泰测试实验室 2025-01-16 15:41 175浏览
  • 随着智慧科技的快速发展,智能显示器的生态圈应用变得越来越丰富多元,智能显示器不仅仅是传统的显示设备,透过结合人工智能(AI)和语音助理,它还可以成为家庭、办公室和商业环境中的核心互动接口。提供多元且个性化的服务,如智能家居控制、影音串流拨放、实时信息显示等,极大提升了使用体验。此外,智能家居系统的整合能力也不容小觑,透过智能装置之间的无缝连接,形成了强大的多元应用生态圈。企业也利用智能显示器进行会议展示和多方远程合作,大大提高效率和互动性。Smart Display Ecosystem示意图,作
    百佳泰测试实验室 2025-01-16 15:37 185浏览
  • 本文介绍瑞芯微开发板/主板Android配置APK默认开启性能模式方法,开启性能模式后,APK的CPU使用优先级会有所提高。触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。源码修改修改源码根目录下文件device/rockchip/rk3562/package_performance.xml并添加以下内容,注意"+"号为添加内容,"com.tencent.mm"为AP
    Industio_触觉智能 2025-01-17 14:09 50浏览
  •  光伏及击穿,都可视之为 复合的逆过程,但是,复合、光伏与击穿,不单是进程的方向相反,偏置状态也不一样,复合的工况,是正偏,光伏是零偏,击穿与漂移则是反偏,光伏的能源是外来的,而击穿消耗的是结区自身和电源的能量,漂移的载流子是 客席载流子,须借外延层才能引入,客席载流子 不受反偏PN结的空乏区阻碍,能漂不能漂,只取决于反偏PN结是否处于外延层的「射程」范围,而穿通的成因,则是因耗尽层的过度扩张,致使跟 端子、外延层或其他空乏区 碰触,当耗尽层融通,耐压 (反向阻断能力) 即告彻底丧失,
    MrCU204 2025-01-17 11:30 127浏览
  • 随着消费者对汽车驾乘体验的要求不断攀升,汽车照明系统作为确保道路安全、提升驾驶体验以及实现车辆与环境交互的重要组成,日益受到业界的高度重视。近日,2024 DVN(上海)国际汽车照明研讨会圆满落幕。作为照明与传感创新的全球领导者,艾迈斯欧司朗受邀参与主题演讲,并现场展示了其多项前沿技术。本届研讨会汇聚来自全球各地400余名汽车、照明、光源及Tier 2供应商的专业人士及专家共聚一堂。在研讨会第一环节中,艾迈斯欧司朗系统解决方案工程副总裁 Joachim Reill以深厚的专业素养,主持该环节多位
    艾迈斯欧司朗 2025-01-16 20:51 123浏览
  • 实用性高值得收藏!! (时源芯微)时源专注于EMC整改与服务,配备完整器件 TVS全称Transient Voltage Suppre,亦称TVS管、瞬态抑制二极管等,有单向和双向之分。单向TVS 一般应用于直流供电电路,双向TVS 应用于电压交变的电路。在直流电路的应用中,TVS被并联接入电路中。在电路处于正常运行状态时,TVS会保持截止状态,从而不对电路的正常工作产生任何影响。然而,一旦电路中出现异常的过电压,并且这个电压达到TVS的击穿阈值时,TVS的状态就会
    时源芯微 2025-01-16 14:23 169浏览
  • 80,000人到访的国际大展上,艾迈斯欧司朗有哪些亮点?感未来,光无限。近日,在慕尼黑electronica 2024现场,ams OSRAM通过多款创新DEMO展示,以及数场前瞻洞察分享,全面展示自身融合传感器、发射器及集成电路技术,精准捕捉并呈现环境信息的卓越能力。同时,ams OSRAM通过展会期间与客户、用户等行业人士,以及媒体朋友的深度交流,向业界传达其以光电技术为笔、以创新为墨,书写智能未来的深度思考。electronica 2024electronica 2024构建了一个高度国际
    艾迈斯欧司朗 2025-01-16 20:45 160浏览
  • 现在为止,我们已经完成了Purple Pi OH主板的串口调试和部分配件的连接,接下来,让我们趁热打铁,完成剩余配件的连接!注:配件连接前请断开主板所有供电,避免敏感电路损坏!1.1 耳机接口主板有一路OTMP 标准四节耳机座J6,具备进行音频输出及录音功能,接入耳机后声音将优先从耳机输出,如下图所示:1.21.2 相机接口MIPI CSI 接口如上图所示,支持OV5648 和OV8858 摄像头模组。接入摄像头模组后,使用系统相机软件打开相机拍照和录像,如下图所示:1.3 以太网接口主板有一路
    Industio_触觉智能 2025-01-20 11:04 72浏览
  • 日前,商务部等部门办公厅印发《手机、平板、智能手表(手环)购新补贴实施方案》明确,个人消费者购买手机、平板、智能手表(手环)3类数码产品(单件销售价格不超过6000元),可享受购新补贴。每人每类可补贴1件,每件补贴比例为减去生产、流通环节及移动运营商所有优惠后最终销售价格的15%,每件最高不超过500元。目前,京东已经做好了承接手机、平板等数码产品国补优惠的落地准备工作,未来随着各省市关于手机、平板等品类的国补开启,京东将第一时间率先上线,满足消费者的换新升级需求。为保障国补的真实有效发放,基于
    华尔街科技眼 2025-01-17 10:44 159浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦