草庐IT

基于 VIVADO 的 FM 调制解调(上)设计篇

chylinne 2023-04-21 原文

一、概述

        本文先简要介绍了频率调制(frequency modulation,FM,简称调频)的原理,然后对其进行方案设计,最后基于 VIVADO 2018.3 使用 Verilog 进行实现。

二、FM 原理

        角度调制(angle modulation)是已调波的总相角  随着基带信号  作某种变化的调制方式,它包括频率调制和相位调制。一般而言,角调信号的表达式为:

其中, 称为相角,它是随  变化的。

        频率调制(frequency modulation,FM,简称调频)的调制表达式可以下式表示:

其中, 称为频偏常数,单位 Hz/V。

三、参数设计

(1)调制参数

        时钟频率:1 MHz

        调制信号:4 kHz 正弦波

        载波频率:100 kHz

(2)解调参数

        解调参数主要是包络检波(低通滤波)器的参数配置,我们使用 Matlab 中的 filterDesigner 进行设计。滤波器可以选择等波纹的 FIR 低通滤波器,采样率设置为 1000 kHz,通带截止频率 10 kHz,阻带截止频率 30 kHz。

设计好后点击左侧边栏第三个框进行量化,选择定点模式,最后点击上方的 “ 目标 ” 生成 .coe 文件。

四、IP 核参数配置

(1)调制信号发生器

        调制信号使用 DDS IP 核产生正弦波数据,Configuration Option 选择 “ Phase Generator and SIN COS LUT ”,输入时钟设置 1 MHz,如下图所示。

由于调制信号的频率已固定为 4 kHz,因此这里的频率控制字(Phase Increment Programmabilit,相位增量可编程性)选择为 Fixed;相位控制字(Phase Offset Programmability,相位偏移可编程性)选择为 None;输出信号选择正弦波(Sine)。

点击 Next 进入下一页配置,选上 Output TREADY 和 ARESETn。

然后在输出频率处选择 4 kHz,即 0.004 MHz。

最后 Generate IP 即可。

(2)已调信号发生器

        已调 FM 信号也用 DDS IP 核产生,Configuration Option 选择 “ SIN COS LUT only ”,其余默认,如下图所示。

 进入下一页后选择输出模式为正弦波(Sine)。

 再进入下一页,将 Output TREADY 和 ARESETn 选上。

(3)低通滤波器

        采用包络检波法进行解调,我们通过 FIR IP 核(FIR Compiler)来实现低通滤波提取包络信息。在 IP 核中选择导入 COE File 的模式,并在下方的 “ Coefficient File ” 处导入 .coe 文件。

 下面三页的 Channel Specification、Implementation 和 Detail Implementation 均保存默认参数。

 在最后的 Inerface 页面上,勾选上 Output TREADY 和 ARESETn。

五、代码设计

(1)顶层代码

        顶层代码对调制和解调两大模块进行连线,这里是以直连线代替了无线信道。其中,调制模块中所需的相位控制字 POFF 也可由顶层进行参数配置,以满足用户的不同需求。

`timescale 1ns / 1ps
//
// Company: UESTC
// Engineer: chylinne
// 
// Create Date: 2022/08/15 14:17:30
// Design Name: fm
// Module Name: fm_top
// Project Name: fm
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module fm_top # (

    /* Phase Offset Programmability
    poff = 2^N * f_carrier
         = 2^16 * 100 kHz
         = 2^16 * 0.1 MHz
         = 6554
    */

    parameter   POFF = 'd6554  //100KHz
)(
    input           rst_n,
    input           clk
);
    
	wire signed	[15:0]  fm_mod_data;
	wire                fm_mod_valid;
	wire                fm_mod_ready;

	wire signed	[15:0]  fm_demod_data;
	wire                fm_demod_valid;
	wire                fm_demod_ready = 1'b1;

    fm_modulation # (
        .POFF(POFF)
    ) u_fm_modulation (
        .clk(clk),
        .rst_n(rst_n),
        
        .fm_mod_data(fm_mod_data),
        .fm_mod_valid(fm_mod_valid),
        .fm_mod_ready(fm_mod_ready)
    );

    fm_demodulation	u_fm_demodulation (
        .clk(clk),
        .rst_n(rst_n),

        .fm_mod_data(fm_mod_data),
        .fm_mod_valid(fm_mod_valid),
        .fm_mod_ready(fm_mod_ready),

        .fm_demod_data(fm_demod_data),
        .fm_demod_valid(fm_demod_valid),
        .fm_demod_ready(fm_demod_ready)
    );

endmodule

(2)调制模块

        调制模块较简单,通过调用 2 个 DDS IP 核实现调制信号(正弦波)和已调信号(FM 信号)的产生,重点在于需要通过相位计数器和相位控制字来计算出正弦波数据对应的相位信息。

`timescale 1ns / 1ps
//
// Company: UESTC
// Engineer: chylinne
// 
// Create Date: 2022/08/15 13:45:42
// Design Name: fm
// Module Name: fm_modulation
// Project Name: fm
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module fm_modulation # (

    /* Phase Offset Programmability
    poff = 2^N * f_carrier
         = 2^16 * 100 kHz
         = 2^16 * 0.1 MHz
         = 6554
    */

    parameter   POFF = 'd6554  //100KHz

)(
    input                 clk,
    input                 rst_n,

    input                 fm_mod_ready,
    output                fm_mod_valid,
    output signed [15:0]  fm_mod_data
);

    reg  signed [15:0]  phase_cnt;

    wire signed	[15:0]  phase_data;
    reg                 phase_valid;
    wire                phase_ready;
    wire signed	[15:0]  phase_add;

    wire signed	[7:0]   sine_signal; // sine modulate signal
    wire                sine_valid;
    wire                sine_ready;

    assign	phase_add   =  sine_signal << 4; // multiply 16
    assign	sine_ready  =  phase_ready;
    assign	phase_data  =  phase_cnt;
    
    always @(posedge clk)
    begin
        if(!rst_n)
            phase_valid <= 0;
        else if(phase_ready)
            phase_valid <= sine_valid;
    end

    always @(posedge clk)
    begin
        if(!rst_n)
        begin
            phase_cnt <= 0;
        end
        else if(phase_ready && sine_valid)
        begin
            phase_cnt <= phase_cnt + phase_add + POFF;
        end
    end

    dds_compiler_sine dds_1 (
        .aclk               (clk),              // input wire aclk
        .aresetn            (rst_n),            // input wire aresetn
        .m_axis_data_tvalid (sine_valid),       // output wire m_axis_data_tvalid
        .m_axis_data_tready (sine_ready),       // input wire m_axis_data_tready
        .m_axis_data_tdata  (sine_signal)       // output wire [7 : 0] m_axis_data_tdata
    );
	
	dds_compiler_fm dds_2 (
		.aclk               (clk),              // input wire aclk
		.aresetn            (rst_n),            // input wire aresetn
		.s_axis_phase_tvalid(phase_valid),      // input wire s_axis_phase_tvalid
		.s_axis_phase_tready(phase_ready),      // output wire s_axis_phase_tready
		.s_axis_phase_tdata (phase_data),       // input wire [15 : 0] s_axis_phase_tdata
		.m_axis_data_tvalid (fm_mod_valid),     // output wire m_axis_data_tvalid
		.m_axis_data_tready (fm_mod_ready),     // input wire m_axis_data_tready
		.m_axis_data_tdata  (fm_mod_data)       // output wire [15 : 0] m_axis_data_tdata
	);

endmodule

(3)解调模块

        解调模块则是通过 FIR 低通滤波器的 IP 核实现包络提取。

`timescale 1ns / 1ps
//
// Company: UESTC
// Engineer: chylinne
// 
// Create Date: 2022/08/15 13:47:23
// Design Name: fm
// Module Name: fm_demodulation
// Project Name: fm
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module fm_demodulation (
    input                   clk,
    input                   rst_n,

    output                  fm_mod_ready,
    input                   fm_mod_valid,
	input  signed [15:0]    fm_mod_data,

    input                   fm_demod_ready,
    output                  fm_demod_valid,
    output signed [15:0]    fm_demod_data
);

    reg  signed [15:0]  mem_data = 0;

    reg  signed [15:0]  dif_data = 0;
    reg                 dif_valid = 0;
    wire                dif_ready;

    reg         [15:0]  abs_data = 0;
    reg                 abs_valid = 0;
    wire                abs_ready;

    wire signed	[39:0]  fir_data;
    wire                fir_valid;
    wire                fir_ready;
	
    always @(posedge clk)		
    begin
        if(!rst_n)
            dif_valid <= 0;
        else
            dif_valid <= (dif_ready) ? fm_mod_valid : dif_valid;
    end

    /* Calculate difference data

    dx/dt
    dt is the clock period.
    dx is the difference of data in current clock and data in last clock.
    */
    always @(posedge clk)
    begin
        if(!rst_n)
        begin
            mem_data <= 0;
            dif_data <= 0;
        end
        else begin
            if(dif_ready && fm_mod_valid)
            begin
                mem_data <= fm_mod_data; // Save the data in current clock
                dif_data <= fm_mod_data - mem_data; // data in current clock minus data in last clock
            end
            else ;
        end
    end
    
    always @(posedge clk)
    begin
        if(!rst_n)
            abs_valid <= 0;
        else 
            abs_valid <= (abs_ready) ? dif_valid : abs_valid;
    end
    
    // Calculate absolute value
    always @(posedge clk)
    begin
        if(!rst_n)
            abs_data <= 0;
        else begin
            if(abs_ready && dif_valid)
            begin
                if(dif_data >= 0)
                    abs_data <= dif_data;
                else
                    abs_data <= -dif_data;
            end
            else ;
        end
    end

    assign	fir_ready       = fm_demod_ready;
    assign	fm_demod_data   = fir_data >>> 19;
    assign	fm_demod_valid  = fir_valid;
    assign	fm_mod_ready    = abs_ready;
    assign	dif_ready       = abs_ready;

    // FIR lowpass filter
    fir_compiler_lowpass fir_1 (
        .aresetn            (rst_n),        // input wire aresetn
        .aclk               (clk),          // input wire aclk
        .s_axis_data_tvalid (abs_valid),    // input wire s_axis_data_tvalid
        .s_axis_data_tready (abs_ready),    // output wire s_axis_data_tready
        .s_axis_data_tdata  (abs_data),     // input wire [15 : 0] s_axis_data_tdata
        .m_axis_data_tvalid (fir_valid),    // output wire m_axis_data_tvalid
        .m_axis_data_tready (fir_ready),    // input wire m_axis_data_tready
        .m_axis_data_tdata  (fir_data)      // output wire [31 : 0] m_axis_data_tdata
    );

endmodule

有关基于 VIVADO 的 FM 调制解调(上)设计篇的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  3. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

    导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

  4. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  5. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  6. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  7. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

  8. ruby-on-rails - 设计注册确认 - 2

    我在我的项目中有一个用户和一个管理员角色。我使用Devise创建了身份验证。在我的管理员角色中,我没有任何确认。在我的用户模型中,我有以下内容:devise:database_authenticatable,:confirmable,:recoverable,:rememberable,:trackable,:validatable,:timeoutable,:registerable#Setupaccessible(orprotected)attributesforyourmodelattr_accessible:email,:username,:prename,:surname,:

  9. ruby-on-rails - 设计通过 reset_password_token 获取用户 - 2

    我正在尝试创建密码规则来设计可恢复的密码更改。我通过passwords_controller.rb做了一个父类(superclass),但我需要在应用规则之前检查用户角色,但我所拥有的只是reset_password_token。 最佳答案 假设您的模型是用户:User.with_reset_password_token(your_token_here)Source 关于ruby-on-rails-设计通过reset_password_token获取用户,我们在StackOverflow

  10. ruby-on-rails - Rails 5,公寓和设计 : sign in with subdomains are not working - 2

    我已经使用Apartment设置了一个Rails5应用程序(1.2.0)和Devise(4.2.0)。由于某些DDNS问题,应用只能在app.myapp.com下访问(请注意子域app)。myapp.com重定向到app.myapp.com。我的用例是每个注册该应用的用户(租户)都应该通过他们的子域(例如tenant.myapp.com)访问他们的特定数据。用户不应限定在其子域内。基本上应该可以从任何子域登录。重定向到租户的正确子域由ApplicationController处理。根据Devise标准,登录页面位于app.myapp.com/users/sign_in。这就是问题开始的

随机推荐