当前位置:   article > 正文

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

dds信号发生器设计


前言

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


一、DDS信号发生器

1.DDS是什么

DDS 是直接数字式频率合成器(Direct Digital Synthesizer)的英文缩写,是一项关键的数字化技术。与传统的频率合成器相比,DDS 具有低成本、低功耗、高分辨率和快速转换时间等优点,广泛使用在电信与电子仪器领域,是实现设备全数字化的一个关键技术。作为设计人员,我们习惯称它为信号发生器,一般用它产生正弦、锯齿、方波等不同波形或不同频率的信号波形,在电子设计和测试中得到广泛应用。
在这里插入图片描述
注:图片摘自《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 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73

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

    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 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    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 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    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 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    5.RTL视图

    在这里插入图片描述

    三、仿真测试模块

    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 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    2.仿真波形图

    在这里插入图片描述
    可以看到,第一部分波形为正弦波,第二部分为方波,第三部分为三角波,第四部分为锯齿波,设计成功。


    总结

    可以通过dds代码中频率字与相位字的改变来实现更多的波形变化。

    声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/运维做开发/article/detail/751238
    推荐阅读
    相关标签
      

    闽ICP备14008679号