FPGA之简易DDS信号发生器设计

这篇具有很好参考价值的文章主要介绍了FPGA之简易DDS信号发生器设计。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

设计一个能产生频率可变、相位可调的能产生正弦波、三角波、方波、锯齿波的信号发生器。


一、DDS信号发生器

1.DDS是什么

DDS 是直接数字式频率合成器(Direct Digital Synthesizer)的英文缩写,是一项关键的数字化技术。与传统的频率合成器相比,DDS 具有低成本、低功耗、高分辨率和快速转换时间等优点,广泛使用在电信与电子仪器领域,是实现设备全数字化的一个关键技术。作为设计人员,我们习惯称它为信号发生器,一般用它产生正弦、锯齿、方波等不同波形或不同频率的信号波形,在电子设计和测试中得到广泛应用。
fpgadds信号发生器,我的FPGA自学笔记,fpga开发
注:图片摘自《FPGA Verliog开发实战指南》
图中主要包括相位累加器、相位调制器、波形存储器、数模转换器四大结构。

2.DDS工作原理

频率字K,数值大小控制输出信号的频率大小,数值越大输出信号频率越高,反之越小。

相位字P,数值大小控制输出信号的相位偏移,主要用于相位的信号调制。

相位累加器输入为频率字输入K表示相位增量,设其位宽为N,满足等式K=2^N*fout/fclk,其在输入相位累加器之前,在系统时钟同步下做数据寄存,数据改变时不会干扰相位累加器的正常工作。

波形数据表 ROM 中存有一个完整周期的正弦波信号。假设波形数据 ROM 的地址位宽为 12 位,存储数据位宽为 8 位,即 ROM 有 212 = 4096 个存储空间,每个存储空间可存储 1字节数据。将一个周期的正弦波信号,沿横轴等间隔采样 212 = 4096 次,每次采集的信号幅度用 1 字节数据表示,最大值为 255,最小值为 0。将 4096 次采样结果按顺序写入 ROM的 4096 个存储单元,一个完整周期正弦波的数字幅度信号写入了波形数据表 ROM 中。波形数据表 ROM 以相位调制器传入的相位码为 ROM 读地址,将地址对应存储单元中的电压幅值数字量输出。

D/A 转 换 器 将 输 入 的 电 压 幅 值 数 字 量 转 换 为 模 拟 量 输 出 , 就 得 到 输 出 信 号CLK_OUT。

输出信号 CLK_OUT 的信号频率 fOUT = K * fCLK / 2N。当 K = 1 时,可得 DDS 最小分辨率为:fOUT = fCLK / 2N,此时输出信号频率最低。根据采样定理,K 的最大值应小于 2N / 2。

相位累加器每计数 2N 次,对应一个正弦周期。而相位累加器 1 秒钟计数 fCLK次,在 k=1 时,DDS 输出的时钟频率就是频率分辨率。频率控制字 K 增加时,相位累加器溢出的频率增加,对应 DDS 输出信号 CLK_OUT 频率变为 K 倍的 DDS 频率分辨率。

DAC:自波形数据表 ROM 输出的波形数据传入 D/A 转换器转换为模拟信号。D/A 转换器即数/模转换器,简称 DAC(Digital to Analog Conver),是指将数字信号转换为模拟信号的电子元件或电路。

DAC使用外部挂载的高速AD/DA板卡。

二、模块代码

1.调用rom模块储存波形图

首先使用matlab产生四种波形的mif文件,深度为4096*4,matlab代码见另一篇文章。

2.按键控制模块

module key_ctrl(
	input wire clk,
	input wire rst_n,
	input wire [3:0]key,
	
	output reg [3:0]wave_select
);

	wire key0;
	wire key1;
	wire key2;
	wire key3;
	
	parameter	sin_wave=4'b0001,	//正弦波
					squ_wave=4'b0010,	//方波
					tri_wave=4'b0100,	//三角波
					saw_wave=4'b1000;	//锯齿波
	
	key_filter key_filter0(

		.clk(clk),
		.rst_n(rst_n),
		.key_in(key[0]),
		
		.key_flag(key0)
		
	);
	
		key_filter key_filter1(

		.clk(clk),
		.rst_n(rst_n),
		.key_in(key[1]),
		
		.key_flag(key1)
		
	);
	
		key_filter key_filter2(

		.clk(clk),
		.rst_n(rst_n),
		.key_in(key[2]),
		
		.key_flag(key2)
		
	);
	
		key_filter key_filter3(

		.clk(clk),
		.rst_n(rst_n),
		.key_in(key[3]),
		
		.key_flag(key3)
		
	);
	
	always @(posedge clk or negedge rst_n)
	if(!rst_n)
		wave_select<=4'b0000;
	else if(key0)
		wave_select<=sin_wave;
	else if(key1)
		wave_select<=squ_wave;
	else if(key2)
		wave_select<=tri_wave;
	else if(key3)
		wave_select<=saw_wave;
	else
		wave_select<=wave_select;

endmodule 

按下四个按键分别生成四种波形

2.按键消抖模块

module key_filter
#(parameter CNT_MAX=999_999)
(

	input wire clk,
	input wire rst_n,
	input wire key_in,
	
	output reg key_flag
	
);
	
	reg [19:0]cnt_20;
	
	always @(posedge clk or negedge rst_n)
	if(!rst_n)
		cnt_20<=1'b0;
	else if(key_in==1'b1)
		cnt_20<=1'b0;
	else if(cnt_20==CNT_MAX &&  key_in==1'b0)
		cnt_20<=cnt_20;
	else
		cnt_20<=cnt_20+1'b1;
		
	always @(posedge clk or negedge rst_n)
	if(!rst_n)
		key_flag<=1'b0;
	else if(cnt_20==CNT_MAX-1'b1)
		key_flag<=1'b1;
	else 
		key_flag<=1'b0;
		
endmodule 

3.DDS生成模块

module dds(
	input wire clk,
	input wire rst_n,
	input wire [3:0]wave_select,

	output wire [7:0]data_out
);

	parameter	sin_wave=4'b0001,	//正弦波
					squ_wave=4'b0010,	//方波
					tri_wave=4'b0100,	//三角波
					saw_wave=4'b1000;	//锯齿波
//本实验,我们希望输出一个频率为 500Hz,初相位为π/2 的正弦波信号。
//计算参数 FREQ_CTRL,即频率输入字 K。
//FREQ_CTRL = K = 2N * fOUT / fCLK,其中 N = 32(相位累加器输出值 fre_add 的位宽)、 fOUT = 500Hz,
//fCLK = 50MHz,带入公式,FREQ_CTRL = K = 42949.67296 ,取整数部分为42949;
//PHASE_CTRL = P =  / (2π / 2M),其中 M =12(输入 ROM 地址位宽)、 = π / 2,带入公式,
//PHASE_CTRL = P = 1024。
//计算参数 PHASE_CTRL,即相位输入字 P。
	parameter	FREQ_CTRL=32'd42949,//相位累加器单次累加值,
					PHASE_CTRL=12'd1024;//相位偏移量
	
	reg [31:0]fre_add;	//相位累加器
	reg [11:0]rom_addr_reg;	//相位调制后的相位码
	reg [13:0]rom_addr;	//ROM读地址
	
	//相位累加器
	always @(posedge clk or negedge rst_n)
	if(!rst_n)
		fre_add<=1'b0;
	else
		fre_add<=fre_add+FREQ_CTRL;
		
	always @(posedge clk or negedge rst_n)
	if(!rst_n)
		begin
			rom_addr<=1'b0;
			rom_addr_reg<=1'b0;
		end
	else
		case(wave_select)
			sin_wave:
				begin
					rom_addr_reg<=fre_add[31:20]+PHASE_CTRL;
					rom_addr<=rom_addr_reg;
				end
			squ_wave:
				begin
					rom_addr_reg<=fre_add[31:20]+PHASE_CTRL;
					rom_addr<=rom_addr_reg+14'd4096;
				end
			tri_wave:
				begin
					rom_addr_reg<=fre_add[31:20]+PHASE_CTRL;
					rom_addr<=rom_addr_reg+14'd8192;
				end
			saw_wave:
				begin
					rom_addr_reg<=fre_add[31:20]+PHASE_CTRL;
					rom_addr<=rom_addr_reg+14'd12288;
				end
			default:
				begin
					rom_addr_reg<=fre_add[31:20]+PHASE_CTRL;
					rom_addr<=rom_addr_reg;
				end
		endcase
		
		wave_ip wave_ip0(
			.address(rom_addr),
			.clock(clk),
			.q(data_out)
		);

endmodule 

4.顶层模块

module dds_top(
	input wire clk,
	input wire rst_n,
	input wire [3:0]key,
	
	output wire dac_clk,	//输出DAC模块时钟
	output wire [7:0]dac_data
);

	wire [3:0]wave_select;
	
	assign dac_clk=~clk;
	
	dds dds0(
		.clk(clk),
		.rst_n(rst_n),
		.wave_select(wave_select),

		.data_out(dac_data)
	);
	
	key_ctrl key_ctrl0(
		.clk(clk),
		.rst_n(rst_n),
		.key(key),
		
		.wave_select(wave_select)
	);
endmodule 

5.RTL视图

fpgadds信号发生器,我的FPGA自学笔记,fpga开发

三、仿真测试模块

1.仿真测试代码

`timescale 1ns/1ns
`define clk_period 20

module dds_top_tb;

	reg clk;
	reg rst_n;
	reg [3:0]key;
	
	wire dac_clk;	//输出DAC模块时钟
	wire [7:0]dac_data;
	reg [21:0]tb_cnt;
	reg key_in;
	reg [1:0]cnt_key;
	
	defparam	dds_top_inst.key_ctrl0.key_filter0.CNT_MAX=24,
				dds_top_inst.key_ctrl0.key_filter1.CNT_MAX=24,
				dds_top_inst.key_ctrl0.key_filter2.CNT_MAX=24,
				dds_top_inst.key_ctrl0.key_filter3.CNT_MAX=24;
	
	parameter	CNT_1MS = 20'd19000 ,
					CNT_11MS = 21'd69000 ,
					CNT_41MS = 22'd149000 ,
					CNT_51MS = 22'd199000 ,
					CNT_60MS = 22'd249000 ;

	dds_top dds_top_inst(
		.clk(clk),
		.rst_n(rst_n),
		.key(key),
		
		.dac_clk(dac_clk),	//输出DAC模块时钟
		.dac_data(dac_data)
	);
	
	initial clk=1'b1;
	always #(`clk_period/2) clk=~clk;
	
	initial begin
		rst_n=1'b0;
		#(`clk_period*20+1);
		rst_n=1'b1;
	end
	
	//tb_cnt:按键过程计数器,通过该计数器的计数时间来模拟按键的抖动过程
	always@(posedge clk or negedge rst_n)
	if(rst_n == 1'b0)
		tb_cnt <= 22'b0;
	else if(tb_cnt == CNT_60MS)
		tb_cnt <= 22'b0;
	else 
		tb_cnt <= tb_cnt + 1'b1;
		
	 //key_in:产生输入随机数,模拟按键的输入情况
	always@(posedge clk or negedge rst_n)
	if(rst_n == 1'b0)
		key_in <= 1'b1;
	else if((tb_cnt >= CNT_1MS && tb_cnt <= CNT_11MS) || (tb_cnt >= CNT_41MS && tb_cnt <= CNT_51MS))
		key_in <= {$random} % 2;
	else if(tb_cnt >= CNT_11MS && tb_cnt <= CNT_41MS)
		key_in <= 1'b0;
	else
		key_in <= 1'b1;
		
	always@(posedge clk or negedge rst_n)
	if(rst_n == 1'b0)
		cnt_key <= 2'd0;
	else if(tb_cnt == CNT_60MS)
		cnt_key <= cnt_key + 1'b1;
	else
		cnt_key <= cnt_key;
	
	always@(posedge clk or negedge rst_n)
	if(rst_n == 1'b0)
		key <= 4'b1111;
	else
		case(cnt_key)
			0: key <= {3'b111,key_in};
			1: key <= {2'b11,key_in,1'b1};
			2: key <= {1'b1,key_in,2'b11};
			3: key <= {key_in,3'b111};
			default:key <= 4'b1111;
		endcase
endmodule 

2.仿真波形图

fpgadds信号发生器,我的FPGA自学笔记,fpga开发
可以看到,第一部分波形为正弦波,第二部分为方波,第三部分为三角波,第四部分为锯齿波,设计成功。


总结

可以通过dds代码中频率字与相位字的改变来实现更多的波形变化。文章来源地址https://www.toymoban.com/news/detail-781102.html

到了这里,关于FPGA之简易DDS信号发生器设计的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 信号发生器:Intel FPGA DDS(NCO)+双路DAC(AD9767)输出正余弦信号

    Quartus18.1 小梅哥AC620开发板+ACM9767模块 示波器 ACM9767模块使用的是ADI公司的AD9767芯片,14位CMOS 双通道DAC,125Msps转换率。 输出形式为差分电流输出,输出电流满量程范围为可设置为 2~20mA。 AD9767的两路DA输出都为补码形式的电流输出IoutA和IoutB。当AD9767数字输入为满量程时(DAC的

    2024年03月24日
    浏览(58)
  • 基于vivado+Verilog FPGA开发 — 基于AD9767高速DAC的DDS信号发生器

    目录  一、功能定义 二、设计输入  1、主模块 2、DDS模块 3、 按键消抖模块 三、功能仿真  四、综合优化 五、布局布线 六、时序仿真 七、板级调试  代码规范:Verilog 代码规范_verilog代码编写规范-CSDN博客 开发流程:FPGA基础知识----第二章 FPGA 开发流程_fpga 一个项目的整个

    2024年03月18日
    浏览(72)
  • 毕设分享|基于51单片机DDS信号发生器设计

    在电子通信技术日益发展的时代潮流下,直接式(DFS)和锁相式(PLL)已经不能满足生活和科研方面对于频率技术的需求。经过科研人员的不断攻坚下,直接数字频率合成器(DDS)应运而生。它现在广泛运用于移动通信、电子雷达、航天等方面。本次设计主要通过FPGA模块+单片机最小

    2024年02月03日
    浏览(55)
  • 【毕业设计—DDS信号发生器】Quartus II 软件新建工程

    大学四年的时间转瞬即逝,2023年我将迎来我的本科毕业。为了记录自己的研究进展,我将在这儿分享我的毕业设计进度~~博客涉及的知识点,如有不对,欢迎大家及时纠正,共同进步! 我安装的是Quartus II 13.1 版本。 1.在电脑D磁盘下新建一个文件夹【DDS】,然后分别新建4个子

    2024年02月03日
    浏览(48)
  • FPGA实验笔记_Vivado:DDS信号发生器;数码管;基于DHT11的温湿度传感器

    目录 1、 FPGA的DDS信号发生器 1.1、DDS简介 1.2、ROM IP核的生成 1.3、波形数据的生成 1.4、 ROM的调用 1.5、 完整代码(包括拓展部分) 2、数码管显示 2.1、数码管简要说明 2.2、SM410564 3、基于DHT11的温湿度传感器 3.1、DHT11 3.2、基本思路 3.3、数据分离模块(BTD) 3.4、数据转换模块(

    2024年02月04日
    浏览(56)
  • 基于STC89C52RC芯片 高频DDS信号发生器AD9851信号源方波正弦波系统设计

    一开始是使用按键进行频率输出数值的增加或者减少,后改进成使用EC11调节输出数值,使数值的输出更加顺滑流畅。 AD9851.c AD9851.h LCD12864.c LCD12864.h EC11.c EC11.h main.c

    2024年02月05日
    浏览(53)
  • FPGA实验五:信号发生器设计

    目录 一、实验目的 二、设计要求 三、实验代码 1.代码原理分析 2.代码设计思路

    2024年02月12日
    浏览(40)
  • DDS信号发生器(stm32+ad9850)

    正点原子精英板、ad9850、杜邦线 https://download.csdn.net/download/qq_45974939/87672298

    2024年02月16日
    浏览(39)
  • 基于vivado DDS ip核的DDS信号发生器(可调频调相)

    基于Vivado DDS ip核的DDS信号发生器: 在Vivado软件中调出DDS ip核进行设置,很多参数可以参考xilinx官方手册,比较重要的是System Clock系统时钟和Phase Widh相位宽度的设置,这是最终得到波形的频率和相位所需要的基础参数,在这里我分别设置为50Mhz和16bit。 在implementation页面设置频

    2024年02月12日
    浏览(34)
  • 基于AD9767高速DAC的DDS信号发生器(Verilog&Vivado)

    基于AD9767高速DAC的DDS信号发生器 提示:以下是本篇文章正文内容,下面案例可供参考 1.做一个双通道的信号发生器; 2.简单调整每个通道的频率输出; 3.能够调整每个通道的输出相位; 4.能够输出正弦波,三角波,方波。 代码如下(示例): 代码如下(示例): 【附件:】

    2024年02月06日
    浏览(57)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包