基于FPGA的DDS信号发生器设计(频率、幅度、波形可调)

目录

 

一、DDS原理

二、整体系统设计

1、现有条件

2、实验性能分析

3、系统模块设计

(1)频率设置模块(f_word_set.v)

(2)波形设置模块(wave_set.v)

(3)幅度设置模块(amplitude_set.v)

(4)DDS模块(DDS.v)

(5)顶层模块(DDS_top.v)

4、仿真验证

三、总结


工程下载地址:https://download.csdn.net/download/qq_33231534/12848911


一、DDS原理

DDS全称为直接数字频率合成(Direct Digital Synthesis),其基本原理是在一个周期波形数据下,通过选取其中全部数据或抽样部分数据组成新的波形,由奈奎斯特采样定理可知,最低两个采样点就可以组成一个波形,但实际上最少需要4个点。其原理框图如下:

其主要由相位控制字、频率控制字、相位累加器、波形存储器几部分组成。

波形存储器:存储一个周期波形的离散信号;

频率控制字:用以控制生成的波形频率。

相位累加器:用来控制波形的相位累加,组成完整的波形显示。

相位控制字:用以控制波形起始位置。

二、整体系统设计

1、现有条件

本实验平台是小梅哥的AC620开发板,板载12位采样精度的DAC芯片TLV5618,输出电压范围为0V~4.096V,其最高时钟频率为20MHz。

2、实验性能分析

由于板载的DAC芯片最高采样频率为20MHz,为了设计余量,同时只是为了验证设计的正确性,这里将DAC采样频率设定为10MHz,同时TLV5618的DAC芯片采用的是SPI借口通信,传输一次数据需要差不多16个时钟周期,这里将其看成20个时钟周期,因此接收数据的最高频率为10/20=0.5MHz。因此本实验信号发生器最高生成的频率设置为0.5MHz。

3、系统模块设计

这里没有做DAC驱动模块,在我前面的博客数据采集系统中有用到DAC驱动模块,由于我身边没有示波器,无法测波形,只能通过仿真软件测试设计的正确性,因此这里不做DAC驱动模块。下边将对各个模块分别讲解:

(1)频率设置模块(f_word_set.v)

这里通过按键依次对频率进行改变,这里设置可支持的频率包括 1Hz、10Hz、100Hz、500Hz、1KHz、5KHz、10KHz、50KHz、100KHz、200KHz、500KHz 共11种不同的选择,按键按下依次切换。

这里对不同频率波形的频率控制字进行计算。为了获取1Hz、10Hz等低频信号,在对其进行相位累加的时候,需要进行其他的计数,这里选取20位宽供其累加,还有12位供波形数据累加,共32位数据进行累加操作,例如fre_acc[31:0],其中低20位数据作为补充累加,高12位作为波形数据累加。对于要生成1Hz波形来说,假设每次需要加x个时钟周期,1Hz周期为T=1/f(此处周期用ns表示),在Tns后32位数据加满,采用50MHz系统时钟,1系统时钟周期20ns,因此有:(T/20)*x=2^32,因此频率控制字计算公式为:            x = (2^32)*20/T   =   (2^32)*20*f(hz)/1000000000   ;

最后得出的各个频率的控制字数据为:

1Hz~86;10Hz~859;100Hz~8590;500Hz~42950;1000Hz~85899;5000Hz~429497;10KHz~858993;50KHz~4294967;100KHz~8589935;200KHz~17179869;500KHz~42949673。

代码如下,其中里边按键消抖模块采用前面博客写的按键消抖模块。


// Company  : 
// Engineer : 
// -----------------------------------------------------------------------------
// https://blog.csdn.net/qq_33231534    PHF's CSDN blog
// -----------------------------------------------------------------------------
// Create Date    : 2020-09-04 15:25:53
// Revise Data    : 2020-09-04 17:06:45
// File Name      : F_word_set.v
// Target Devices : XC7Z015-CLG485-2
// Tool Versions  : Vivado 2019.2
// Revision       : V1.1
// Editor         : sublime text3, tab size (4)
// Description    : 频率控制字的生成
 

module F_word_set(
	input				clk		,
	input				rst_n	,
	input				key1_in	,

	output	reg	[25:0]	f_word	
	);
	
	wire		key_flag	;
	wire		key_state	;
	reg	[3:0]	cnt			;

	key_filter fword_key (
			.clk       (clk),
			.rst_n     (rst_n),
			.key_in    (key1_in),
			.key_flag  (key_flag),
			.key_state (key_state)
		);

	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			cnt <= 4'd0;
		end
		else if (key_flag) begin
			if (cnt==4'd10) begin
				cnt <= 4'd0;
			end
			else begin
				cnt <= cnt + 1'b1;
			end
		end
	end

	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			f_word <= 0;
		end
		else begin
			case(cnt)
				4'd0:f_word = 26'd86;		//1Hz
				4'd1:f_word = 26'd859;		//10Hz
				4'd2:f_word = 26'd8590;		//100Hz
				4'd3:f_word = 26'd42950;	//500Hz
				4'd4:f_word = 26'd85899;	//1kHz
				4'd5:f_word = 26'd429497;	//5kHz
				4'd6:f_word = 26'd858993;	//10kHz
				4'd7:f_word = 26'd4294967;	//50kHz
				4'd8:f_word = 26'd8589935;	//100kHz
				4'd9:f_word = 26'd17179869;	//200kHz
				4'd10:f_word = 26'd42949673;//500kHz
				default:;
			endcase
		end
	end
endmodule

(2)波形设置模块(wave_set.v)

这里主要通过按键设置4种波形:正弦波、三角波、锯齿波、方波。这里通过按键按下,改变波形控制字的数据,这里没有放各个波形的ROM模块,ROM模块放在DDS模块中,以便进行数据输出。


// Company  : 
// Engineer : 
// -----------------------------------------------------------------------------
// https://blog.csdn.net/qq_33231534    PHF's CSDN blog
// -----------------------------------------------------------------------------
// Create Date    : 2020-09-04 15:10:48
// Revise Data    : 2020-09-04 15:23:44
// File Name      : wave_set.v
// Target Devices : XC7Z015-CLG485-2
// Tool Versions  : Vivado 2019.2
// Revision       : V1.1
// Editor         : sublime text3, tab size (4)
// Description    : dds信号发生器的波形选择,按键按下切换波形
 

module wave_set(
	input				clk		,
	input				rst_n	,
	input				key0_in	,

	output	reg	[1:0]	wave_c		//wave_c oo~正弦波  01~三角波  10~锯齿波  11~方波
	);

	wire	key_flag	;
	wire	key_state	;

	key_filter wave_key (
			.clk       (clk),
			.rst_n     (rst_n),
			.key_in    (key0_in),
			.key_flag  (key_flag),
			.key_state (key_state)
		);

	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			wave_c <= 0; //默认正弦波
		end
		else if (key_flag) begin
			wave_c <= wave_c + 1'b1;
		end
	end
endmodule

(3)幅度设置模块(amplitude_set.v)

通过按键控制波形幅度设置,这里可以设置为原波形的1倍、1/2倍、1/4倍、1/8倍、1/16倍。


// Company  : 
// Engineer : 
// -----------------------------------------------------------------------------
// https://blog.csdn.net/qq_33231534    PHF's CSDN blog
// -----------------------------------------------------------------------------
// Create Date    : 2020-09-05 09:35:58
// Revise Data    : 2020-09-05 09:35:58
// File Name      : amplitude_set.v
// Target Devices : XC7Z015-CLG485-2
// Tool Versions  : Vivado 2019.2
// Revision       : V1.1
// Editor         : sublime text3, tab size (4)
// Description    : 信号电压幅度设置 1/2/4/8/16分之一

module amplitude_set(
	input				clk		,
	input				rst_n	,
	input				key2_in	,

	output	reg	[4:0]	amplitude	
	);

	reg	[2:0]	cnt			;
	wire		key_flag	;
	wire		key_state	;


	key_filter amplitude_key (
				.clk       (clk),
				.rst_n     (rst_n),
				.key_in    (key2_in),
				.key_flag  (key_flag),
				.key_state (key_state)
			);

	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			cnt <= 3'd0;
		end
		else if (key_flag) begin
			if (cnt == 3'd4) begin
				cnt <= 3'd0;
			end
			else begin
				cnt <= cnt + 1'b1;
			end
		end
	end

	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			amplitude <= 0;			
		end
		else begin
			case(cnt)
				3'd0: amplitude <= 5'd1;
				3'd1: amplitude <= 5'd2;
				3'd2: amplitude <= 5'd4;
				3'd3: amplitude <= 5'd8;
				3'd4: amplitude <= 5'd16;
				default:amplitude <= 5'd1;
			endcase
		end
	end
endmodule

(4)DDS模块(DDS.v)

这个模块例化四个波形存储ROM模块(ROM模块很简单,在数据采集系统里也写过,这里不再赘述),同时进行DDS的相位累加,通过查找表,读出ROM中的数据。


// Company  : 
// Engineer : 
// -----------------------------------------------------------------------------
// https://blog.csdn.net/qq_33231534    PHF's CSDN blog
// -----------------------------------------------------------------------------
// Create Date    : 2020-09-04 17:09:55
// Revise Data    : 2020-09-04 17:31:29
// File Name      : DDS.v
// Target Devices : XC7Z015-CLG485-2
// Tool Versions  : Vivado 2019.2
// Revision       : V1.1
// Editor         : sublime text3, tab size (4)
// Description    : DDS模块
 
module DDS(
	input				clk			,
	input				rst_n		,
	input		[25:0]	f_word		,
	input		[1:0]	wave_c		,
	input		[11:0]	p_word		,
	input		 	,

	output	reg	[11:0]	dac_data	
	);

	localparam	DATA_WIDTH = 4'd12;
	localparam	ADDR_WIDTH = 4'd12;

	reg		[11:0]	addr	 ;
	wire	[11:0]	dac_data0;
	wire	[11:0]	dac_data1;
	wire	[11:0]	dac_data2;
	wire	[11:0]	dac_data3;


	//波形选择
	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			dac_data <= 12'd0;
		end
		else begin
			case(wave_c)
				2'b00:dac_data <= dac_data0/amplitude;	//正弦波
				2'b01:dac_data <= dac_data1/amplitude;	//三角波
				2'b10:dac_data <= dac_data2/amplitude;	//锯齿波
				2'b11:dac_data <= dac_data3/amplitude;	//方波
				default:;
			endcase
		end
	end

	//相位累加器
	reg	[31:0]	fre_acc;
	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			fre_acc <= 0;
		end
		else begin
			fre_acc <= fre_acc + f_word;
		end
	end

	//生成查找表地址
	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			addr <= 0;
		end
		else begin
			addr <= fre_acc[31:20] + p_word;
		end
	end

	//正弦波
	sin_rom #(
		.DATA_WIDTH(DATA_WIDTH),
		.ADDR_WIDTH(ADDR_WIDTH)
	) inst_sin_rom (
		.addr (addr),
		.clk  (clk),
		.q    (dac_data0)
	);

	//三角波
	sanjiao_rom #(
			.DATA_WIDTH(DATA_WIDTH),
			.ADDR_WIDTH(ADDR_WIDTH)
		) inst_sanjiao_rom (
			.addr (addr),
			.clk  (clk),
			.q    (dac_data1)
		);

	//锯齿波
	juchi_rom #(
			.DATA_WIDTH(DATA_WIDTH),
			.ADDR_WIDTH(ADDR_WIDTH)
		) inst_juchi_rom (
			.addr (addr),
			.clk  (clk),
			.q    (dac_data2)
		);

	//方波
	fangbo_rom #(
			.DATA_WIDTH(DATA_WIDTH),
			.ADDR_WIDTH(ADDR_WIDTH)
		) inst_fangbo_rom (
			.addr (addr),
			.clk  (clk),
			.q    (dac_data3)
		);

endmodule

(5)顶层模块(DDS_top.v)


// Company  : 
// Engineer : 
// -----------------------------------------------------------------------------
// https://blog.csdn.net/qq_33231534    PHF's CSDN blog
// -----------------------------------------------------------------------------
// Create Date    : 2020-09-04 18:32:24
// Revise Data    : 2020-09-04 19:03:10
// File Name      : DDS_top.v
// Target Devices : XC7Z015-CLG485-2
// Tool Versions  : Vivado 2019.2
// Revision       : V1.1
// Editor         : sublime text3, tab size (4)
// Description    : 
 
module DDS_top(
	input					clk			,
	input					rst_n		,
	input					key0_in		,
	input					key1_in		,
	input					key2_in		,

	output	wire	[11:0]	dac_data	
	);

	wire	[1:0]	wave_c		;
	wire	[25:0]	f_word		;
	wire	[4:0]	amplitude	;

	DDS inst_DDS
	(
		.clk      (clk),
		.rst_n    (rst_n),
		.f_word   (f_word),
		.wave_c   (wave_c),
		.p_word   (12'd0),
		.amplitude(amplitude),
		.dac_data (dac_data)
	);

	F_word_set inst_F_word_set 
	(
		.clk(clk), 
		.rst_n(rst_n), 
		.key1_in(key1_in), 
		.f_word(f_word)
	);

	wave_set inst_wave_set 
	(
		.clk(clk), 
		.rst_n(rst_n), 
		.key0_in(key0_in), 
		.wave_c(wave_c)
	);

	amplitude_set inst_amplitude_set(
		.clk	(clk)		,
		.rst_n	(rst_n)		,
		.key2_in(key2_in)	,

		.amplitude(amplitude)	
	);

endmodule

4、仿真验证

由于仿真时间会比较长,因此对不同波形,不同功能分开测试,其主要代码如下:


// Company  : 
// Engineer : 
// -----------------------------------------------------------------------------
// https://blog.csdn.net/qq_33231534    PHF's CSDN blog
// -----------------------------------------------------------------------------
// Create Date    : 2020-09-04 19:07:11
// Revise Data    : 2020-09-04 22:50:36
// File Name      : DDS_top_tb.v
// Target Devices : XC7Z015-CLG485-2
// Tool Versions  : Vivado 2019.2
// Revision       : V1.1
// Editor         : sublime text3, tab size (4)
// Description    : 


`timescale 1ns/1ns
module DDS_top_tb();

	reg					clk			;
	reg					rst_n		;
	reg					key0_in		;
	reg					key1_in		;
	reg					key2_in		;

	wire	[11:0]		dac_data	;

	localparam	clk_period = 20;

	DDS_top inst_DDS_top
		(
			.clk      (clk),
			.rst_n    (rst_n),
			.key0_in  (key0_in),
			.key1_in  (key1_in),
			.key2_in  (key2_in),
			.dac_data (dac_data)
		);


		initial clk = 0;
		always #(clk_period/2) clk = ~clk;

		initial begin
			#1;
			rst_n = 0;
			key0_in = 1;
			key1_in = 1;
			key2_in = 1;
			#(clk_period*20);
			rst_n = 1;
			#(clk_period*10);

			// key0_in = 0;
			// #30000000;
			// key0_in = 1;
			// #30000000;
			// key0_in = 0;
			// #30000000;
			// key0_in = 1;
			// #30000000;
			// key0_in = 0;
			// #30000000;
			// key0_in = 1;

			//#2000000000;

			key1_in = 0;
			#30000000;
			key1_in = 1;
			#30000000;
			//#200000000;

			key1_in = 0;
			#30000000;
			key1_in = 1;
			#30000000;
			//#20000000;

			key1_in = 0;
			#30000000;
			key1_in = 1;
			#30000000;
			#4000000;

			key2_in = 0;
			#30000000;
			key2_in = 1;
			#30000000;
			#4000000;

			key2_in = 0;
			#30000000;
			key2_in = 1;
			#30000000;
			#4000000;
			
			key2_in = 0;
			#30000000;
			key2_in = 1;
			#30000000;
			#4000000;

			// key1_in = 0;
			// #30000000;
			// key1_in = 1;
			// #30000000;
			// #2000000;

			// key1_in = 0;
			// #30000000;
			// key1_in = 1;
			// #30000000;
			// #400000;

			// key1_in = 0;
			// #30000000;
			// key1_in = 1;
			// #30000000;
			// #200000;

			// key1_in = 0;
			// #30000000;
			// key1_in = 1;
			// #30000000;
			// #40000;

			// key1_in = 0;
			// #30000000;
			// key1_in = 1;
			// #30000000;
			// #20000;

			// key1_in = 0;
			// #30000000;
			// key1_in = 1;
			// #30000000;
			// #10000;

			// key1_in = 0;
			// #30000000;
			// key1_in = 1;
			// #30000000;
			// #4000;

			
			#(clk_period*20);
			$stop;
		end
endmodule

仿真波形:

(1)1Hz正弦波

(2)1Hz三角波

(3)1Hz锯齿波

(4)1Hz方波

(5)不同频率方波

(6)不同频率方波

(7)不同频率正弦波

(8)不同频率三角波

(9)不同频率锯齿波

(10)不同幅度正弦波

三、总结

整个设计从仿真来看,符合预期条件,但是没有通过DAC模块进行上板测试。此设计基本完成了DDS信号发生器的设计,有其他不足之处,敬请指正。

猜你喜欢

转载自blog.csdn.net/qq_33231534/article/details/108424647