FM调制的FPGA实现(三)

2023-11-10

				版权声明:本文为博主原创文章,如果觉得不错欢迎转载,记得标明出处就行。					https://blog.csdn.net/HOOKNET/article/details/81278232				</div>
							<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-f57960eb32.css">
							            <div id="content_views" class="markdown_views prism-atom-one-dark">
						<!-- flowchart 箭头图标 勿删 -->
						<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"><path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path></svg>
						<h1><a name="t0"></a><a id="_1" target="_blank"></a>一、前言</h1>

1.1. 平台

  • Vivado 2017.4;
  • Matlab 2016b;

1.2. 参数

  • 载波频率:5M;
  • 频率偏移:-75KHz ~ 75KHz;
  • 系统时钟:100M;
  • AD位宽:12位;

二、关于FM调制

图解AM、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文件。

  1. 生成.coe配置文件

Matlab代码部分参考本人之前的博客:
AM调制的FPGA实现

其中只需要把位宽(改成12)和文件生成路径作相应修改即可。

  1. 调用一个单口ROM IP核

关于IP核的配置如下


  1. 产生调制信号

整个工程的系统时钟为100MHz,这里假设调制信号是一个频率为500KHz的单频信号,换算成频率控制字的话为:

500K * 2^32 / 100M = 21474836

DDS代码如下:

module 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

  • 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

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的值即可。

  1. 乘法操作

乘法器IP核配置如下:

  1. 除法操作

除法操作可以调用除法器IP核来实现,也可以通过移位相加的方法实现。考虑到除数是2的整数次幂,因此只需要把乘法器输出的结果右移11位即可。

  1. DDS输出已调信号

这部分其实和上面的DDS实现方法类似,只是在频率控制字后面再加上频偏控制字即可。代码如下:

module 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

  • 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

3.3. 顶层模块设计

顶层文件不过多介绍,代码如下:

module 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

  • 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

3.4. TestBench

`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

  • 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

3.5. 仿真结果

运行仿真结果如下:

其实从仿真来看看不出什么明显的效果,是因为频偏相对于载频来说太小了,变化不明显。如果将频偏改成2MHz再仿真,结果如下:
在这里插入图片描述

从仿真结果来看输出波形存在着明显的频率变化,表明输出的是FM已调信号了。但这些都只是基于仿真来看的,至于输出信号是否正真满足指标要求,还得将已调信号通过DA输出到频谱仪上进行验证。

四、总结

FM调制在实现方式上还是比较简单的,但是可能需要一定的理解。以上的实现过程比较粗糙,只是从仿真层面去大致实现一个FM调制的效果,在实际应用中会有更加复杂的情况。另外,本人建议有条件的尽量在频谱仪上验证调制效果,对于指标严格的调制,普通示波器的频谱分析功能不一定能胜任。

另外,如有需要改进的地方,欢迎各位指出。

        </div>
					<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-258a4616f7.css" rel="stylesheet">
                  </div>
</article>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FM调制的FPGA实现(三) 的相关文章

  • 将输出从符号数学 (sym) 转换为浮点型

    我的问题类似于这个问题 https stackoverflow com questions 11114101 how to convert mupad symbol i sqrt 1 to i in matlab 11114959 1111
  • 如何建立数据流挖掘的滑动窗口模型?

    我们遇到的情况是 流 来自传感器的数据或服务器上的点击流数据 采用滑动窗口算法 我们必须将最后 例如 500 个数据样本存储在内存中 然后 这些样本用于创建直方图 聚合并捕获有关输入数据流中异常的信息 请告诉我如何制作这样的滑动窗 如果您询
  • 将单元格转换为双精度

    gt gt C 1 2 CF 2 C 1 2 CF 2 gt gt whos C Name Size Bytes Class Attributes C 2x2 478 cell 我怎样才能转换C into double以便 gt gt C
  • 将 3d 矩阵重塑为 2d 矩阵

    我有一个 3d 矩阵 n by m by t 在 MATLAB 中表示n by m一段时间内网格中的测量值 我想要一个二维矩阵 其中空间信息消失了 只有n m随着时间的推移测量t剩下 即 n m by t 我怎样才能做到这一点 你需要命令r
  • Matlab 编辑器不使用 emacs 快捷方式

    Is there some way I can make the matlab integrated editor not use emacs shortcut but use more normal shortcuts such that
  • 白色像素簇提取

    我正在研究指纹毛孔提取项目 并陷入毛孔 白色像素簇 提取的最后阶段 我有两个输出图像 我们可以从中获取毛孔 但不知道该怎么做 这两个图像的尺寸不同 image1 的尺寸为 240 320 image2 的尺寸为 230 310 这是我的图像
  • 将组合字符串和数字输入的元胞数组写入文本文件

    考虑以下 DateTime 2007 01 01 00 00 2007 02 01 00 00 2007 03 01 00 00 Headers Datetime Data Dat 100 200 300 Data DateTime num
  • 如何以编程方式指定 MATLAB 编辑器键绑定

    我想将键盘键绑定设置为Windows 默认设置我想在启动时使用startup m因为我希望在大量系统上设置此设置 首选项对话框中的等效设置是 MATLAB gt Keyboard gt Shortcuts gt Active Setting
  • 同时重新排序和旋转图像的高效方法

    为了快速加载 jpeg 我为turbojpeg 实现了一个 mex wrapper 以有效地将 大 jpeg 读入 MATLAB 对于 4000x3000px 的图像 实际解码只需要大约 120 毫秒 而不是 5 毫秒 然而 像素顺序是 R
  • 在 MATLAB 中创建共享库

    一位研究人员在 MATLAB 中创建了一个小型仿真 我们希望其他人也能使用它 我的计划是进行模拟 清理一些东西并将其变成一组函数 然后我打算将其编译成C库并使用SWIG https en wikipedia org wiki SWIG创建一
  • 如何加载具有可变文件名的 .mat 文件?

    select all mat files oar dir oar mat n oar name loop through files for l 1 length oar load pat oar l lt this is the mat
  • 如何每次使用按钮将数据添加到 MATLAB 中的现有 XLSX 文件?

    我有一个函数可以生成一些变量 例如分数 对 错 未回答 使用按钮调用此功能 问题是如何每次将函数生成的这些值添加 附加到 XLSX 文件中 或者 如何创建 MAT 文件以便可以添加它 可能的解决方案是什么 附加到 xls 文件所涉及的挑战是
  • MATLAB - GUI 和 OPC 服务器

    我想在 MATLAB 中设计一个图形用户界面 可以使用 MATLAB 的过程控制对象链接和嵌入 OPC 工具箱连续读取数据 我怎样才能实现这个 我已经设计了图形用户界面 但我无法将数据读入图形用户界面 就这样做 type opctoolMA
  • 将数据提示堆栈放在轴标签顶部,并在轴位置发生更改后更新轴标签

    此问题仅适用于 unix matlab Windows 用户将无法重现该问题 我在尝试创建位于 y 轴标签顶部的数据提示时遇到问题 下图很能说明问题 正如您所看到的 在 ylabel 附近创建的数据提示将到达 ylabel 文本的底部 而期
  • 平衡两轮机器人而不使其向前/向后漂移

    我正在尝试设计一个控制器来平衡 2 轮机器人 约 13 公斤 并使其能够抵抗外力 例如 如果有人踢它 它不应该掉落 也不应该无限期地向前 向后漂移 我对大多数控制技术 LQR 滑模控制 PID 等 都很有经验 但我在网上看到大多数人使用 L
  • 如何在Matlab中绘制网络?

    我有一个矩阵AMatlab中的维数mx2每行包含两个节点的标签 显示网络中的直接链接 例如 如果网络有4矩阵的节点A可能A 1 2 1 3 2 1 2 4 3 2 4 1 4 2 其中第一行表示有一个链接来自1 to 2 第二行表示有一个链
  • 在 matlab 代码中使用 dll 文件

    我需要使用 Matlab 中由 dll 文件定义的函数 我有一个例子 那个家伙将 dll 转换为 mexw32 文件 但我知道我是如何做到这一点的 我尝试使用加载库但它没有创建任何文件 我怎样才能做到这一点 loadlibrary http
  • 如何使用Matlab将数据保存到Excel表格中?

    我想将数据以表格形式保存在 Excel 工作表中 它应该看起来像 Name Age R no Gpa Adnan 24 18 3 55 Ahmad 22 12 3 44 Usman 23 22 3 00 每次当我执行我的文件时类数据 m 下
  • 在 MATLAB 中模拟 C++ 模板

    我试图找出创建 C 模板或 Java 通用对象的替代方案的最佳方法 出于多种不同的原因 我过去曾多次想这样做 但现在我想做的是为几个相关的类创建 saveobj 和 loadobj 函数 我的想法是 我想要一组通用的例程来创建默认结构 然后
  • 如何正确从表中删除 NaN 值

    在 Matlab 中阅读 Excel 电子表格后 不幸的是 我的结果表中包含了 NaN 例如这个 Excel 表格 将产生此表 其中出现额外的 NaN 列 我尝试使用以下代码片段删除 NaN measurementCells readtab

随机推荐

  • parent.layer.closeAll();关闭弹出层

    可参考文档 https wenku baidu com view e05a3fe15bf5f61fb7360b4c2e3f5727a5e92486 html 关闭所有页面 parent layer closeAll 先得到当前ifame层的
  • 数据库的备份与恢复

    目录 1 数据库的备份与恢复是什么 2 数据库的备份与恢复的三种常见方法 2 1 使用第三方工具 我用的是navicat 导入 导出 2 2 使用mysqldump命令备份和恢复 导入 导出 2 3 LOAD DATA INFILE 导入
  • ArcGIS制作全球地图并生成纬度统计分布线

    转载 ArcGIS制作全球地图并生成纬度统计分布线https mp weixin qq com s LTA9I2lZ1nwA1xdHlD9vjg
  • MYSQL数据导入导出&视图&索引&执行计划

    目录 一 数据导入 导出 二 视图运用 三 索引 四 执行计划 一 数据导入 导出 创建log数据表 CREATE TABLE t log id varchar 32 NOT NULL COMMENT 唯一标识 ip varchar 15
  • C++STL库之sort函数

    sort函数 sort函数介绍 背景 功能 语法 便捷函数 sort函数应用 普通排序 结构体排序 sort函数介绍 背景 sort函数用于C 中 对给定区间所有元素进行排序 默认为升序 也可进行降序排序 sort函数进行排序的时间复杂度为
  • 【Meta-AI】Sam-分割一切 测试

    什么是 SAM 近日 Meta AI在官网发布了基础模型 Segment Anything Model SAM 并开源 其本质是用GPT的方式 基于Transform 模型架构 让计算机具备理解了图像里面的一个个 对象 的通用能力 SAM模
  • 【数学建模笔记 13】数学建模的差分方程模型

    13 差分方程模型 定义 设函数 x k x k x k x k
  • Go语言从入门到规范-6.9、Go处理yml和ini文件

    Go语言从入门到规范 6 9 Go处理yml和ini文件 文章目录 Go语言从入门到规范 6 9 Go处理yml和ini文件 1 前言 2 ini概念 2 1 概述 2 2 格式 2 3 示例 3 Go语言处理ini文件 1 前言 一般ym
  • 聊聊缓存相关知识

    文章目录 缓存原理 缓存穿透 缓存击穿 缓存雪崩 穿透 击穿 雪崩对比 缓存预热 缓存更新 缓存降级 缓存原理 缓存穿透 缓存穿透 指用户查询数据 在数据库没有 自然在缓存中也不会有 这样就导致用户查询的时候 在缓存中找不到 每次都要去数据
  • 用arcgis for javascript 开发一个简单的二维地图(入门案例)

    效果如图 一 引入arcgis 2 加载模块 使用第二个标记从 API 加载特定模块 使用以下代码片段中的语法加载以下模块 esri Map 加载特定于创建地图的代码 esri views MapView 加载允许以 2D 方式查看地图的代
  • 时间旅行java_[ 一起学React系列 -- 6 ] 秘术之时间旅行-1

    标题看起来挺新颖的 笔者都觉得很高大上是不是哈哈 抛转 时间旅行在生活中是一个非常吸引人的概念 虽然现在无法实现但说不定未来的某天就实现了 然后就穿梭会过去杀掉小时候的自己然后就开始懵逼自己是谁类似的狗血剧情 那么问题来了 我们能活到那个时
  • 一次后台管理时间筛选开发经历

    这个需求是一个独立出来的 没有他的接口 在页面的左上角 有一个antd的时间选择框 有起止时间 在页面的右边 是一个查询按钮 点击查询按钮之后 页面就筛选出符合标准的数据 其实除了日期筛选 之前还有一个类型筛选 再加上本页面的数据是通过接口
  • Linux C 线程编程

    Linux C 线程编程 一 线程创建 二 线程退出 三 线程等待 四 线程同步 4 1 匿名信号量 4 2 互斥锁 4 3 条件变量 五 线程池 六 进程与线程 一 线程创建 include
  • Cocos2d 中对图片的各种操作

    关于精灵的各种操作 总结一下以便以后复习查找 内容简要 1 初始化 2 创建无图的精灵 3 设置精灵贴图大小 4 添加入层中 5 对精灵进行缩放 6 对精灵款或高进行缩放 7 旋转精灵 8 设置精灵透明度 9 精灵的镜像反转 10 设置精灵
  • 【解决】python获取文件大小,下载进度条报错KeyError: ‘content-length‘

    python3使用request httpx下载文件 获取不到文件大小 response没有content length header 最简单的排查问题的办法就是用浏览器去下载 如果浏览器在下载时 也不显示总大小 那么说明服务器不支持 HT
  • nginx对于XXX.com和XXX.com/index给映射到www.xxx.com的方式

    location root C website index index html index htm index php include C website htaccess if host www xincanzs com rewrite
  • css加载会造成阻塞吗?

    https www cnblogs com chenjg p 7126822 html
  • ios私钥证书的创建方法

    ios私钥证书是苹果公司为ios开发者打包app 推出的一种数字证书 只有同一个苹果开发者账号生成的ios私钥证书打的包 才能上架同一个开发者账号的app store 因此不要指望别人给你共享私钥证书和描述文件 因为别人的证书和描述文件打的
  • seata server 1.6版本安装及配置

    文章目录 下载 安装并启动 数据库 导入数据库 修改配置文件 服务注册 配置中心 手动添加 脚本导入 前些天发现了一个巨牛的人工智能学习网站 通俗易懂 风趣幽默 忍不住分享一下给大家 点击跳转到网站 Seata 是一款开源的分布式事务解决方
  • FM调制的FPGA实现(三)

    版权声明 本文为博主原创文章 如果觉得不错欢迎转载 记得标明出处就行 https blog csdn net HOOKNET article details 81278232 div class markdown views prism a