1. 将已调制的 AM 信号取绝对值
关于 AM 信号的产生, 参见上一篇博客: AM 调制的 FPGA 实现
简单说明一下对数据取反的思路: 如果是无符号数, 则不存在符号位, 也就是说数据都是正数, 不需要取绝对值; 如果是有符号数, 通过检测最高位的符号位, 如果符号位是 1, 则表示数据是负数, 对数据取反, 如果符号位是 0, 则表示数据是正数, 不需要取反操作.
取绝对值的 Verilog 实现:
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
data_tdata <= 0;
end
else if(AM_mod[15] == 1) begin
data_tdata <= -{AM_mod}; // 如果符号位是 1, 对数据取反
end
else if(AM_mod[15] == 0) begin
data_tdata <= AM_mod; // 如果符号位是 0, 数据不变
end
else begin
data_tdata <= data_tdata;
end
end
2. 使用 FIR 滤波器滤除高频分量
关于 Vivado 的 FIR IP 核可以说是功能很强大的, 但这里不需要其他复杂的功能, 只需要简单的生成一个的低通滤波器就行了.
类似于 ROM 核的生成, 配置 FIR 同样需要 Matlab 配合. 可见, Matlab 的功能是多么强大. 这里 Matlab 的主要作用是对滤波器的性能进行仿真并生成相应的抽头系数.
使用 Matlab 生成 FIR 的抽头系数
在 Matlab 的命令行窗口输入: filterDesigner(以前是用 fdatool 命令, 不过输入 fdatool 也可以, 只是会提醒你改用新的命令) 弹出滤波器设计窗口:
接下来, 对滤波器的一些参数进行设置:
参数设置好后, 点击 Design Filter 按钮查看生成滤波器的幅频响应图, 通过幅频响应等图来判断滤波器是否达到设计要求:
设计的滤波器满足性能指标后需要将抽头系数导出, 保存为. coe 文件. 在导出前需要对系数进行量化. 因为需要解调的 AM 信号也是 16 位宽, 所以这里的位宽设置保持默认值, 这些可以根据实际情况自行修改.
量化过后就能将抽头系数导出为. coe 文件了:
生成 FIR IP 核
IP 核的具体配置如下:
其他保持默认即可:
同样, 在 IP 核配置界面也可以查看滤波器的幅频特性:
IP 核生成完毕后, 就可以编写 IP 核的调用模块了.
FIR IP 核调用模块:
module FIR_Control(
input clk,
input rst_n,
input signed [15:0] s_axis_data_tdata,
output reg [7:0] data_out
);
wire s_axis_data_tready;
wire m_axis_data_tvalid;
wire [39:0] m_axis_data_tdata; // 滤波器输出信号
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
data_out <= 0;
end
else begin
data_out <= m_axis_data_tdata[33:26]; // 根据仿真结果进行截位
end
end
//-------------- 调用 FIR 核 ----------------//
FIR FIR_inst0(
.aclk (clk),
.s_axis_data_tvalid (1), // 拉高时 IP 核开始工作
.s_axis_data_tready (s_axis_data_tready),
.s_axis_data_tdata (s_axis_data_tdata), // 输入信号
.m_axis_data_tvalid (m_axis_data_tvalid), // 拉高时表明数据输出有效
.m_axis_data_tdata (m_axis_data_tdata) // 输出信号
);
//---------------------------------------//
endmodule
需要注意的是:
m_axis_data_tdata 信号是滤波器的数据输出信号, 我们在使用时一般都要对此数据进行截位操作, 如何进行截位需要根据仿真结果来确定. 比如, 在这个工程中, 我需要的滤波器的输出数据是 8 位, 但不能一下子截取高 8 位, 而且 m_axis_data_tdata 是个 40 位的数据, 从仿真波形来看 m_axis_data_tdata[39:34] 都是符号位, 因此从 33 位开始往下截取 8 位数据 (当然也可以从 34 位开始截, 这样的话就多了一位符号位, 相应的数据位就变少了一位).
3. 去直流处理
经过 FIR 滤波后的波形其实就是一个叠加了直流分量的调制信号. 在本工程中, AM 调制是 100% 调制, 也就是说解调时经过 FIR 后的信号的最小值为 0, 可以把它看作是无符号的数, 直接经 DA 输出就行了.
如果不是 100% 调制呢? 也就是说解调时经过 FIR 后的信号的最小值是大于 0 的, 那么这个大于 0 的量就相当于直流, 需要去掉后再经 DA 输出.
因此, 在这个工程中, 不需要去直流处理. 下面给出顶层文件的代码.
顶层模块编写:
module TOP(
input clk,
input rst_n,
output [7:0] AM_demod
);
//--------------------------------//
reg signed [15:0] data_tdata;
wire signed [15:0] AM_mod;
//--------------------------------//
//----------- 取绝对值 -------------//
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
data_tdata <= 0;
end
else if(AM_mod[15] == 1) begin
data_tdata <= -{AM_mod}; // 如果符号位是 1, 对数据取反
end
else if(AM_mod[15] == 0) begin
data_tdata <= AM_mod; // 如果符号位是 0, 数据不变
end
else begin
data_tdata <= data_tdata;
end
end
//--------------------------------//
//-----------AM 已调信号 ------------//
modulate modulate_inst0(
.clk (clk),
.rst_n (rst_n),
.AM_mod (AM_mod)
);
//--------------------------------//
//---------- 滤波器控制模块 ---------//
FIR_Control FIR_Control_inst2(
.clk (clk),
.rst_n (rst_n),
.s_axis_data_tdata (data_tdata),
.data_out (AM_demod)
);
//--------------------------------//
endmodule
4. 解调仿真
编写 TestBeach:
`timescale 1ns/1ps
module tb_AM();
//=================== 解调部分 ====================//
//---------- 接口设置 ----------//
reg sclk;
reg rst_n;
wire [7:0] AM_demod;
//--------------------------//
initial sclk = 1;
always #5 sclk = ~sclk; //100M 时钟
initial begin
rst_n = 0;
#500
rst_n = 1;
end
//---------- 解调模块 ----------//
TOP TOP_inst(
.clk (sclk),
.rst_n (rst_n),
.AM_demod (AM_demod)
);
//---------------------------//
endmodule
仿真结果
由仿真结果可知, 最终输出信号正确还原了已调制信号的包络, 表明解调正确.
来源: https://www.cnblogs.com/HOOKNET/p/8407766.html