Quartus原理图电子琴:FPGA分频查表与消抖设计

发布时间:2026/10/28 4:49:43
Quartus原理图电子琴:FPGA分频查表与消抖设计 简介这份资源是一套基于Quartus II原理图设计方法完成的电子琴FPGA工程面向已具备数字电路基础、希望从门电路搭建过渡到完整项目的初学者与课程设计学生。工程把音符频率生成、定时计数、DA转换接口、按键控制器与乐曲存储等模块以原理图方式连接可直接用于编译、波形仿真和下载验证帮助理解数字逻辑在音乐合成中的具体落地过程。压缩包为rar格式共413个文件、约5.42MB其中bdf原理图与bsf模块符号构成设计主体vwf波形仿真文件、vhd描述文件、mif存储器初始化文件、sof下载比特流以及qpf、qsf工程配置、rpt编译报告和大量cdb、hdb编译数据库共同组成完整可复现的工程目录便于按模块查阅与二次修改。该资源已有1045人学习下载适合用来对照理解FPGA设计流程中的模块划分、时钟与计数精度处理及资源占用优化思路。1. 需求先立住为什么这套电子琴要选 Quartus 原理图而不是纯代码带过几届做数字系统课设的学生之后我发现一个反直觉的现象讲电子琴这个题目最难的不是写出分频器而是让评审一眼看懂信号是怎么从按键走到蜂鸣器的。纯 HDL 写法把这条链路藏在几个 always 块里答辩时只能靠嘴讲而用 Quartus 原理图设计方法做电子琴程序顶层一张 .bdf 图就能把「按键输入—音符选择—分频—发声—显示」画成一条看得见的通路谁在驱动谁、哪个信号跨了时钟域全都摆在纸面上。这套方案适合三类人要做数字电路课程设计的学生、要交 FPGA 综合实践项目的同学以及需要一个结构清晰、能演示、能讲解的小型数字系统的从业者。它不追求功能堆砌追求的是链路完整、可测、可复现。2. 总体架构设计顶层原理图与三个层级的职责划分原理图设计的第一个决定不是画什么元件而是先决定「这张图上有几层」。我一般按输入、控制、输出三段切顶层只放连线、不放心思所有算数、判断、译码都下沉到子图或者自定义元件里。这样做的直接好处是顶层图能当答辩 PPT 用评审看顶层就懂系统边界追问细节再点进子图。2.1 顶层 .bdf 的端口定义与信号走向顶层端口通常按四组来定这个划分在 Quartus 的引脚分配和后面的测试夹具设计里会一直沿用端口组典型位宽方向说明clk1输入板载晶振50 MHz 最常见rst_n1输入低电平复位按键直接接key813输入音符按键按音域数量定beep1输出驱动无源蜂鸣器seg / sel8 / 46输出数码管段码与位选顶层图里不要把复位同步、按键消抖这些事散落在各处统一由一个input_ctrl子图出口一个note_idx总线控制层只认这个总线。信号走向就变成一条干净的链key → input_ctrl → note_idx → tone_gen → beep显示支路从note_idx分出去。评审问「按键抖动会不会影响音准」时你指着这条链就能回答抖动在input_ctrl里就被吃掉了下游拿到的已经是稳定索引音准只跟分频系数有关。有一件事要提前想清楚音符索引是用二进制编码还是独热码。八到十三个键二进制 4 位编码省事但后续查表要加一级译码独热码可以直接当 ROM 地址的低位掩码用逻辑更直白。我的做法是前面用二进制编码进 ROM 之前用一个小译码子图转成地址这两张图分开改音域的时候只改译码那张。2.2 三层模块划分与自定义元件生成原理图设计的核心技能不是拖元件而是知道什么时候该把一个功能做成自定义元件.bsf。判断标准很朴素这个功能要用超过三个基本门或者两个 LPM 元件才能搭出来就写一个 Verilog 文件、生成符号、放进图里。硬用门电路去搭一个 20 ms 计数器图会变成蜘蛛网改一个参数要动十几根线这是血泪经验。三层划分大致是输入层按键同步、消抖、编码成note_idx外加一个「无按键」检测输出key_valid。控制层音符频率选择ROM 或 case 译码、节拍计数、可选的自动演奏状态机。输出层分频链产生方波、数码管动态扫描、LED 指示。生成符号的操作路径是写好debounce.v在 Quartus 里对它执行「Create Symbol Files for Current File」工程目录下会多出一个.bsf然后在顶层 bdf 里用 Insert Symbol 把它放进来。注意生成符号之后如果改了端口必须重新生成一次 .bsf否则顶层图引用的还是旧端口编译报「port not found」的时候十有八九是这个原因这个坑后面还会专门讲。子图的层次不要超过三层。顶层 三个功能子图 若干自定义元件已经足够表达一个电子琴再往下拆导航成本会超过收益而且 Quartus 的层次化导航在原理图模式下并不好用。2.3 工程建立与器件、引脚的落地配置新建工程的顺序建议固定下来别每次凭记忆先选器件1 万逻辑单元量级的 FPGA 足够这套设计实际占用通常不到 10%再选顶层实体类型为 Block Diagram/Schematic File最后再进引脚分配。器件选大了编译慢选小了后面加自动演奏功能可能放不下 ROM。引脚分配我习惯用 tcl 脚本而不是图形界面点原因是换个板子只要改脚本里的引脚号不用重新点一遍。写法是# 时钟与复位 set_location_assignment PIN_E1 -to clk set_location_assignment PIN_M1 -to rst_n # 音符按键 8 个低有效 set_location_assignment PIN_J1 -to key[0] set_location_assignment PIN_J2 -to key[1] set_location_assignment PIN_K1 -to key[2] set_location_assignment PIN_K2 -to key[3] # 蜂鸣器与数码管位选 set_location_assignment PIN_A1 -to beep set_location_assignment PIN_B1 -to sel[0] # IO 电平标准按板上外设电压选 set_instance_assignment -name IO_STANDARD 3.3-V LVTTL -to beep这段脚本放在工程根目录用 Tools → Tcl Scripts 执行一次即可。关键参数只有两个-to后面必须和原理图顶层端口名完全一致包括总线下标IO_STANDARD要跟板上外设的供电电压对齐。曾经有个板子数码管是 5 V 供电、FPGA 是 3.3 V段码输出按 3.3 V 标准配亮度明显偏暗后来换成开漏输出加外部上拉才正常这类电平匹配问题在原理图上完全看不出来只能靠引脚配置和实测。3. 音频通路设计从 50 MHz 分频到音符频率表电子琴的音准完全取决于分频系数算得对不对这一块是整个设计里唯一「差之毫厘、听感立刻不对」的部分。很多翻车案例不是逻辑写错是分频系数按整数近似得太粗或者把「翻转周期」和「输出周期」搞混导致整体高八度或者低八度。3.1 分频系数的计算与取整误差控制原理图里的分频链本质是一个计数器计数到 N-1 时输出翻转输出方波频率就是f_clk / (2N)。所以要得到目标频率 f分频系数是N f_clk / (2f)。50 MHz 时钟下中音区各音符的系数如下音符目标频率(Hz)分频系数 N实际频率(Hz)相对偏差C4261.6395555261.6270.001%D4293.6685132293.6620.001%E4329.6375843329.6300.000%F4349.2371584349.2320.001%G4392.0063776391.9970.001%A4440.0056818440.0030.001%B4493.8850620493.8770.001%C5523.2547780523.2310.004%误差全部在千分之一以内人耳完全分辨不出。这里有个容易踩的坑有人为了「省资源」把计数器位宽压到 12 位最大只能计到 4095那中音区以下根本发不出来只能发高音听起来像蚊子叫。计数器位宽按最大分频系数算25 MHz 除以最低音频率取足够位宽多几位寄存器在这个规模下不心疼。N 值怎么进原理图有两种做法。做法一是用 LPM 比较器加计数器搭一个「可变模计数器」N 由一个多路选择器从常量里选。做法二是把 N 存进 LPM_ROM用音符索引当地址输出直接接到比较器的 B 端。第二种更好改换音域、加半音、甚至做十二平均律之外的调式只要改 mif 文件图一根线都不用动。3.2 音符频率表与查表结构用 ROM 查表的实现路径是在 Quartus 的 MegaWizard 里实例化LPM_ROM字宽 17 位够放 95555深度取 16 或 32初始化文件用 mif。mif 的写法要严格WIDTH17; DEPTH16; ADDRESS_RADIXUNS; DATA_RADIXUNS; CONTENT BEGIN 0 : 95555; -- C4 1 : 85132; -- D4 2 : 75843; -- E4 3 : 71584; -- F4 4 : 63776; -- G4 5 : 56818; -- A4 6 : 50620; -- B4 7 : 47780; -- C5 8 : 42566; -- D5 9 : 37921; -- E5 10 : 35794; -- F5 11 : 31888; -- G5 12 : 28409; -- A5 13 : 25310; -- B5 14 : 95555; -- 保留位无按键时给一个默认值 15 : 95555; END;注意最后两行的处理。无按键时如果地址线悬空或者给了一个未定义值ROM 输出可能是 0分频系数变 0计数器立刻溢出或者干脆不翻转蜂鸣器要么静音要么发一个极高频的啸叫。我一般会用key_valid去控制输出使能而不是指望 ROM 的默认值双保险。ROM 的地址位宽和深度要对上深度 16 对应地址 4 位如果原理图里接了 5 位地址多出来的高位会被忽略或者报错编译警告里会提示地址越界别当没看见。查表结构还有一个隐性好处可以做「音色」的雏形。把 ROM 扩成两个区一个区放分频系数、一个区放占空比阈值输出级按占空比比较产生不同波形方波、25% 占空比的脉冲音色就不一样了。这在答辩里是很好的加分点实现成本只是多加一根地址线和一组比较逻辑。3.3 输出级驱动与占空比选择蜂鸣器分有源和无源这个必须提前确认。有源蜂鸣器内部自带振荡电路给直流就响频率固定你分频分得再准它也只发一个音无源蜂鸣器才需要外部方波驱动才是电子琴的正确选择。曾经有人拿有源蜂鸣器做了一整套分频链上板发现所有按键响的是同一个音以为是分频器写错了查了一下午最后发现是器件选错了这种坑只能靠清单式核对避免。占空比用 50% 是最省事的分频器翻转一次就是 50%。如果想要更响一点可以让高电平占比大一些但不要超过 70%否则方波的谐波成分变化会导致音色发闷。音量控制用 PWM 斩波实现在音频方波上再叠一层几 kHz 的使能信号占空比决定响度这一层加不加看演示需要不加也不影响功能评分。IO 驱动能力要注意。蜂鸣器工作电流常在 20 mA 以上而 FPGA 单个 IO 的持续输出能力有限长期直驱有风险。常规做法是加一只三极管做开关基极串限流电阻原理图上就是一根线加一个器件评审看到这个细节会认为你考虑过工程实际。引脚分配时把 beep 所在的 IO 驱动强度适当调高再配合外部三极管稳定性就够用了。4. 人机交互设计按键消抖、数码管扫描与演奏记录输入输出这两块决定了演示时的观感。按键抖一下、数码管闪一下功能明明是对的演示效果却像没做完。这一章的两个模块我建议都做成自定义元件图面干净参数也好调。4.1 按键同步与消抖状态机机械按键的抖动时间典型值在 520 ms直接进逻辑会导致一次按下被识别成好几次。消抖的标准做法是先做两级触发器同步消除亚稳态再用一个计数器在输入稳定 20 ms 后才更新输出。写成自定义元件// 20ms 消抖clk 50MHz 时 COUNT_MAX 1_000_000 module debounce #(parameter COUNT_MAX 20d1_000_000) ( input wire clk, input wire rst_n, input wire key_in, // 原始按键低有效 output reg key_out // 消抖后输出低有效 ); reg [1:0] sync; // 两级同步防亚稳态 reg [19:0] cnt; reg key_stable; always (posedge clk or negedge rst_n) begin if (!rst_n) begin sync 2b11; cnt 20d0; key_stable 1b1; key_out 1b1; end else begin sync {sync[0], key_in}; // 输入与当前稳定值不同才开始计时 if (sync[1] ! key_stable) begin if (cnt COUNT_MAX - 1) begin cnt 20d0; key_stable sync[1]; key_out sync[1]; end else begin cnt cnt 1b1; end end else begin cnt 20d0; // 输入回到稳定值计数清零 end end end endmodule三个地方值得说。COUNT_MAX做成参数换不同频率的晶振只改实例化时的参数不用改逻辑同步级数固定两级少一级在跨时钟或者异步输入下有亚稳态风险多一级纯属浪费计数清零的条件是「输入回到当前稳定值」这样短于 20 ms 的毛刺完全不会影响输出也不会因为连续抖动把计数器拖长。参数COUNT_MAX在 50 MHz 下对应 20 ms如果时钟是 27 MHz改成 540000 即可比例换算别算错一个零算错一个零就是 200 ms 的迟钝手感。多个按键可以共享一个消抖模块的计数器吗不行。每个按键的抖动是独立的共享计数器会让按键之间互相干扰A 键按下把 B 键的计数清零出现「连按两个键只有一个响」。老老实实例化 N 个资源占用可以接受。4.2 数码管动态扫描与显示内容组织数码管动态扫描的本质是分时复用位选轮流有效段码同步切换靠人眼视觉暂留合成稳定显示。扫描频率要满足两个条件单个数码管刷新率高于 50 Hz 不闪整体帧率高到不出现拖影。常用的取值是每位 1 ms四位就是 250 Hz 帧率六位是 166 Hz都够用。// 1kHz 位扫描clk 50MHz每位停留 1ms module seg_scan #(parameter DIV 16d50_000) ( input wire clk, input wire rst_n, input wire [15:0] value, // 4 位十六进制显示内容 output reg [3:0] sel, // 位选低有效 output reg [7:0] seg // 段码共阳低有效 ); reg [15:0] cnt; reg [1:0] idx; reg [3:0] cur; always (posedge clk or negedge rst_n) begin if (!rst_n) begin cnt 16d0; idx 2d0; end else if (cnt DIV - 1) begin cnt 16d0; idx idx 1b1; // 轮流点亮下一位 end else begin cnt cnt 1b1; end end // 当前位对应的数据 always (*) begin case (idx) 2d0: cur value[3:0]; 2d1: cur value[7:4]; 2d2: cur value[11:8]; default: cur value[15:12]; endcase end // 位选与段码译码共阳0 有效 always (*) begin sel ~(4b0001 idx); case (cur) 4h0: seg 8hC0; 4h1: seg 8hF9; 4h2: seg 8hA4; 4h3: seg 8hB0; 4h4: seg 8h99; 4h5: seg 8h92; 4h6: seg 8h82; 4h7: seg 8hF8; 4h8: seg 8h80; 4h9: seg 8h90; default: seg 8hFF; // 其余显示熄灭 endcase end endmodule段码表是共阳的共阴要把每一位取反这个搞反了表现是「该亮的段全灭、该灭的全亮」一眼能看出来。位选用移位加取反四位数码管对应sel[3:0]如果板子上位选是高有效把取反去掉即可。value端口设计成 16 位十六进制显示音符序号、频率值、当前音名都够用如果只想显示一位音名可以把这个模块简化成单字符版本。显示内容怎么组织取决于演示想给人看什么。我的习惯是第一位数码管显示当前按下的音名C、D、E 用 1、2、3 代替或者用七段拼字母后三位显示该音的分频系数。答辩时按一下键显示立刻跳变评审能直接对上「按键—索引—系数—发声」整条链比任何口头解释都有效。这个映射用一张小的组合逻辑表就能做放在顶层和 seg_scan 之间。4.3 演奏记录与回放模块的取舍自动演奏、录音回放是很常见的加分需求但从原理图设计的角度要算清楚投入产出。回放的本质是把一串「音符索引 持续时长」存进片上 RAM用一个地址计数器顺序读出节拍由另一个计数器控制。片上 RAM 用LPM_RAM或者LPM_ROM都行存一首 32 音符的短曲字宽 8 位高 4 位音名、低 4 位时值存储量微不足道。真正花时间的是曲谱录入不是逻辑。我的建议是如果课设评分不要求先不做回放把按键响应、音准、显示这三件事做扎实如果要做用 ROM 固化的方式而不是实时录音录音需要额外的写控制、地址管理和按键组合逻辑量翻倍而且演示时容易因为节奏按键不熟而出错。用 ROM 固化一首曲子按一个「播放」键就自动弹演示风险最低。回放和手动弹奏之间要做优先级仲裁播放状态下按任意音符键立即停止播放、切换到手动模式。这个仲裁用一个二选一的多路选择器加一个小状态位就够了放在控制层别塞进音符发生子图里否则子图会同时依赖按键和播放状态复用性就没了。5. 避坑与常见问题排查五个最容易翻车的点这一章按「现象 → 原因 → 解决」写全是这套设计里实际会撞上的问题。调试时按顺序排查能省掉大量时间。第一所有按键发同一个音或者干脆不响。现象是分频链明显在工作示波器能看到方波但频率不随按键变化。原因分两种蜂鸣器用了有源型号外部频率对它无效或者 ROM 地址根本没接上note_idx一直读的是地址 0。解决是先换无源蜂鸣器验证再用 SignalTap 抓一下 ROM 的 address 端口按键时地址是否跳变。地址不跳多半是编码子图输出没连到 ROM原理图上看着有线实际是总线宽度不匹配被优化掉了编译报告里的 warning 要逐条看。第二编译报「cant resolve symbol」或者找不到端口。现象是改了自定义元件的端口之后顶层编译失败。原因是 .bsf 符号文件没有随源文件更新。解决是对修改过的 .v 文件重新执行一次 Create Symbol Files for Current File然后在顶层把旧符号删掉重新插入。这个操作我每次改完端口都会强制走一遍比事后找报错快得多。第三音准整体偏高或偏低一个固定比例。现象是所有音符听起来都对但跟标准音高差一截。原因通常是分频公式用错把N f_clk/(2f)写成了N f_clk/f输出低了一个八度或者计数器是「计到 N 清零」而不是「计到 N-1 清零」差一个周期频率越高偏差越明显。解决是拿 A4 校准用手机上的调音器应用测一下440 Hz 附近就对偏低八度就把系数减半别一个个改。第四数码管显示重影或者只有一位亮。现象是多位数码管上出现叠加的模糊数字或者只有最右一位正常。原因是扫描频率太低低于 50 Hz 会闪或者位选和段码更新不同步切换位选的瞬间段码还是上一位的数据。解决是把位选切换和段码译码放在同一个时钟沿更新中间不要插入组合逻辑延迟扫描分频系数按每位 1 ms 算别为了省资源把分频比调得太大。第五换一块板子之后整机不工作。现象是编译通过、下载成功但没有任何反应。原因是引脚分配还是旧板子的或者 IO 电平标准和新板子的外设电压不匹配。解决是把引脚配置做成 tcl 脚本换板子只改脚本同时核对晶振频率旧板 50 MHz、新板 27 MHz 的话所有分频系数都得按比例重算这一步漏了就会出现「音准全错但功能正常」的玄学现象。6. 验收与答辩用一张测试表把系统跑通讲清楚这套设计的验收不需要复杂仪器一块板子、一个能测频率的示波器或者一个调音器应用就够了。我一般会准备一张测试表逐项打勾答辩时直接投出来评审看到的是可验证的结论而不是「我做了个电子琴」。测试项操作方法合格判据静态电流与上电上电不按键无异常发热数码管显示默认值单键发声依次按 8 个音符键每键发声音高递增无哑音音准按 A4 键用调音器测440 Hz ± 2 Hz消抖快速连按同一键 20 次无误触发、无漏触发显示同步按键同时看数码管音名与系数同步跳变多键同时按同时按两个键按预定优先级发声不出现啸叫长时间运行连续弹奏 10 分钟无死机、无音准漂移音准这一项最容易出问题也最容易验证。用调音器应用对着蜂鸣器测如果测出来是 442 或者 438说明分频系数的取整方向有系统性偏差可以把 N 值统一加一或减一试试如果测出来是 880那就是高了一个八度回头查公式。别用耳朵判断音准人的音高感在连续听同一段旋律时会漂移这是玄学靠不住。答辩演示的节奏我建议固定成三段每段不超过两分钟。第一段讲架构把顶层 bdf 投出来用手指沿着key → input_ctrl → tone_gen → beep走一遍说清楚每一级做什么、为什么这么切第二段做功能演示从低音到高音依次按键让评审听到音阶是连续的再快速连按展示消抖第三段讲一个具体的设计决策比如为什么用 ROM 查表而不是 case 译码或者为什么消抖要做两级同步这一段是拉开差距的地方讲得具体评审就会认为你真的动手调过。如果时间还有富余可以现场改一个参数演示可维护性把 mif 里某个音符的分频系数改掉重新编译下载让评审听到那个音变了。这个动作比任何 PPT 都有说服力因为它证明这套原理图不是一次性的死图而是可配置的工程。最后说个习惯。从那以后我每次带人做这套设计都强制在验收前走一遍「换一块板子重新配引脚、重新测音准」的流程哪怕时间很紧也不跳过。因为所有翻车都发生在「我以为这里不用改」的地方而验收表就是那剂后悔药。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询