FPGA学习积累之AM调制解调(解调部分没搞太明白)

基于FPGA的AM调制与解调以及从中学到的东西(注:解调没搞太清楚,没写)


随便说说

(这次小杨第一次写博客,有问题,见谅!)
前几天没事做就想学习学习信号类的知识,然后就想到了去年电赛折磨我的AM与FM的调制与解调。AM的调制与解调用FPGA来实现还是不是很难,但是我还是看了两位博主的博客(链接在这一段结尾)。本次AM调制与解调的信号都是有DDS输出的,载波为1MHz,调制波为10KHz。
AM调制与解调:(博主:子木呀)
AM调制与解调:(博主:黄子炫)


一、平台

1.软件:Vivado 2018.3(就只会这个。。。。)
2.硬件:EGO1


二、DDS IP的使用

(这里加个链接(DDS的配置))

注意左边的RTL

个人能力有限,就只能看懂这四个地方(/手动糊脸/)。

1.Configuration Options(配置选项):配置DDS的类型,下来有三个选项,这里选着“Phase Generator and SIN Cos LUT”。

2.System Clock(配置DDS的输入系统时钟):对应的引脚(看左边的那个RTL,由aclk输入),这里配置10.24MHz(这个值后面会解释)。

3.Number of Channels(通道数):因为只用到一个输出,所以通道数为1。(其实是自己不会多通道的使用。。。。)

4.Parameter Selection(参数选择):这里我把选项下拉出来了。为了后面使用的方便,这里我选择“Hardware Parameters”

在这里插入图片描述
在这里插入图片描述
这里我把Hardware Parameters和System Parameters的两个界面给截图出来对比了一下。System Parameters需要去计算频率分辨率,这个计算起来就。。。。(我算不明白)。Hardware Parameters就很方便,它的Phase Width默认值为16,Output Width默认值为8。Phase Width的值会影响到我们的输入频率,我使用的时候配的是10位宽。下面展示Phase Width与输入频率的关系。(为了省事,我把一位博主写的部分搬过来,感谢子木呀!)

DDS核频率分辨率计算公式如下:

其中Δθ就是我们的频率控制字取值后文用fre_word表示,Bθ(n)是相位累加器位宽后文用B表示,fclk为DDS工作时钟。频率分辨率就是当Δθ=1时的fout。先假如我们需要的信号频率范围:1M-10MHz,分辨率0.01MHz。
取Δθ=1,fout=0.01MHz,B=10bit,我们可得DDS工作时fclk=10.24MHz。

这里展示一下Configuration配置完的界面(注意左边的RTL):

在这里插入图片描述
在这里插入图片描述
Implementation界面需要配置这四点。

1.选择可编程阶段增量(我觉得就是频率控制字的可编程。。)

2.相位偏移就不需要

3.输出波形为余弦波 [ AM调制公式:P(t)=(A+M_a cos⁡(t_1 ) )×cos⁡(t_2 ) ]

4.不需要相位输出

在这里插入图片描述
这个地方一开始是报错的,需要我们手动修改。前面我们的Phase Width为10,所以这个地方我们要配置为十位的二进制,这里为0000_0001。当这个界面没有报错的时候,左边的RTL的S_AXIS_CONFIG才会高亮。
在这里插入图片描述
在这里插入图片描述这是两个Summary的结果。

到此,DDS IP的用户界面就配置完了。


三、FIR滤波(用于后面AM解调)

FIR滤波器参数的生成

FIR滤波器的参数我用的MATLAB生成的,这里也顺带学习了一下MATLAB的Filter Designer工具。

在这里插入图片描述
1.采样频率我用的100MHz(后面发现100MHz程序运行太慢了。。。。),采样定律规定的是大于采样信号的2倍

2.这里截止频率我用的20KHz。调制信号为10KHz,所以大于10KHz就行,
但是如果有杂波,杂波频率在10KHz~1MHz之间的话,截止频率的要求就有点多了

3.滤波阶数为127

最后记得点击Design Filter,要不然那个波形不是这个滤波器的波形。
接下来生成.COE文件

在这里插入图片描述
FIR IP
在这里插入图片描述1.选择COE File

2.加载刚刚生成的.COE文件

在这里插入图片描述
1.输入的采样频率为10MHz(上面的100MHz改了,嘿嘿,10MHz我都嫌慢)

2.系统输入时钟

到此FIR的配置就完了。


四、AM调制

AM调制公式:P(t)=(A + M_a × cos⁡(t_1) ) × cos⁡(t_2)
其中当A=1时,调制深度就是M_a;cos⁡(t_1 )为调制信号,cos⁡(t_2)为载波。
为了好解释代码,上式变为(D + depth_con × cos⁡(t_1) ) × cos⁡(t_2)

代码如下(还是嫖别人代码香,嘿嘿,虽然不道德,但是掌握了知识就是自己的了,再次感谢开篇链接的两位博主):

reg  [7:0] D = 127;          //AM波直流分量 
    reg signed[7:0] depth_con;  
 
    always@(posedge clk_out)         //设置调制深度
    begin
       case (depth)
            0:  depth_con <= 0 ;       //调制深度为0,直流分量对应值
            1:  depth_con <= 13;       //调制深度为0.1,直流分量对应值
            2:  depth_con <= 28;       //调制深度为0.2,直流分量对应值
            3:  depth_con <= 45;       //调制深度为0.3,直流分量对应值
            4:  depth_con <= 64;       //调制深度为0.4,直流分量对应值
            5:  depth_con <= 85;       //调制深度为0.5,直流分量对应值
            6:  depth_con <= 110;       //调制深度为0.6,直流分量对应值
            7:  depth_con <= 138;       //调制深度为0.7,直流分量对应值  
            8:  depth_con <= 171;       //调制深度为0.8,直流分量对应值               
            9:  depth_con <= 209;       //调制深度为0.9.,直流分量对应值
            10:  depth_con <= 255;       //调制深度为1,直流分量对应值       
       endcase
    end
    
    wire signed[15:0] modulate_mul8ma;
    wire signed[7:0] modulate_mulma;
    mult_genx mult_floating_point (        
      .CLK(clk_out),  // input wire CLK
      .A(modulate_out),      // input wire [7 : 0] A
      .B(depth_con),      // input wire [7 : 0] B
      .P(modulate_mul8ma)      // output wire [15 : 0] P
    );
    assign modulate_mulma = modulate_mul8ma>>8;
    reg [7:0] modulate_withdc;
    always@(posedge clk_out)
    begin
        modulate_withdc <=  modulate_mulma + D;
    end
    

    mult_gen_0 mult_modulation (        
      .CLK(clk_out),  // input wire CLK
      .A(modulate_withdc),      // input wire [7 : 0] A
      .B(carrier_out),      // input wire [7 : 0] B
      .P(AM_out)      // output wire [15 : 0] P
    );

因为Verilog语言不能像C语言、python等等其他编程语言一样可以直接用浮点数,我们要做一些必要的处理完成浮点数计算的问题,所以depth = depth_con>>8(depth_con / 256)。但是代码并没有直接做这一步就计算,而是先将载波与depth_con相乘之后在>>8送给modulate_mulma。为了保证输出的数据都是16位宽的,这里modulate_mulma还加了一个127。这里的两个乘法器要注意一下他们输入参数是否有无符号位,否则计算出的已调制波有问题。

到此,调制就完了。

五、整体代码

代码如下(还是嫖别人代码香,嘿嘿,虽然不道德,但是掌握了知识就是自己的了,再次感谢开篇链接的两位博主):

`timescale 1ns / 1ps

module AM_generate_top(
    input sysclk
    );
    wire [15:0] AM_out;
    wire [3:0] depth= 9;
    wire [15:0] carrier_con = 10;
    wire [15:0] modulate_con = 1;
    wire [7:0] carrier_out;
    wire [7:0] modulate_out;
    wire clk_out;
    
    clk_div clk_1024k
       (
        // Clock out ports
        .clk_out1(clk_out),     // output clk_out1
       // Clock in ports
        .clk_in1(sysclk));      // input clk_in1
    
    carrier_dds DDS_carrier(
      .aclk(clk_out),                                  // input wire aclk
      .s_axis_config_tvalid(1),  // input wire s_axis_config_tvalid
      .s_axis_config_tdata(carrier_con),    // input wire [15 : 0] s_axis_config_tdata
      .m_axis_data_tvalid(),      // output wire m_axis_data_tvalid
      .m_axis_data_tdata(carrier_out)        // output wire [7 : 0] m_axis_data_tdata
    );
    
    modulation_dds DDS_modulation(
      .aclk(clk_out),                                  // input wire aclk
      .s_axis_config_tvalid(1),  // input wire s_axis_config_tvalid
      .s_axis_config_tdata(modulate_con),    // input wire [15 : 0] s_axis_config_tdata
      .m_axis_data_tvalid(),      // output wire m_axis_data_tvalid
      .m_axis_data_tdata(modulate_out)        // output wire [7 : 0] m_axis_data_tdata
    );
    
    
    reg  [7:0] A= 127;          //AM波直流分量 
    reg signed[7:0] depth_con;  
 
    always@(posedge clk_out)         //设置调制深度
    begin
       case (depth)
            0:  depth_con <= 0 ;       //调制深度为0,直流分量对应值
            1:  depth_con <= 13;       //调制深度为0.1,直流分量对应值
            2:  depth_con <= 28;       //调制深度为0.2,直流分量对应值
            3:  depth_con <= 45;       //调制深度为0.3,直流分量对应值
            4:  depth_con <= 64;       //调制深度为0.4,直流分量对应值
            5:  depth_con <= 85;       //调制深度为0.5,直流分量对应值
            6:  depth_con <= 110;       //调制深度为0.6,直流分量对应值
            7:  depth_con <= 138;       //调制深度为0.7,直流分量对应值  
            8:  depth_con <= 171;       //调制深度为0.8,直流分量对应值               
            9:  depth_con <= 209;       //调制深度为0.9.,直流分量对应值
            10:  depth_con <= 255;       //调制深度为1,直流分量对应值       
       endcase
    end
    (* use_dsp48 = "yes" *)    //这个没太大关系
    
    wire signed[15:0] modulate_mul8ma;
    wire signed[7:0] modulate_mulma;
    mult_genx mult_floating_point (        //调用乘法IP核,就是进行前面说的得到小数的那一部分操作
      .CLK(clk_out),  // input wire CLK
      .A(modulate_out),      // input wire [7 : 0] A
      .B(depth_con),      // input wire [8 : 0] B
      .P(modulate_mul8ma)      // output wire [15 : 0] P
    );
    assign modulate_mulma = modulate_mul8ma>>8;
    reg [7:0] modulate_withdc;
    always@(posedge clk_out)
    begin
        modulate_withdc <=  modulate_mulma + A;
    end
    

    mult_gen_0 mult_modulation (         //与载波相乘
      .CLK(clk_out),  // input wire CLK
      .A(modulate_withdc),      // input wire [7 : 0] A
      .B(carrier_out),      // input wire [7 : 0] B
      .P(AM_out)      // output wire [15 : 0] P
    );

reg [15:0] AM_abs;
always @(posedge clk_out) begin

    if(AM_out[15] == 1)    begin        //全波整流
        AM_abs <= -{AM_out};        //如果符号位是1,对数据取反
    end
    else if(AM_out[15] == 0)    begin
        AM_abs <= AM_out;           //如果符号位是0,数据不变
    end
end

    wire [39:0] demolate_signal;
    fir_my fir (             //低通滤波
      .aclk(sysclk),                              // input wire aclk
      .s_axis_data_tvalid(1),  // input wire s_axis_data_tvalid
      .s_axis_data_tready(),  // output wire s_axis_data_tready
      .s_axis_data_tdata(AM_abs),    // input wire [15 : 0] s_axis_data_tdata
      .m_axis_data_tvalid(),  // output wire m_axis_data_tvalid
      .m_axis_data_tdata(demolate_signal)    // output wire [39 : 0] m_axis_data_tdata
    );

    wire [7:0] demolate_final; 
    assign demolate_final[7:0] = demolate_signal[35:28];  //截位
    
endmodule

六、仿真波形

在这里插入图片描述
这里有两张有意思的:

在这里插入图片描述
在这里插入图片描述其实都是没问题的,就是有个小地方没搞对。

总结

这次AM调制与解调的学习是建立在我没有相关专业基础知识上,所以我最多只能理解到AM调制和解调的算法,但是并没有深入理解到它的内涵。不过在这个过程中学到了如何配置DDS和FIR的IP我觉得也挺值得的,万一明年电赛用到了呢?对吧。看着仿真的波形就得很美妙(特别是那个AM_out),我体会到了FPGA功能的另一种美以及信号处理的乐趣。(个人能力有限,有问题的话请大佬指明出来,我去学习)
(我还是想说一下那个解调:我的理解就是全波整流之后,做低通把那个10KHz的信号给留下来了,不知道是不是这个意思。)

  • 6
    点赞
  • 69
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
AM调制是一种广泛应用于无线通信和广播系统的调制技术。在AM调制技术中,载波频率被调制成与基带信号频率相关的波形,以便在接收端重新还原原始信息信号。 在Verilog语言中,实现AM调制的方法有多种。其中一种方法是使用 Direct Digital Synthesizer (DDS), DDS 是一种可编程数字信号产生器,它能够按照预定的频率、相位和振幅生成连续的数字信号。 DDS的原理是通过对一个基础的参考频率信号进行数字数值控制,以产生高精度的输出信号,从而实现频率的可编程输出。在实现DDS的Verilog代码中,它需要实现基本的计数器、相位累加器、幅度控制和输出寄存器等模块。 接着,可以通过 Verilog 模块来将原始的基带信号进行解析,并将其映射到 DDS 模块的幅度控制输入端。这样,DDS 模块将在其输出端产生一个与基带信号相对应的调制信号,它能够调制载波信号。此时,我们可以使用 Verilog 中的相加和电平转换模块来将调制信号和载波信号进行相加及电平转换。 这样,就可以实现AM调制的过程。但是需要注意的是,为了保证同步性,需要确保载波信号和基带信号采样的时间是相同的,以便在接收端进行解调。 总之,使用Verilog编程实现AM调制需要遵循Verilog语言的特点与DDS的基本原理。仔细考虑设计和调试过程,才能实现一个高效、稳定且可靠的AM调制系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值