用FPGA复刻FM收音机:手把手教你用Xilinx FIR IP实现调频信号解调(附Verilog源码)
2026/5/6 21:09:18 网站建设 项目流程

用FPGA复刻经典FM收音机:从微分到FIR滤波的硬件级信号解码实战

记得小时候拆解过老式收音机吗?那些缠绕着铜线的磁棒、散发着松香味的电路板,还有随着旋钮转动沙沙作响的扬声器,构成了我们对无线通信的最初认知。如今,我们可以在FPGA上重现这个魔法——本文将带你用Xilinx FIR IP核搭建一个完整的FM解调系统,从射频信号中提取出清晰的音频波形。不同于教科书式的理论推导,我们会聚焦如何用Verilog构建真实的信号处理流水线,特别适合那些想通过实践理解数字信号处理的硬件开发者。

1. FM解调的核心原理与硬件实现路径

调频广播之所以能抵抗噪声干扰,关键在于它将音频信息编码在了载波频率的变化中。想象一下歌手演唱时声带的振动——音调高低对应着频率变化,而FPGA要做的就是捕捉这种微妙的变化。传统模拟电路使用鉴频器实现,而数字域我们采用更精确的微分+绝对值+低通滤波方案。

硬件解调三步骤

  1. 微分处理:用当前采样值减去前一个采样值(x[n] - x[n-1]),相当于计算信号的瞬时变化率
  2. 绝对值转换:将双向的微分结果转为单极性信号,保留频率变化信息
  3. FIR低通滤波:滤除高频载波成分,提取原始音频信号

在Xilinx Vivado中,这三个步骤分别对应:

  • 微分:用寄存器实现的延迟减法器
  • 绝对值:简单的数据路径选择逻辑
  • FIR滤波:调用Xilinx FIR Compiler IP核
// 微分模块的Verilog实现片段 always @(posedge clk) begin if(rst_n == 0) begin uf_data_r <= 0; sv_data_r <= 0; end else if(uf_tready_i == 1 && s_axis_data_tvalid == 1) begin sv_data_r <= s_axis_data_tdata; // 缓存前一个采样值 uf_data_r <= s_axis_data_tdata - sv_data_r; // 当前值减前值 end end

2. Vivado工程搭建与IP核配置要点

使用Vivado 2017.4创建工程时,需要特别注意时钟域的匹配。FM广播信号通常位于76-108MHz频段,但我们的演示系统采用1MHz采样率处理100kHz载波信号,这是为了仿真阶段能清晰观察波形变化。

关键IP核配置参数

IP核类型参数项推荐值作用说明
DDS CompilerFrequency Resolution0.01Hz确保载波频率精确
Phase Width16-bit足够相位精度
FIR CompilerFilter TypeLow Pass提取音频基带信号
Coefficient File10kHz截止频率系数文件需自行生成
Data Width16-bit匹配前端处理位宽

创建Block Design时,建议按信号流方向排列IP核:

[DDS调制源] → [微分模块] → [绝对值模块] → [FIR IP] → [AXI-Stream接口]

注意:FIR系数生成可使用MATLAB的fdatool工具,导出.coe文件时选择16位定点数格式。典型参数为:10kHz截止频率、30kHz阻带、1MHz采样率。

3. 从零编写Verilog解调核心代码

完整的FM解调器需要处理AXI-Stream流控信号,以下是模块化设计的关键要点:

3.1 微分模块设计

  • 采用双寄存器结构实现单周期延迟
  • 添加流控握手信号(tready/tvalid)
  • 处理有符号数减法时的位宽扩展
module fm_differentiator ( input clk, rst_n, input signed [15:0] s_axis_data_tdata, input s_axis_data_tvalid, output s_axis_data_tready, output signed [15:0] m_axis_data_tdata, output m_axis_data_tvalid, input m_axis_data_tready ); reg signed [15:0] prev_sample; always @(posedge clk) begin if(~rst_n) prev_sample <= 0; else if(s_axis_data_tvalid && s_axis_data_tready) prev_sample <= s_axis_data_tdata; end assign m_axis_data_tdata = s_axis_data_tdata - prev_sample; assign m_axis_data_tvalid = s_axis_data_tvalid; assign s_axis_data_tready = m_axis_data_tready; endmodule

3.2 绝对值转换逻辑

  • 利用符号位进行条件取反
  • 保持数据有效标志同步传递
always @(posedge clk) begin if(abs_tready_i == 1 && uf_valid_r == 1) begin abs_data_r <= uf_data_r[15] ? -uf_data_r : uf_data_r; end end

3.3 FIR接口适配

  • 处理FIR IP核的40位输出截位
  • 时钟域交叉处理(如果存在多时钟域)

4. 仿真调试与性能优化技巧

在testbench中生成4kHz音频调制的100kHz载波信号,通过观察各阶段波形验证设计正确性:

典型仿真波形特征

  • 调制阶段:载波频率随调制信号周期性变化
  • 微分输出:呈现调制信号的包络形态
  • 滤波前:包含高频毛刺的脉冲信号
  • 滤波后:光滑的音频正弦波

资源优化策略

  1. 流水线设计:在微分和绝对值模块间插入寄存器
  2. 系数对称性:启用FIR IP的对称系数优化选项
  3. 位宽压缩:在保证信噪比前提下降低内部数据位宽
// 测试激励生成示例 initial begin rst_n = 0; #100 rst_n = 1; // 模拟FM信号输入 repeat(1000) begin @(posedge clk); s_axis_data_tvalid <= 1; s_axis_data_tdata <= $sin($time/1e6*2*3.14*100e3); end end

5. 进阶扩展:从仿真到实际电台接收

完成基础验证后,可以通过以下步骤实现真实FM信号接收:

  1. 增加前端电路

    • 使用RTL-SDR之类的射频前端
    • 添加ADC采样模块
  2. 动态滤波处理

    • 根据信号强度自适应调整FIR参数
    • 实现自动增益控制(AGC)
  3. 音频后处理

    • 添加数字音量控制
    • 实现立体声解码(需要PLL提取导频)
// 动态系数重载示例 wire [15:0] new_coeffs [0:127]; fir_reloader reload_inst ( .clk(clk), .new_coeffs(new_coeffs), .reload_trigger(agc_gain_change) );

调试时最常遇到的问题是滤波后的音频存在失真,这时需要检查:

  • 微分环节是否发生数据溢出
  • FIR系数是否与采样率匹配
  • 时钟域交叉处的同步处理

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询