概述
基于高云FPGA的FM调制与解调
- 一、概述
- 二、平台
- 三、要求
- 四、原理
- 五、调试过程
- 1、FM调制
- 2、FM解调
- 六、程序
- (1)生成.mi文件
- (2)主程序
- (3)微分
- 六、注意事项
一、概述
基于高云FPGA开发板自主设计支持FM调制方式的数字发射机,FM调制外部输入的音频信号,可使FM接收机正确接收并播放。
二、平台
Gowin_V1.9.8
Modelsim SE-64 2019.2
(如果用Gowin,首先要学会Gowin和Modelsim 如何进行联调;用vivado可忽略)
三、要求
(1)FM调制信号由外部音频输入;
(2)FM发射机在国家划分的FM广播频段,包含多个电台频率;
(3)调制信号通过天线发出,接收距离可达两米以上;
四、原理
FM调制的结构如下图所示:
调制信号是用DDS产生正弦信号来模拟实现的。DDS的实现需要用到ROM IP核,在配置ROM IP核之前需要用Matlab生成IP核所需要的.mi文件。
在FM调制中,有两个比较重要的概念:中心频率和频偏。中心频率可以理解为只有“Freq”时的频率;频偏可以理解为当再加上一个变量后,输出频率大小与中心频率的差值。因此FM调制的关键就在于如何确定“Freq”这个常量后面再加上的这个变量的值。
假设中心频率为:5M,频偏为:-75KHz ~ 75KHz。当输入调制信号幅度为0时,输出的FM已调信号频率为5MHz,即载波频率;当输入调制信号幅度最大(即+211)时,输出的FM已调信号频率为5.075MHz(5M+75K);当输入调制信号幅度最小(即-211)时,输出的FM已调信号频率为4.925MHz(5M-75K)。根据以上情况,可以通过调制信号的大小计算出此时输出频率的大小。总结如下表:
五、调试过程
1、FM调制
(1)首先,通过MATLAB生产.mi文件,.mi文件里是一个正弦波的采样:
(2)Gowin_pROM ip核调用.mi文件生成一个5k HZ、12位的正弦调制波。(通过rom_addr地址的长度控制频率)
(3)通过调制波幅度控制载波频率;生成中心频率为500k HZ,频偏为:-15k Hz ~ 15k Hz的已调信号。(根据调制波幅度修改rom_addr地址的长度)
(4)最后截取已调信号的高8位作为输出。
2、FM解调
(1)首先对FM已调波进行微分,将调制信号与载波的频率关系转换为幅度关系;
(2)再进行FIR低通滤波,得到滤波信号。(可能是截止频率计算得有问题,信号不平滑)
(3)由于滤波后的信号不平滑,故增加一个均值滤波算法。
六、程序
(1)生成.mi文件
x = linspace(0,6.28,4096); %在0和2pi间取1024个点
y1 = sin(x)+1;
y1 = round(y1 * 2047);
fid = fopen('sine.mi','wt'); % 生成.mi文件
fprintf(fid,'#File_format=Hexn');
fprintf(fid,'#Address_depth=4096n');
fprintf(fid,'#Data_width=12n');
fprintf(fid,'%03xn',y1); % 生成三位数据,不足的在前面补零
fclose(fid);
(2)主程序
module FM_mod(
input clk,
input rst_n,
output wire [11:0] Data_in_carry,
output wire [11:0] Data_in_modulate,
output wire [7:0] Data_out,
output wire clk_out_DA,
output wire [11:0]data_out2,
output wire [11:0]demolate_final,
output wire wr_en_data_i,
output wire wr_en_coeffi_i,
output wire [15:0] din_coeffi_i,
output wire input_rdy,
output reg [4:0]cnt,
output wire done,
output wire [53:0]demolate_final_out
);
wire clk_200M;
assign Data_out[7:0] = Data_in_carry[11:4];
assign clk_out_DA = clk_200M ;
reg [11:0] rom_addr_carry;
reg [11:0] rom_addr_modulate;
//倍频
Gowin_rPLL your_instance_name_1(
.clkout(clk_200M), //output clkout
.clkin(clk) //input clkin
);
//分频5M
reg [3:0] counter;
reg clk_5M;
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
counter <= 4'd0;
clk_5M <= 0;
end
else
if(counter== 4'd4)
begin
clk_5M <=~clk_5M;
counter <= 4'd0;
end
else
begin
counter <= counter + 1;
end
//频率控制
always@(posedge clk_5M or negedge rst_n)
if(!rst_n)
begin
rom_addr_modulate <= 10'd0;
end
else
begin
rom_addr_modulate <= rom_addr_modulate+1; //1.25KHZ //4096
end
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
rom_addr_carry <= 10'd0;
end
else
begin
rom_addr_carry <= rom_addr_carry +10+Data_in_modulate[11:8]-7; //500KHZ , 12.5K/1 , +-75K
// rom_addr_carry <= rom_addr_carry +5; //1MHZ +-0.4M
end
//正弦//调制
Gowin_pROM Gowin_pROM_modulate(
.dout(Data_in_modulate), //output [11:0] dout
.clk(clk_5M), //input clk
.ce(1'b1), //input ce
.reset(~rst_n), //input reset
.ad(rom_addr_modulate) //input [9:0] ad
);
//正弦//载波
Gowin_pROM Gowin_pROM_carry(
.dout(Data_in_carry), //output [11:0] dout
.clk(clk_200M), //input clk
.ce(1'b1), //input ce
.reset(~rst_n), //input reset
.ad(rom_addr_carry) //input [9:0] ad
);
//对调制信号 FM_Mod 进行微分,对微分后的信号取绝对值
wire [11:0]FM_Mod;
assign FM_Mod[11:0] = Data_in_carry[11:0];
Weifen_FM_Mod Weifen_FM_Mod(
.clk(clk),
.rst_n(rst_n), //rst_n,
.FM_Mod(FM_Mod),// [11:0] FM_Mod, //输入已调信号
.buffer(buffer), //reg [11:0] buffer,
.data_out1(data_out1), //[11:0] data_out1, //微分后的信号data_out1
.data_out2(data_out2) //reg [11:0] data_out2 //取绝对值后的数据data_out2
);
wire [11:0]AM_abs;
assign AM_abs = data_out2;
wire [53:0] demolate_signal;
//------------调用FIR滤波器--------------//
Basic_FIR_Filter_Top Basic_FIR_Filter_Top_1(
.clk(clk), //input clk
.rst_n(rst_n), //input rst_n
.ini(ini_i), //input ini
.wr_en_data(wr_en_data_i), //input wr_en_data
.wr_en_coeffi(wr_en_coeffi_i), //input wr_en_coeffi
.din_coeffi(din_coeffi_i), //input [11:0] din_coeffi
.din_data(AM_abs), //input [11:0] din_data
.dout(demolate_signal), //output [53:0] dout
.done(done), //output done
.input_rdy(input_rdy) //output input_rdy
);
reg [5:0]flag_sery;
reg wr_en_coeffi;
reg signed [15:0]din_coeffi;
reg ini;
//系数写入时序//
always @(posedge clk or negedge rst_n)
begin
if(!rst_n) begin
wr_en_coeffi <= 1;
flag_sery <= 0;
ini <= 1;
end
else begin
if(flag_sery == 0) begin
wr_en_coeffi <= 1;
din_coeffi <= 28;
flag_sery <= 1;
end
else if(flag_sery == 1) begin
din_coeffi <= 29;
flag_sery <= 2;
end
else if(flag_sery == 2) begin
din_coeffi <= 31;
flag_sery <= 3;
end
else if(flag_sery == 3) begin
din_coeffi <= 32;
flag_sery <= 4;
end
else if(flag_sery == 4) begin
din_coeffi <= 33;
flag_sery <= 5;
end
else if(flag_sery == 5) begin
din_coeffi <= 34;
flag_sery <= 6;
end
else if(flag_sery == 6) begin
din_coeffi <= 34;
flag_sery <= 7;
end
else if(flag_sery == 7) begin
din_coeffi <= 35;
flag_sery <= 8;
end
else if(flag_sery == 8) begin
din_coeffi <= 35;
flag_sery <= 9;
end
else if(flag_sery == 9) begin
din_coeffi <= 34;
flag_sery <= 10;
end
else if(flag_sery == 10) begin
din_coeffi <= 34;
flag_sery <= 11;
end
else if(flag_sery == 11) begin
din_coeffi <= 33;
flag_sery <= 12;
end
else if(flag_sery == 12) begin
din_coeffi <= 32;
flag_sery <= 13;
end
else if(flag_sery == 13) begin
din_coeffi <= 31;
flag_sery <= 14;
end
else if(flag_sery == 14) begin
din_coeffi <= 29;
flag_sery <= 15;
end
else if(flag_sery == 15) begin
din_coeffi <= 28;
flag_sery <= 16;
end
else begin
wr_en_coeffi <= 0;
ini <= 0;
end
end
end
//数据输入时序//
reg wr_en_data;
reg [4:0]cnt_1;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
wr_en_data <= 0;
cnt <= 0;
cnt_1 <= 0;
end
else if(input_rdy == 1&cnt[4] == 1)
begin
wr_en_data <= 1;
cnt <= cnt+1;
end
else if(cnt_1[4] == 0)
begin
wr_en_data <= 0;
cnt_1 <= cnt_1+1;
end
else
begin
wr_en_data <= 0;
cnt <= cnt+1;
end
end
assign wr_en_data_i = wr_en_data;
assign wr_en_coeffi_i = wr_en_coeffi;
assign din_coeffi_i = din_coeffi;
assign ini_i = ini;
reg [53:0]demolate_final_1;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
demolate_final_1 <= 0;
end
else if(demolate_signal !== 0)
begin
demolate_final_1 <= demolate_signal;
end
else
begin
demolate_final_1 <= demolate_final_1;
end
end
assign demolate_final[11:0] = demolate_final_1[16:5]; //截位
//平滑
reg [53:0]Smooth_1;
reg [53:0]Smooth_2;
reg [53:0]Smooth_3;
reg [53:0]Smooth_4;
reg [53:0]Smooth_5;
reg [53:0]Smooth_6;
reg [53:0]Smooth_7;
reg [53:0]Smooth_8;
always @(posedge clk_5M or negedge rst_n)
begin
if(!rst_n)
begin
Smooth_1 <= 0;
Smooth_2 <= 0;
Smooth_3 <= 0;
Smooth_4 <= 0;
Smooth_5 <= 0;
Smooth_6 <= 0;
Smooth_7 <= 0;
Smooth_8 <= 0;
end
else if(demolate_final_1)
begin
Smooth_1 <= demolate_final_1;
Smooth_2 <= Smooth_1;
Smooth_3 <= Smooth_2;
Smooth_4 <= Smooth_3;
Smooth_5 <= Smooth_4;
Smooth_6 <= Smooth_5;
Smooth_7 <= Smooth_6;
Smooth_8 <= Smooth_7;
end
end
assign demolate_final_out = Smooth_1 + Smooth_2 + Smooth_3 + Smooth_4 + Smooth_5 + Smooth_6 + Smooth_7 + Smooth_8;
endmodule
(3)微分
//对调制信号FM_Mod进行微分,对微分后的信号取绝对值
module Weifen_FM_Mod(
input clk,
input rst_n,
input [11:0] FM_Mod, //输入已调信号
output reg [11:0] buffer,
output [11:0] data_out1, //微分后的信号data_out1
output reg [11:0] data_out2 //取绝对值后的数据data_out2
);
assign data_out1=FM_Mod-buffer;
//--------------------------微分(累减)--------------------------------------------//
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
buffer<=0;
end
else begin
buffer<=FM_Mod; //一个时钟的延迟
end
end
//--------------------------取绝对值-----------------------------------------//
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
data_out2 <= 0;
end
else if(data_out1[11] == 1) begin
data_out2 <= -{data_out1}; //如果符号位是1,对数据取反
end
else if(data_out1[11] == 0) begin
data_out2 <= data_out1; //如果符号位是0,数据不变
end
else begin
data_out2 <= data_out2;
end
end
endmodule
六、注意事项
FIR滤波器这块的系数时序写入,写的有点拉胯。仅限参考
最后
以上就是体贴玫瑰为你收集整理的基于高云FPGA的FM调制与解调一、概述二、平台三、要求四、原理五、调试过程六、程序六、注意事项的全部内容,希望文章能够帮你解决基于高云FPGA的FM调制与解调一、概述二、平台三、要求四、原理五、调试过程六、程序六、注意事项所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复