基于FPGA的DDS信号发生器(图文并茂+深度原理解析)

news2024/11/28 8:34:47

        篇幅有限,本文详细源文件已打包 至个人主页资源,需要自取......


前言

        DDS(直接数字合成)技术是先进的频率合成手段,在数字信号处理与硬件实现领域作用关键。它因低成本、低功耗、高分辨率以及快速转换时间等优点备受认可。

        本文着重探究基于 FPGA 的简易 DDS 信号发生器设计原理与流程,同时给出 Verilog 代码实例。从原理剖析到具体实现步骤逐步深入,阐述如何利用 FPGA 技术与 DDS 技术相结合,实现信号发生器功能,为相关领域技术人员提供有价值的参考与借鉴。

一、DDS是什么?

        DDS(Direct Digital Synthesizer,直接数字合成器)是一种数字合成技术,它利用数字方式生成模拟信号。

二、物理层

1.结构示意图

        DDS基本结构主要由相位累加器、相位调制器、波形数据表ROM、D/A转换器等四大结构组成。DDS结构示意图,如下图所示

        1.相位累加器(Phase Accumulator)

        作为DDS的核心,相位累加器负责生成相位码,其输入为频率字输入,位宽通常用N表示。相位累加器的输出是连续累加的结果,用于控制信号的频率。

        2.相位调制器(Phase Modulator)

        接收相位累加器的输出,并加上相位偏移值P,用于实现信号的相位调制
        3.波形数据表ROM(Waveform ROM)

        存储一个或多个周期的波形数据,如正弦波。ROM的地址由相位调制器的输出决定,从而读取相应的波形数据。
        4.数模转换器(D/A Converter)

        将波形数据表ROM输出的数字信号转换为模拟信号,即最终的输出信号CLK_OUT。

2.DDS工作原理

        DDS信号发生器基本原理是通过查找表法读取ROM中存储的三角波,方波,锯齿波等数据,通过处理,能做到输出的波形频率和相位可调制,主要步骤如下:

        1.系统时钟 CLK 为整个系统的工作时钟,频率为 fclk;

        2.频率字输入 F_WORD(用 K 表示),数值大小控制输出信号的频率大小,数值越大输出信号频率越高,反之,输出信号频率越低;

        频率字输入K,表示相位增量,设其位宽为N,满足等式K = 2N * fOUT / fCLK。

        3.相位字输入P_WORD(用 P 表示),为整数,数值大小控制输出信号的相位偏移,主要用于相位的信号调制;

        4.输出信号为 CLK_OUT,频率为 fout;

        5.相位累加器根据频率字输入K和系统时钟频率Fclk累加相位值,生成相位码。相位累加器是DDS信号发生器的核心部分,它的作用是逐周期地累积相位。相位累加器通常是一个N位的寄存器,其值在每个系统时钟周期CLK下累加频率控制字Fword。相位累加的过程可以用以下公式表示:

        Phase_Acc = Phase_Acc + Fword

其中,Phase_Acc是当前时钟周期的相位累加值,Fword是频率控制字,它决定了累加的步长。

        相位累加寄存器通常用于存储相位累加器的当前值,以便进行相位调制或作为查找ROM的地址,记为Phase_Reg。考虑到ROM表地址深度M的影响,相位累加寄存器取Phase_Acc高M比特,也就是Phase_Reg = Phase_Acc >> (N - M)

        6.相位调制器根据相位字输入P调整相位累加器的输出,实现相位调制。相位调制器接收相位寄存器的输出,并可能加上一个相位控制字Pword,用于实现信号的相位偏移或调制。相位调制可以用以下公式表示:

        Phase_Mod = Phase_Reg + Pword

Phase_Reg是相位寄存器的输出,Pword是相位控制字。

        7.波形数据表ROM根据相位调制器的输出地址Phase_Mod,读取对应的波形数据,将ROM表的地址位宽记为M。

        假设波形数据ROM的地址位宽为12位,存储数据位宽为8位,即ROM有212 = 4096个存储空间,每个存储空间可存储1字节数据。

        8.D/A转换器将ROM输出的数字波形数据转换为模拟信号CLK_OUT,频率为fOUT,计算公式如下: fOUT = K * fCLK / 2N。当K = 1时,可得DDS最小分辨率为:fOUT = fCLK / 2N,此时输出信号频率最低。根据采样定理,K的最大值应小于2N / 2。

三、设计思路

1.模块图

        

        key_ctrl模块:生成选择信号,选择输出的波形是三角波,方波还是锯齿波,同时实例化按键消抖模块

        dds_ctrl模块:对输入的按键选择信号控制rom的读取操作,生成需要的波形,同时能实现频率相位可调

2.时序图

3.代码实现

module dds_ctrl(
	input	wire			sys_clk		,
	input	wire			sys_rst_n	,
	input	wire	[3:0]	wave_sel	,
	input	wire			touch_key1	,
	input	wire			touch_key2	,

	output	wire	[7:0]	dac_data	,
	output	wire	[7:0]	dac_data1	
);

reg 	[11:0]	F_WORD		;
reg 	[11:0]	P_WORD		;
reg 	[23:0]	fre_add		;
reg 	[11:0]	rom_add_reg	;
reg 	[11:0]	rom_add_reg1	;
reg		[13:0]	rom_addr	;
reg		[13:0]	rom_addr1	;

wire			key_flag	;
//wire			key_flag1	;

always @ (posedge sys_clk or negedge sys_rst_n)
	if(!sys_rst_n)	
		F_WORD <= 12'd500 ;
	else if(F_WORD == 12'd4096)
		F_WORD <= 12'd500 ;
	else if(touch_key1 == 1'b0)
		F_WORD <= F_WORD + 4'd10 ;
		
always @ (posedge sys_clk or negedge sys_rst_n)
	if(!sys_rst_n)	
		P_WORD <= 12'd0 ;
	else if(P_WORD == 12'd4095)
		P_WORD <= 12'd0 ;
	else if(key_flag == 1'b1)
		P_WORD <= P_WORD + 10'd512 ;
						
always @ (posedge sys_clk or negedge sys_rst_n)
	if(!sys_rst_n)
		fre_add <= 24'd0 ;
	else 
		fre_add <= fre_add + F_WORD ;
	
always @ (posedge sys_clk or negedge sys_rst_n)
	if(!sys_rst_n)
		rom_add_reg <= 12'd0 ;
	else 
		rom_add_reg <= fre_add[23:12] + P_WORD ;

always @ (posedge sys_clk or negedge sys_rst_n)
	if(!sys_rst_n)
		rom_add_reg1 <= 12'd0 ;
	else 
		rom_add_reg1 <= fre_add[23:12] ;
		
always @ (posedge sys_clk or negedge sys_rst_n)
	if(!sys_rst_n)
		rom_addr <= 14'd0 ;
	else 
		case(wave_sel)
			4'b0001:	
				rom_addr <= rom_add_reg ;
			4'b0010:
				rom_addr <= rom_add_reg + 14'd4096 ;
			4'b0100: 
				rom_addr <= rom_add_reg + 14'd8192 ;
			4'b1000:
				rom_addr <= rom_add_reg + 14'd12288 ;
			default: rom_addr <= rom_add_reg ;
		endcase		

always @ (posedge sys_clk or negedge sys_rst_n)
	if(!sys_rst_n)
		rom_addr1 <= 14'd0 ;
	else 
		case(wave_sel)
			4'b0001:	
				rom_addr1 <= rom_add_reg1 ;
			4'b0010:
				rom_addr1 <= rom_add_reg1 + 14'd4096 ;
			4'b0100: 
				rom_addr1 <= rom_add_reg1 + 14'd8192 ;
			4'b1000:
				rom_addr1 <= rom_add_reg1 + 14'd12288 ;
			default: rom_addr1 <= rom_add_reg1 ;
		endcase	

rom_wave	rom_wave_inst (
	.address ( rom_addr 	),
	.clock 	 ( sys_clk  	),
	.q 		 ( dac_data  	)
	);
	
rom_wave	rom_wave_inst1 (
	.address ( rom_addr1 	),
	.clock 	 ( sys_clk  	),
	.q 		 ( dac_data1  	)
);
	

touch_key touch_key_u1(
	.sys_clk	(sys_clk	),
	.sys_rst_n	(sys_rst_n	),
	.key_in		(touch_key2	),
                 
	.key_flag	(key_flag	)
);

/* touch_key touch_key_u2(
	.sys_clk	(sys_clk	),
	.sys_rst_n	(sys_rst_n	),
	.key_in		(touch_key1	),
                 
	.key_flag	(key_flag1	)
); */

endmodule  
module key_ctrl(
	input	wire			sys_clk		,
	input	wire			sys_rst_n	,
	input	wire	[3:0]	key			,
	
	output	reg 	[3:0]	wave_sel 	
);

wire	key3	;
wire	key2	;
wire	key1	;
wire	key0	;

always @ (posedge sys_clk or negedge sys_rst_n)
	if(!sys_rst_n)
		wave_sel <= 4'b0001 ;
	else if(key3 == 1'b1) 
		wave_sel <= 4'b1000 ;//锯齿波
	else if(key2 == 1'b1) 
		wave_sel <= 4'b0100 ;//
	else if(key1 == 1'b1) 
		wave_sel <= 4'b0010 ;//
	else if(key0 == 1'b1) 
		wave_sel <= 4'b0001 ;//正弦波
	
key_filter
#(
	.CNT_MAX (20'd9)//.CNT_MAX (20'd999_999)
)
key_filter_u1
(
	.clk	 (sys_clk	),
	.rst_n   (sys_rst_n ),
	.key_in  (key[3]  	),
	          
	.key_flag(key3	    )
);

key_filter
#(
	.CNT_MAX (20'd9)//.CNT_MAX (20'd999_999)
)
key_filter_u2
(
	.clk	 (sys_clk	),
	.rst_n   (sys_rst_n ),
	.key_in  (key[2]  	),
	          
	.key_flag(key2	    )
);

key_filter
#(
	.CNT_MAX (20'd9)//.CNT_MAX (20'd999_999)
)
key_filter_u3
(
	.clk	 (sys_clk	),
	.rst_n   (sys_rst_n ),
	.key_in  (key[1]  	),
	          
	.key_flag(key1	    )
);

key_filter
#(
	.CNT_MAX (20'd9)//.CNT_MAX (20'd999_999)
)
key_filter_u4
(
	.clk	 (sys_clk	),
	.rst_n   (sys_rst_n ),
	.key_in  (key[0]  	),
	          
	.key_flag(key0	    )
);

endmodule

总结

DDS并不复杂,只需要搞清楚原理,很容易快速掌握。

1、通过调节频率控制字K,可以控制相位累加器的累加速度,进而ROM读取地址的速度,这样就可以控制输出波形频率了;

2、通过调节相位控制字P,可以控制相位调制器,进而控制ROM读取地址的初值,这样就可以控制输出波形的初值;

3、通过调整ROM表中的数据,可以通过matlab、python等生成不同的波形数据,进而输出不同的波形。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2206615.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++ stack和queue的使用介绍和模拟实现

内容摘要&#xff1a; 本文介绍了stack和queue的构造函数和一些成员函数&#xff0c;并模拟实现了stack和queue&#xff0c;分析了为什么选择deque作为适配器默认封装的对象 stack的介绍 栈是只能够在一端进行插入和删除的&#xff0c;这就是我们一直常说的“后进先出”&#x…

未来10年,哪些行业将被AI彻底颠覆?

随着人工智能&#xff08;AI&#xff09;技术的快速发展&#xff0c;许多行业的工作方式正在发生显著变化。一些原本依赖人工处理的任务&#xff0c;正逐渐由AI接手并优化。在未来&#xff0c;AI将不仅仅是辅助工具&#xff0c;它可能会彻底改变某些行业的运作模式&#xff0c;…

通过AI技术克服自动化测试难点(下)

前面的文章里我们对可以应用到测试中的AI技术做了整体介绍&#xff0c;详细介绍了OpenCV技术、OCR技术和神经网络&#xff0c;本文我们继续为大家介绍卷积神经网络、数据集以及AI技术在其他方面和测试相关的创新。 卷积神经网络整体上的原理是这样的&#xff0c;首先在底层特征…

筛选因数快速法+map

前言&#xff1a;老是忘记怎么快速筛选因数&#xff0c;我们只需要枚举小于sqrt&#xff08; num &#xff09; 的数&#xff0c;这样可以降低很多复杂度&#xff0c;而且我们的因数一定是成对出现的&#xff0c;所以我们遇到一个因数的时候x&#xff0c;判断 x 2 x^2 x2 是否…

Java基础知识全面总结

第一章&#xff1a;类与对象 第一课&#xff1a;什么是面向对象编程 1.面向对象编程和面向过程编程的区别 无论是面向过程编程还是面向对象编程都是用于解决一个实际问题&#xff0c;当面向过程编程在解决一个问题时&#xff0c;更多的情况下是不会做出重用的设计思考的&…

FreeRTOS-内存管理

FreeRTOS-内存管理 一、内存管理简介二、内存管理实验 一、内存管理简介 在使用FreeRTOS创建任务、队列、信号量等对象时&#xff0c;有动态创建和静态创建(本质上内存分配的问题)FreeRTOS提供了5种动态内存管理算法&#xff0c;分别为heap_1、heap_2、heap_3、heap_4、heap_5…

二叉树的遍历 and 基本操作实现

二叉树的遍历 and 基本操作实现 1.二叉树的遍历前序遍历中序遍历后序遍历层序遍历 2.基本操作实现2.1 获取节点个数2.2 获取叶子节点的个数2.3 获取第K层节点的个数2.4 获取二叉树的高度2.5 检测值为value的元素是否存在 1.二叉树的遍历 二叉树由于其特殊结构&#xff0c;有四…

qt的5.sql和opencv 2024.10.11

1.QSqlQuery的增删改查 mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <QSqlQueryModel> #include <QTableView> #include <QLineEdit> #include <QSpinBox> #include <QPushButton> #includ…

如何阻止Chrome自动登录到网站

在数字化时代&#xff0c;浏览器的便利性极大地提高了我们的在线体验。然而&#xff0c;这种便利性有时也会带来隐私和安全方面的担忧。例如&#xff0c;Chrome浏览器可能会在某些网站上自动登录&#xff0c;这对于共享设备或公共计算机来说可能是个问题。本文将指导您如何阻止…

【C++】第三节:类与对象(中)

1、类的6个默认成员函数 如果一个类中什么成员都没有&#xff0c;简称为空类。 空类中真的什么都没有吗&#xff1f;并不是&#xff0c;任何类在什么都不写时&#xff0c;编译器会自动生成以下6个默认成员函数。 默认成员函数&#xff1a;用户没有显式实现&#xff0c;编译器…

【网络安全】将两个 Self-XSS 转变为可利用的 XSS

未经许可,不得转载。 文章目录 Self-XSS-1Self-XSS-2Self-XSS-1 目标应用程序为某在线商店,在其注册页面的First Name字段中注入XSS Payload: 注册成功,但当我尝试登录我的帐户时,我得到了403 Forbidden,即无法登录我的帐户。 我很好奇为什么我无法登录我的帐户,所以我…

SpringBoot集成Redis基础知识

Redis是干什么用的 Redis是一个开源的内存数据库&#xff0c;因其高性能、高可用性和丰富的数据结构&#xff0c;被广泛应用于多种场景下的数据存储和处理需求。以下是Redis的主要用途&#xff1a; 缓存&#xff1a;Redis最常用的用途是作为高性能缓存层&#xff0c;以减轻数…

docker部署虚拟机

创建新的容器web02&#xff0c;-v表示目录映射&#xff0c;-p时端口映射&#xff0c;把宿主机目录挂载到容器中 docker run -itd -p 80:80 -v /data/webapps/www/:/usr/share/nginx/html/ --nameweb02 nginx:latest 此时我们在发布网站时只需要放在宿主机的目录里就可以了 解…

Python数据分析-学生表现预测

一、研究背景 随着教育水平的不断提升&#xff0c;学生的学习成绩和综合素质得到了越来越多的关注。除了传统的学术成绩外&#xff0c;课外活动、家长支持等因素也在很大程度上影响着学生的学习表现和未来发展。现代社会中&#xff0c;家长、教师、学校和研究人员都在努力寻找…

Spring Boot课程问答:技术难题轻松解决

4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式&#xff0c;是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示&#xff1a; 图4-1系统工作原理…

如何将长链接缩短

在我们平时的网络活动中&#xff0c;经常会遇到需要将长链接缩短的情况。有细心的小伙伴会发现&#xff0c;平时收到的短信里面都会携带一个很短的链接&#xff0c;这就是将长链接缩短之后的效果。长链接不仅不美观&#xff0c;而且在社群、各种网络平台、短信等场景推送时&…

安装SNMP并zabbix监控

windos server 安装SNMP 通过server manage ,add roles and features 选择features-SNMP service,选择next或者install等待安装完成后&#xff0c;server manage-tools-component service进入service管理窗口选择service-SNMP service&#xff0c;并将服务状态改为自动启动和运…

【宽字节注入】

字符编码 url 编码 GBK编码 utf8 编码 宽字节注入 php中的转译函数 宽字节注入介绍 练习 正常输入没有回显&#xff1a; 没有回显 usernameadmin&passwordadmin 闭合单引号&#xff0c;依旧没有回显 usernameadmin and 11%23&passwordadmin利用宽字节尝试闭合,依旧…

嵌入式C语言中链表的插入实现方法

大家好,今天主要给大家分享一下,如何使用链表插入功能。 第一:嵌入式中链表具体实现 链表查找思路:从链表的a0起,判断是否为第i结点,若是则返回该结点的指针,否则查找下一结点,依次类推。 具体代码的链表插入实现: linklist Locate(linklist_t h, data_t x) { …

基于差分进化灰狼混合优化的SVM(DE-GWO-SVM)数据预测算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1 DE优化 4.2 GWO优化 5.完整程序 1.程序功能描述 基于差分进化灰狼混合优化的SVM(DE-GWO-SVM)数据预测算法matlab仿真&#xff0c;对比SVM和GWO-SVM。 2.测试软件版本以及运行结果展示…