FPGA基础之数字信号发生器(DDS)

一、DDS原理

最近在复习至芯培训课程的DDS部分,DDS的核心主要包括频率发生器、相位累加器和波形存储器三部分。波形存储器的作用是存储不同相位对应地址的波形值,相位累加器根据不同的相位产生对应的地址,用于选择不同的波形值,频率发生器根据输入频率产生频率控制字,其大小决定了相位累加器地址累加的速度,不同的累加速度决定了输出不同的频率大小。

二、本工程的相关说明

实现的功能如下:输入有四个按键,分别对应波形选择、幅度调节、频率加和频率减;输出有蜂鸣器、6个动态数码管和输出波形d_wave。当四个按键有任意一个按下时,蜂鸣器响,数码管显示波形、幅度和频率对应的值,d_dds模块输出对应波形。

1、顶层设计

顶层dds模块包括以下几个部分:按键处理模块(key_ctrl)、脉冲处理模块(flag_ctrl)、信号发生器核心部分(d_dds)、蜂鸣器模块(beep)和6个动态数码管显示模块。顶层RTL视图如下。
在这里插入图片描述dds顶层代码如下:

module dds(

	input		wire					clk,
	input		wire					rst_n,
	
	input		wire					key_sel,
	input		wire					key_amp,
	input		wire					key_fadd,
	input		wire					key_fsub,

	output	wire					beep,
	output	wire	[9:0]			d_wave,
	output	wire	[2:0]			sel,
	output	wire	[7:0]			seg
);

	wire					flag_s;
	wire					flag_a;
	wire					flag_fa;
	wire					flag_fs;
	
	wire		[1:0]		wave_sel;
	wire		[1:0]		wave_a;
	wire		[19:0]	wave_freq;
 
	key_ctrl key_ctrl_inst(

		.clk					(clk		),
		.rst_n				(rst_n	),
		                   
		.key_sel				(key_sel	),
		.key_amp				(key_amp	),
		.key_fadd			(key_fadd),
		.key_fsub			(key_fsub),
                         
		.flag_s				(flag_s	),
		.flag_a				(flag_a	),
		.flag_fa				(flag_fa	),
		.flag_fs				(flag_fs	)
	);

	flag_ctrl flag_ctrl_inst(

		.clk					(clk			),
		.rst_n				(rst_n		),
		                   
		.flag_s				(flag_s		),
		.flag_a				(flag_a		),
		.flag_fa				(flag_fa		),
		.flag_fs				(flag_fs		),
		                   
		.wave_sel			(wave_sel	),
		.wave_a				(wave_a		),
		.wave_freq			(wave_freq	)
	);

	d_dds d_dds_inst(

		.clk					(clk			),
		.rst_n				(rst_n		),
		                   
		.wave_sel			(wave_sel	),
		.wave_a				(wave_a		),
		.wave_freq			(wave_freq	),
		                   
		.d_wave				(d_wave		)
	);	
	
	beep beep_inst(

		.clk					(clk			),
		.rst_n				(rst_n		),
		                   
		.flag_s				(flag_s		),
		.flag_a				(flag_a		),
		.flag_fa				(flag_fa		),
		.flag_fs				(flag_fs		),
                         
		.beep					(beep			)
	);	
	
	show_data show_data_inst(

		.clk					(clk			),
		.rst_n				(rst_n		),
		                   
		.wave_sel			(wave_sel	),
		.wave_a				(wave_a		),
		.wave_freq			(wave_freq	),
		                   
		.sel					(sel			),
		.seg					(seg			)
	);	
	
endmodule 

2、各模块相关说明

(1)按键处理模块(key_ctrl)

按键处理包括两个步骤,消抖和边缘检测,需要注意的点是需要对每一个按键进行这两步操作,生成对应的四个脉冲信号:波形选择、幅度调节、频率加和减。顶层代码如下:

module key_ctrl(

	input		wire					clk,
	input		wire					rst_n,
	
	input		wire					key_sel,
	input		wire					key_amp,
	input		wire					key_fadd,
	input		wire					key_fsub,

	output	wire					flag_s,
	output	wire					flag_a,
	output	wire					flag_fa,
	output	wire					flag_fs
);

	wire					key_wave1;
	wire					key_wave2;
	wire					key_wave3;
	wire					key_wave4;
	
	key_filter key_filter_inst1(

		.clk					(clk),
		.rst_n				(rst_n),
		
		.key					(key_sel),

		.key_wave			(key_wave1)
	);

	edge_check edge_check_inst1(

		.clk					(clk),
		.rst_n				(rst_n),

		.key					(key_wave1),
		
		.flag_neg			(flag_s),
		.flag_pos			()
	);

	key_filter key_filter_inst2(

		.clk					(clk),
		.rst_n				(rst_n),
		
		.key					(key_amp),

		.key_wave			(key_wave2)
	);

	edge_check edge_check_inst2(

		.clk					(clk),
		.rst_n				(rst_n),

		.key					(key_wave2),
		
		.flag_neg			(flag_a),
		.flag_pos			()
	);

	key_filter key_filter_inst3(

		.clk					(clk),
		.rst_n				(rst_n),
		
		.key					(key_fadd),

		.key_wave			(key_wave3)
	);

	edge_check edge_check_inst3(

		.clk					(clk),
		.rst_n				(rst_n),

		.key					(key_wave3),
		
		.flag_neg			(flag_fa),
		.flag_pos			()
	);

	key_filter key_filter_inst4(

		.clk					(clk),
		.rst_n				(rst_n),
		
		.key					(key_fsub),

		.key_wave			(key_wave4)
	);

	edge_check edge_check_inst4(

		.clk					(clk),
		.rst_n				(rst_n),

		.key					(key_wave4),
		
		.flag_neg			(flag_fs),
		.flag_pos			()
	);

endmodule 

(2)脉冲处理模块(flag_ctrl)

本模块根据四个按键产生的脉冲信号,输出波形选择、幅度和频率的控制信号。需要注意的点是幅度和频率变化只能在选中某一波形的前提条件下进行变化,当切换成其他波形时,要保持上一个波形的设定值不变,初始频率设为50k,按键对应频率加减对应为100Hz。代码如下:

module flag_ctrl(

	input		wire					clk,
	input		wire					rst_n,
	
	input		wire					flag_s,
	input		wire					flag_a,
	input		wire					flag_fa,
	input		wire					flag_fs,
	
	output	reg	[1:0]			wave_sel,
	output	reg	[1:0]			wave_a,
	output	reg	[19:0]		wave_freq
);

	parameter		FREQ = 50_000;

	reg		[1:0]			amp_1;
	reg		[1:0]			amp_2;
	reg		[1:0]			amp_3;	
	reg		[1:0]			amp_4;
	
	reg		[19:0]		freq1;
	reg		[19:0]		freq2;
	reg		[19:0]		freq3;
	reg		[19:0]		freq4;	
	
	
	
	always @ (posedge clk, negedge rst_n)
		begin
			if (!rst_n)
				wave_sel <= 0;
			else
				if (flag_s == 1)
					wave_sel <= wave_sel + 1'b1;
				else
					wave_sel <= wave_sel;
		end
		
//===================== according to wave_sel, adjust amplitude ===========================
		
	always @ (posedge clk, negedge rst_n)
		begin
			if (!rst_n)
				amp_1 <= 0;
			else
				if (wave_sel == 2'b00 && flag_a == 1)
					amp_1 <= amp_1 + 1'b1;
				else
					amp_1 <= amp_1;
		end

	always @ (posedge clk, negedge rst_n)
		begin
			if (!rst_n)
				amp_2 <= 0;
			else
				if (wave_sel == 2'b01 && flag_a == 1)
					amp_2 <= amp_2 + 1'b1;
				else
					amp_2 <= amp_2;
		end

	always @ (posedge clk, negedge rst_n)
		begin
			if (!rst_n)
				amp_3 <= 0;
			else
				if (wave_sel == 2'b10 && flag_a == 1)
					amp_3 <= amp_3 + 1'b1;
				else
					amp_3 <= amp_3;
		end

	always @ (posedge clk, negedge rst_n)
		begin
			if (!rst_n)
				amp_4 <= 0;
			else
				if (wave_sel == 2'b11 && flag_a == 1)
					amp_4 <= amp_4 + 1'b1;
				else
					amp_1 <= amp_1;
		end

//============== according to wave_sel, adjust frequency ==================================

	always @ (posedge clk, negedge rst_n)
		begin
			if (!rst_n)
				freq1 <= FREQ;
			else
				if (wave_sel == 2'b00 && flag_fa == 1)
					freq1 <= freq1 + 20'd100;
				else
					if (wave_sel == 2'b00 && flag_fs == 1)
						freq1 <= freq1 - 20'd100;
					else
						freq1 <= freq1;
		end

	always @ (posedge clk, negedge rst_n)
		begin
			if (!rst_n)
				freq2 <= FREQ;
			else
				if (wave_sel == 2'b01 && flag_fa == 1)
					freq2 <= freq2 + 20'd100;
				else
					if (wave_sel == 2'b01 && flag_fs == 1)
						freq2 <= freq2 - 20'd100;
					else
						freq2 <= freq2;
		end

	always @ (posedge clk, negedge rst_n)
		begin
			if (!rst_n)
				freq3 <= FREQ;
			else
				if (wave_sel == 2'b10 && flag_fa == 1)
					freq3 <= freq3 + 20'd100;
				else
					if (wave_sel == 2'b10 && flag_fs == 1)
						freq3 <= freq3 - 20'd100;
					else
						freq3 <= freq3;
		end

	always @ (posedge clk, negedge rst_n)
		begin
			if (!rst_n)
				freq4 <= FREQ;
			else
				if (wave_sel == 2'b11 && flag_fa == 1)
					freq4 <= freq4 + 20'd100;
				else
					if (wave_sel == 2'b11 && flag_fs == 1)
						freq4 <= freq4 - 20'd100;
					else
						freq4 <= freq4;
		end
		
//=================== according wave_sel, output frequency and amplitude ==========================

	always @ (*)
		case (wave_sel)
			2'b00			:	begin wave_freq = freq1; wave_a = amp_1; end
			2'b01			:	begin wave_freq = freq2; wave_a = amp_2; end
			2'b10			:	begin wave_freq = freq3; wave_a = amp_3; end
			2'b11			:	begin wave_freq = freq4; wave_a = amp_4; end
		endcase

endmodule 

(3)DDS核心模块

本模块包括四个部分,频率发生器、相位累加器、波形存储器和幅度调节器。需要注意的点:频率发生器的控制字word的位宽为什么要是64位,相位累加器的基地址和偏移地址的处理,波形存储器模块在rom中写入.mif文件时特别注意文件路径,应放在工程文件夹目录下,幅度调节器部分应注意对所成的幅值进行+1处理。顶层代码如下:

module d_dds(

	input		wire					clk,
	input		wire					rst_n,
	
	input		wire	[1:0]			wave_sel,
	input		wire	[1:0]			wave_a,
	input		wire	[19:0]		wave_freq,
	
	output	wire	[9:0]			d_wave
);

	wire		[31:0]		f_word;
	wire		[9:0]			addr_rom1024x8;
	wire		[7:0]			wave;

	freq_ctrl freq_ctrl_inst(

		.clk					(clk),
		.rst_n				(rst_n),

		.wave_freq			(wave_freq),

		.f_word				(f_word)
	);

	addr_ctrl addr_ctrl_inst(

		.clk					(clk),
		.rst_n				(rst_n),
		
		.wave_sel			(wave_sel),
		.f_word				(f_word),

		.addr					(addr_rom1024x8)
	);

	rom1024x8	rom1024x8_inst (
	
		.address					(addr_rom1024x8),
		.clock					(clk),
		.q							(wave)
	);

	a_ctrl a_ctrl_inst(

		.clk					(clk),
		.rst_n				(rst_n),

		.wave_a				(wave_a),
		.wave					(wave),

		.d_wave				(d_wave)
	);

endmodule 

(4)蜂鸣器模块

此模块需要明确的是给什么样的信号蜂鸣器才能发声,本设计使用的蜂鸣器是无源蜂鸣器,给连续高低变化的信号就能发生,当然信号频率有一个范围。需要注意的是按键脉冲信号到来,100ms计数器的启动控制信号的条件是什么,代码如下:
module beep(

input		wire					clk,
input		wire					rst_n,

input		wire					flag_s,
input		wire					flag_a,
input		wire					flag_fa,
input		wire					flag_fs,

output	reg					beep

);

parameter		T		= 10_000;
parameter		MAX	= 5_000_000;

reg		[15:0]		cnt_10k;
reg						wave_10k;
reg		[22:0]		cnt_100ms;

wire						wave_100ms;
wire						flag_or;

assign flag_or = (flag_s == 1 || flag_a == 1 || flag_fa == 1 || flag_fs == 1) ? 1'b1 : 1'b0;

//=================== generate 10k wave ===========================================================

always @ (posedge clk, negedge rst_n)
	begin
		if (!rst_n)
			cnt_10k <= 0;
		else
			if (cnt_10k < T - 1)
				cnt_10k <= cnt_10k + 1'b1;
			else
				cnt_10k <= 0;
	end

always @ (posedge clk, negedge rst_n)
	begin
		if (!rst_n)
			wave_10k <= 0;
		else
			if (cnt_10k < T / 2)
				wave_10k <= 1'b1;
			else
				wave_10k <= 1'b0;
	end

//================== generate wave_100ms ==========================================================

always @ (posedge clk, negedge rst_n)
	begin
		if (!rst_n)
			cnt_100ms <= MAX - 1;
		else
			if ((cnt_100ms == MAX - 1) && flag_or == 1)
				cnt_100ms <= 0;
			else
				if (cnt_100ms < MAX - 1)
					cnt_100ms <= cnt_100ms + 1'b1;
				else
					cnt_100ms <= cnt_100ms;
	end
	
assign wave_100ms = (cnt_100ms != MAX - 1) ? 1'b1 : 1'b0;					

//================= generate beep ===============================================================

always @ (posedge clk, negedge rst_n)
	begin
		if (!rst_n)
			beep <= 1;
		else
			beep <= ~ (wave_10k && wave_100ms);
	end

endmodule

(5)6个动态数码管显示模块

前两个数码管分别显示波形选择和幅度,后四个显示频率值。特别需要注意的是波形选择和幅度信号的位宽,以及频率对应需要bin2bcd模块。代码如下:

module show_data(

	input		wire					clk,
	input		wire					rst_n,
	
	input		wire	[1:0]			wave_sel,
	input		wire	[1:0]			wave_a,
	input		wire	[19:0]		wave_freq,
	
	output	wire	[2:0]			sel,
	output	wire	[7:0]			seg
);

	wire		[3:0]			b_wave_sel;
	wire		[3:0]			b_wave_a;
	wire		[27:0]		bcd;
	wire		[23:0]		show_data;
	
	assign b_wave_sel = wave_sel + 1'b1;
	assign b_wave_a = wave_a + 1'b1;	
	assign show_data = {b_wave_sel, b_wave_a, bcd[23:8]};
	
	bin2bcd # (.WIDTH_BIN(20), .WIDTH_BCD(28)) bin2bcd_inst(

		.bin					(wave_freq),

		.bcd					(bcd)
	);

	seven_tube_drive seven_tube_drive_inst(

		.clk					(clk),
		.rst_n				(rst_n),
		
		.show_data			(show_data),
		
		.seven_tube_seg	(seg),
		.seven_tube_sel	(sel)
	);
	
endmodule 

3、测试文件tb

这个工程不建议对所有模块进行仿真,因为子模块太多,提供一个简单的,不足之处请谅解。代码如下:

`timescale 1ns/1ps

module dds_tb;

	reg					clk;
	reg					rst_n;

	reg					key_sel;
	reg					key_amp;
	reg					key_fadd;
	reg					key_fsub;

	wire					beep;
	wire	[9:0]			d_wave;
	wire	[2:0]			sel;
	wire	[7:0]			seg;
	
	wire		[7:0]		addr = dds_inst.d_dds_inst.addr_ctrl_inst.p_addr;

	defparam		dds_inst.key_ctrl_inst.key_filter_inst1.T_5ms = 10;
	defparam		dds_inst.key_ctrl_inst.key_filter_inst2.T_5ms = 10;
	defparam		dds_inst.key_ctrl_inst.key_filter_inst3.T_5ms = 10;	
	defparam		dds_inst.key_ctrl_inst.key_filter_inst4.T_5ms = 10;

	defparam		dds_inst.beep_inst.T = 10;
	defparam		dds_inst.beep_inst.MAX = 50;		
	
	dds dds_inst(

		.clk						(clk			),
		.rst_n					(rst_n		),
		                      
		.key_sel					(key_sel		),
		.key_amp					(key_amp		),
		.key_fadd				(key_fadd	),
		.key_fsub				(key_fsub	),
                            
		.beep						(beep			),
		.d_wave					(d_wave		),
		.sel						(sel			),
		.seg						(seg			)
	);

	initial clk = 1;
	always #10 clk = ~ clk;
	
	initial 
		begin
			rst_n = 0;
			key_sel = 1;
			key_amp = 1;
			key_fadd = 1;
			key_fsub = 1;
			# 201
			
			rst_n = 1;
			task_stop;
		
			key_sel = 0;
			task_stop;
			
			key_amp = 0;
			task_stop;
			
			key_fadd = 0;			
			task_stop;
						
			key_fsub = 0;
			task_stop;
			
			$stop;
		end
	
	task task_stop;
		begin
			repeat (10)
				begin
					while (addr != 255) @ (posedge clk);
					while (addr != 0)	@ (posedge clk);
				end
		end
	endtask			
	
endmodule 

三、个人总结

本人做得还较为顺利,希望给初学者提供一个参考,详细代码见附件。

  • 9
    点赞
  • 82
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值