一、前言
1.1. 平台
- Vivado 2017.4;
- Matlab 2016b;
1.2. 参数
- 载波频率:5M;
- 频率偏移:-75KHz ~ 75KHz;
- 系统时钟:100M;
- AD位宽:12位;
二、关于FM调制
简单来说就是:幅度改变频率。
与之相对应的是AM:幅度改变幅度。
怎么理解呢?对FM来说:调制信号的幅度大小决定了已调信号的频率。当调制信号的幅度改变时,已调信号的频率也会随之改变,而且只有频率会改变,幅度是保持不变的。
这里对FM调制的理论部分不过多讲述,有兴趣的可以自行上网查阅。
三、FM调制的FPGA实现
一个完整的FM调制系统主要分为以下几个部分:
- AD模块
- FM调制模块
- DA模块
其中,AD模块将模拟调制信号转换成数字调制信号。调制信号可以通过信号发生器产生。在本工程中,将在FPGA内部通过DDS产生一个正弦信号来模拟AD采样数据。FM调制模块的功能就是将调制信号变成FM已调信号。已调信号通过DA模块变成模拟信号后就可以通过天线发送出去。
3.1. 产生调制信号
调制信号是用DDS产生正弦信号来模拟实现的。DDS的实现需要用到ROM IP核,在配置ROM IP核之前需要用Matlab生成IP核所需要的.coe文件。
- 生成.coe配置文件
Matlab代码部分参考本人之前的博客:
AM调制的FPGA实现
其中只需要把位宽(改成12)和文件生成路径作相应修改即可。
- 调用一个单口ROM IP核
关于IP核的配置如下
- 产生调制信号
整个工程的系统时钟为100MHz,这里假设调制信号是一个频率为500KHz的单频信号,换算成频率控制字的话为:
500K * 2^32 / 100M = 21474836
DDS代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37module DDS_Mod( input clk, input rst_n, output signed [11:0] sin //调制信号 ); //--------------------------------------------------------// parameter Freq = 32'd21474836; //500kHz parameter cnt_width = 8'd32; //--------------------------------------------------------// //--------------------------------------------------------// reg [cnt_width-1:0]cnt_I = 0; wire [9:0] addr_I; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt_I <= 0; end else begin cnt_I <= cnt_I + Freq; end end assign addr_I = cnt_I[cnt_width-1:cnt_width-10]; //--------------------------------------------------------// //--------------------调用一个单口ROM核--------------------// Sin Sin_inst( .clka (clk), .addra (addr_I), .douta (sin) ); endmodule
3.2. FM调制模块
其实FM调制模块本质上也是一个DDS,区别就在于前者是一个频率可以按照一定规律变化的DDS。那么如何才能让频率不断变化呢?回到上面的代码中,有一个名为“Freq”的常量,这个常量的大小决定了DDS输出频率的大小。如果在这个常量后面再加上一个不断变化的值,那么这个DDS输出的就是一个频率不断变化的波形了。
在FM调制中,有两个比较重要的概念:中心频率和频偏。中心频率可以理解为只有“Freq”时的频率;频偏可以理解为当再加上一个变量后,输出频率大小与中心频率的差值。因此FM调制的关键就在于如何确定“Freq”这个常量后面再加上的这个变量的值。
本工程中中心频率为:5M,频偏为:-75KHz ~ 75KHz。当输入调制信号幅度为0时,输出的FM已调信号频率为5MHz,即载波频率;当输入调制信号幅度最大(即+211)时,输出的FM已调信号频率为5.075MHz(5M+75K);当输入调制信号幅度最小(即-211)时,输出的FM已调信号频率为4.925MHz(5M-75K)。根据以上情况,可以通过调制信号的大小计算出此时输出频率的大小。总结如下表:
输入调制信号大小 | 对应的频率偏移 | 对应频偏的频率控制字 |
---|---|---|
0 | 0 | 0 |
2^11 | 75k | 2^32 * 75k / 100M = 3221225 |
X | Y | N |
上表中,X是已知量,只需要通过等式 N = x * 3221225 / 2^11
求出N的值即可。
- 乘法操作
乘法器IP核配置如下:
- 除法操作
除法操作可以调用除法器IP核来实现,也可以通过移位相加的方法实现。考虑到除数是2的整数次幂,因此只需要把乘法器输出的结果右移11位即可。
- DDS输出已调信号
这部分其实和上面的DDS实现方法类似,只是在频率控制字后面再加上频偏控制字即可。代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48module FM_Mod( input clk, input rst_n, input [11:0] adc_data, output [11:0] FM_Mod ); parameter Freq_I = 32'd214_748_365; //载波信号的频率5M,时钟100M parameter Freq_Word = 32'd3_221_225; //频偏为75K parameter cnt_width = 8'd32; //-------------计算频偏控制字--------------// wire signed [43:0] mult_data; wire signed [31:0] Freq_Offset; MULT MULT_inst( .CLK (clk), .A (adc_data), .B (Freq_Word), .P (mult_data) ); assign Freq_Offset = mult_data[43:12]; //移位 //---------------------------------------// reg [cnt_width-1:0]cnt_I; wire [9:0] addr_I; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt_I <= 0; end else begin cnt_I <= cnt_I + Freq_I + Freq_Offset; end end assign addr_I = cnt_I[cnt_width-1:cnt_width-10]; //----------------ROM核-----------------// Sin Sin_inst( .clka (clk), .addra (addr_I), .douta (FM_Mod) ); endmodule
3.3. 顶层模块设计
顶层文件不过多介绍,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29module TOP( input clk, input rst_n, output [11:0] FM_Mod_data ); //----------------ADC-----------------// wire [11:0] adc_data; DDS_Mod DDS_Mod_inst( .clk (clk), .rst_n (rst_n), .sin (adc_data) ); //------------------------------------// //---------------FM调制----------------// FM_Mod FM_Mod_inst( .clk (clk), .rst_n (rst_n), .adc_data (adc_data), .FM_Mod (FM_Mod_data) ); endmodule
3.4. TestBench
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30`timescale 1ns/1ps module tb_TOP(); reg sclk; reg rst_n; wire [11:0] FM_Mod_data; //---------系统时钟----------// initial sclk = 1; always #5 sclk = !sclk; //---------复位---------// initial begin rst_n = 0; #100 rst_n = 1; end //-----------------------// TOP TOP_inst( .clk (sclk), .rst_n (rst_n), .FM_Mod_data (FM_Mod_data) ); //-----------------------// endmodule
3.5. 仿真结果
运行仿真结果如下:
其实从仿真来看看不出什么明显的效果,是因为频偏相对于载频来说太小了,变化不明显。如果将频偏改成2MHz再仿真,结果如下:
从仿真结果来看输出波形存在着明显的频率变化,表明输出的是FM已调信号了。但这些都只是基于仿真来看的,至于输出信号是否正真满足指标要求,还得将已调信号通过DA输出到频谱仪上进行验证。
四、总结
FM调制在实现方式上还是比较简单的,但是可能需要一定的理解。以上的实现过程比较粗糙,只是从仿真层面去大致实现一个FM调制的效果,在实际应用中会有更加复杂的情况。另外,本人建议有条件的尽量在频谱仪上验证调制效果,对于指标严格的调制,普通示波器的频谱分析功能不一定能胜任。
另外,如有需要改进的地方,欢迎各位指出。
最后
以上就是彪壮咖啡豆最近收集整理的关于FM调制的FPGA实现一、前言二、关于FM调制三、FM调制的FPGA实现四、总结的全部内容,更多相关FM调制内容请搜索靠谱客的其他文章。
发表评论 取消回复