Design of DDS signal generator based on FPGA (adjustable frequency, amplitude and waveform)

table of Contents

 

1. DDS principle

2. Overall system design

1. Existing conditions

2. Experimental performance analysis

3. System module design

(1) Frequency setting module (f_word_set.v)

(2) Waveform setting module (wave_set.v)

(3) Amplitude setting module (amplitude_set.v)

(4) DDS module (DDS.v)

(5) Top module (DDS_top.v)

4. Simulation verification

Three, summary


Project download address: https://download.csdn.net/download/qq_33231534/12848911


1. DDS principle

DDS is called Direct Digital Synthesis (Direct Digital Synthesis). Its basic principle is to form a new waveform by selecting all of the data or sampled part of the data in a cycle of waveform data. According to the Nyquist sampling theorem, the minimum two samples Points can form a waveform, but in fact, at least 4 points are required. The principle block diagram is as follows:

It is mainly composed of phase control word, frequency control word, phase accumulator and waveform memory.

Waveform memory: store a discrete signal of a periodic waveform;

Frequency control word: used to control the generated waveform frequency.

Phase accumulator: used to control the phase accumulation of the waveform to form a complete waveform display.

Phase control word: used to control the starting position of the waveform.

2. Overall system design

1. Existing conditions

This experimental platform is Xiaomeige’s AC620 development board, with 12-bit sampling precision DAC chip TLV5618 onboard, with an output voltage range of 0V~4.096V, and its highest clock frequency is 20MHz.

2. Experimental performance analysis

Since the highest sampling frequency of the on-board DAC chip is 20MHz, in order to design a margin and only to verify the correctness of the design, the DAC sampling frequency is set to 10MHz here, and the DAC chip of TLV5618 uses SPI communication, which is transmitted once. The data needs almost 16 clock cycles, which is regarded as 20 clock cycles here, so the highest frequency of receiving data is 10/20=0.5MHz. Therefore, the highest frequency generated by the signal generator in this experiment is set to 0.5MHz.

3. System module design

There is no DAC driver module here. The DAC driver module is useful in my previous blog data acquisition system. Because I don’t have an oscilloscope and can’t measure the waveform, I can only test the correctness of the design through simulation software, so I don’t make a DAC driver module here. . Each module will be explained separately below:

(1) Frequency setting module (f_word_set.v)

Here you can change the frequency in turn by pressing the button. The supported frequencies here include 1Hz, 10Hz, 100Hz, 500Hz, 1KHz, 5KHz, 10KHz, 50KHz, 100KHz, 200KHz, 500KHz, and 11 different options. Press the button to switch in order.

Here the frequency control words of different frequency waveforms are calculated. In order to obtain low-frequency signals such as 1Hz, 10Hz, etc., other counts need to be performed when phase accumulation is performed on them. Here, 20 bits are selected for accumulation, and there are 12 bits for waveform data accumulation. A total of 32 bits of data are accumulated. For example, fre_acc[31:0], where the low 20 bits of data are used as supplementary accumulation, and the high 12 bits are used as waveform data to accumulate. For generating a 1Hz waveform, assuming that x clock cycles need to be added each time, the 1Hz cycle is T=1/f (here the cycle is represented by ns), the 32-bit data is filled up after Tns, and the 50MHz system clock is used. The system clock cycle is 20ns, so: (T/20)*x=2^32, so the frequency control word calculation formula is: x = (2^32)*20/T = (2^32)*20*f( hz)/1000000000;

The final control word data of each frequency is:

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

The code is as follows, in which the button anti-shake module inside uses the button anti-shake module written in the previous blog.


// 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) Waveform setting module (wave_set.v)

Here are four types of waveforms: sine wave, triangle wave, sawtooth wave, and square wave. Here, press the key to change the data of the waveform control word. There is no ROM module for each waveform, and the ROM module is placed in the DDS module for data output.


// 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 setting module (amplitude_set.v)

Press the button to control the waveform amplitude setting, here can be set to 1 times, 1/2 times, 1/4 times, 1/8 times, 1/16 times of the original waveform.


// 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 module (DDS.v)

This module instantiates four waveform storage ROM modules (the ROM module is very simple and has also been written in the data acquisition system, so I will not repeat it here), and at the same time perform the phase accumulation of DDS, and read the data in the ROM through the lookup table.


// 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) Top module (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. Simulation verification

Since the simulation time will be relatively long, different waveforms and different functions are tested separately. The main codes are as follows:


// 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

Simulation waveform:

(1) 1Hz sine wave

(2) 1Hz triangle wave

(3) 1Hz sawtooth wave

(4) 1Hz square wave

(5) Square waves of different frequencies

(6) Square waves of different frequencies

(7) Sine waves of different frequencies

(8) Triangular waves of different frequencies

(9) Sawtooth waves of different frequencies

(10) Sine waves of different amplitudes

Three, summary

From the simulation point of view, the entire design meets the expected conditions, but the DAC module is not tested on the board. This design basically completes the design of the DDS signal generator. There are other shortcomings, please correct me.

 

 

Guess you like

Origin blog.csdn.net/qq_33231534/article/details/108424647