用Quartus和Verilog做个能响的电子琴:从分频值计算到FPGA烧录全流程

张开发
2026/4/18 23:12:29 15 分钟阅读

分享文章

用Quartus和Verilog做个能响的电子琴:从分频值计算到FPGA烧录全流程
从零构建FPGA电子琴音乐频率、Verilog与硬件实现的深度实践第一次听到自己编写的代码通过FPGA发出准确的音阶时那种成就感是难以言喻的。本文将带你完整经历这个神奇的过程——从理解音乐频率背后的数学原理到用Verilog实现分频逻辑最终让开发板唱出旋律。不同于简单的代码复制粘贴我们会深入每个环节的设计思路特别关注那些容易导致仿真通过但板子不响的细节陷阱。1. 音阶频率与数字分频原理钢琴上每个琴键对应的频率并非随意设定而是遵循十二平均律的数学规律。中央A4音的标准频率为440Hz相邻半音之间的频率比为2^(1/12)。对于我们的电子琴项目需要计算C4到C5这8个自然音阶对应钢琴白键的准确频率音阶名称频率计算公式理论频率(Hz)实际应用频率C4440*(2^(-9/12))261.63262D4440*(2^(-7/12))293.66294E4440*(2^(-5/12))329.63330F4440*(2^(-4/12))349.23349G4440*(2^(-2/12))392.00392A4440440.00440B4440*(2^(2/12))493.88494C5440*(2^(3/12))523.25523在数字电路中我们通过时钟分频来产生这些频率。假设使用1MHz1,000,000Hz的基准时钟分频值计算公式为分频值 基准时钟频率 / (2 × 目标频率) - 1以C4音阶为例分频值 1,000,000 / (2 × 262) - 1 ≈ 1907转换为十六进制就是0x773但实际代码中使用的0xEF0即3824看起来是另一套计算逻辑。这是因为代码可能采用了不同的分频实现方式如计数器比较值而非周期数实际频率可能需要微调以避免谐波干扰开发板扬声器特性可能要求特定占空比提示不同开发板的时钟频率可能不同务必确认板载晶振频率。常见的25MHz、50MHz时钟需要相应调整分频计算。2. Quartus工程创建与Verilog核心逻辑实现启动Quartus Prime后按以下步骤创建项目File → New Project Wizard指定工程目录避免中文路径命名工程为Electronic_Organ选择正确的FPGA器件型号如Cyclone IV EP4CE6E22C8添加Verilog文件module electronic_organ ( input clk, // 1MHz时钟 input [7:0] key, // 8个按键输入 output reg speaker ); // 音阶分频参数基于1MHz时钟 parameter [11:0] div_values[7:0] { 12hEF0, // C4 12hD4F, // D4 12hBDA, // E4 12hB31, // F4 12h9F7, // G4 12h8E0, // A4 12h7E8, // B4 12h776 // C5 }; reg [11:0] div_counter 0; reg [11:0] current_div 0; reg [7:0] key_reg; always (posedge clk) begin // 按键消抖处理 key_reg key; // 确定当前分频值 case (1b1) key_reg[0]: current_div div_values[0]; key_reg[1]: current_div div_values[1]; key_reg[2]: current_div div_values[2]; key_reg[3]: current_div div_values[3]; key_reg[4]: current_div div_values[4]; key_reg[5]: current_div div_values[5]; key_reg[6]: current_div div_values[6]; key_reg[7]: current_div div_values[7]; default: current_div 0; endcase // 分频计数器逻辑 if (current_div 0) begin div_counter 0; speaker 0; end else begin if (div_counter current_div) begin div_counter 0; speaker ~speaker; // 翻转输出产生方波 end else begin div_counter div_counter 1; end end end endmodule这段代码做了几项关键改进使用数组存储分频参数提高可读性添加按键寄存器减少亚稳态风险采用更清晰的case语句处理按键优先级明确无按键时的静音处理3. 功能仿真与Testbench编写在烧录到FPGA前必须通过仿真验证逻辑正确性。创建Testbench文件organ_tb.vtimescale 1ns/1ns module organ_tb; reg clk; reg [7:0] key; wire speaker; electronic_organ uut (.*); initial begin clk 0; forever #500 clk ~clk; // 模拟1MHz时钟 end initial begin $dumpfile(wave.vcd); $dumpvars(0, organ_tb); key 8b00000000; #1000; // 测试C4音阶 key 8b00000001; #2000000; // 观察2ms输出 // 测试A4音阶 key 8b00100000; #2000000; // 测试多键同时按下应只响应最高优先级 key 8b00000101; #2000000; $finish; end endmodule使用ModelSim进行仿真时重点关注按键变化后输出频率是否立即切换无按键时是否保持静音多键同时按下的优先级处理输出方波的占空比是否接近50%常见仿真问题排查如果输出始终为高/低电平检查分频计数器复位逻辑如果频率偏差大确认Testbench时钟周期设置正确如果响应延迟可能是按键消抖逻辑过强4. 引脚分配与硬件实现仿真通过后需要将设计映射到实际硬件。以DE10-Lite开发板为例配置时钟开发板提供50MHz时钟需通过PLL分频到1MHz在Quartus中创建PLL IP核设置输入50MHz输出1MHz引脚分配通过Assignment Editor信号名称FPGA引脚开发板对应功能clkPIN_P1150MHz晶振key[0]PIN_C10SW0.........key[7]PIN_C11SW7speakerPIN_A8蜂鸣器硬件连接检查清单确认跳线帽连接蜂鸣器到正确IO检查开关上拉/下拉电阻配置测量电源电压稳定在3.3V确保下载器驱动安装正确烧录程序后若没有声音按以下步骤排查用万用表测量蜂鸣器两端电压是否有变化尝试直接给蜂鸣器引脚输出高电平测试硬件使用SignalTap逻辑分析仪抓取实际输出波形检查PLL锁定信号是否正常5. 进阶优化与扩展思路基础功能实现后可以考虑以下增强功能音效改善方案// 在输出级添加PWM调制改善音质 reg [3:0] pwm_counter; always (posedge clk) begin pwm_counter pwm_counter 1; if (speaker (pwm_counter 4b1000)) audio_out 1; else audio_out 0; end功能扩展建议添加LED显示当前音阶实现按键力度感应通过按压时间录制和回放简单旋律通过UART接收音符指令对于想深入学习的开发者推荐研究使用DDS直接数字合成技术生成更纯净的正弦波添加FIR滤波器消除高频谐波实现ADSR包络控制使音色更丰富完成这个项目后你会惊讶于FPGA在实时音频处理上的潜力。当第一次听到《小星星》从自己设计的电路中流淌出来时那些调试到深夜的时光都变得值得了。

更多文章