基于FPGA的双通道DDS信号发生器

硬件:FPGA开发板 ,AD9767双通道DA转换器

软件:ISE,Matlab,Modelsim

最终效果:输出方波,正弦波,三角波以及锯齿波,可以通过按键改变输出波形的频率,频率在1Hz-1MHz可调,输出波形的电压通过旋钮可调

 

一、生成波形数据

        第一步,通过Matlab生成波形数据文件,数据最终存储在FPGA的ROM中,以.coe结尾。这里以生成正弦信号为例,由于AD9767是14位的DA转换芯片,所以生成的数据位宽也是14位。

clear;
clc;

radix=2;      %进制的格式
width=14;     %数据的位宽
depth=1024;   %数据的深度

fid =fopen ('sin.coe','w');
fprintf(fid,'MEMORY_INITIALIZATION_RADIX=%d;\n',radix);
fprintf(fid,'MEMORY_INITIALIZATION_VECTOR=\n');

for i=0:depth-1
    sin_data=floor((sin(2*pi*i/depth)+1)*0.5*(2^width-1));
    data=dec2bin(sin_data,width);    %十进制到二进制的转换
    if (i~=depth-1) 
        fprintf(fid,'%s,\n',data);    
    else
        fprintf(fid,'%s;',data);    %最后一行以分号结尾
    end
end

fclose (fid);

生成的.coe文件需要符合语法规范,将生成的coe文件保存到生成的Rom IP核中。

二、FPGA程序部分设计

        系统的整体框架分为FPGA和AD9767模块两部分,FPGA产生两路数字信号,AD9767模块将数字信号数模转换后经过滤波后输出。

        FPGA部分中,输出信号的频率由频率控制字决定。每经过一个时钟周期,加法器就将频率控制字与相位累加寄存器相加,最后得到的结果作为波形数据表的读取地址。同时相加结果反馈到加法器的输入端,使得每次相加都可以实现读取地址的递增,当累加次数足够多相位累加寄存器溢出,读取地址“归零”,如此循环往复输出正弦波信号。

1.顶层模块AD9767_DDS的设计

        通过按键key1实现频率控制字的控制,按键key2实现波形的选择,led灯用来指示现在输出的波形。

module AD9767_DDS(
	input sclk, 
	input rst_n,
	input  key1,
	input  key2,
	output [1:0]led,
	
	output DACA_CLK,	//通道1时钟信号
	output DACB_CLK,	//通道2时钟信号
	output DACA_WRT,	//通道1使能信号
	output DACB_WRT,	//通道2使能信号
	output [13:0]DAC_DATA1,	//通道1输出数据
	output [13:0]DAC_DATA2	//通道2输出数据
);

	assign D_CLK = sclk;
	
	assign DACA_CLK = D_CLK;
	assign DACB_CLK = D_CLK;
	
	assign DACA_WRT = D_CLK;
	assign DACB_WRT = D_CLK;
	

	wire [31:0]Fword;
	wire [1:0]wave_sel;	//控制输出波形形状
	wire [2:0]Fword_sel;//改变频率控制字

	DDS_Module DDS_Module0(
		.clk(D_CLK),
		.rst_n(rst_n),
		.EN(1'b1),
		.key(key2),
		.Fword(Fword),
		.Pword(10'd0),
		.wave_sel(wave_sel),
		.DA_Clk(),
		.DA_Data(DAC_DATA1)
	);
	
	DDS_Module DDS_Module1(
		.clk(D_CLK),
		.rst_n(rst_n),
		.EN(1'b1),
		.key(key2),
		.Fword(Fword),
		.Pword(10'd512),	//相对于通道1有180度的偏移
		.DA_Clk(),
		.DA_Data(DAC_DATA2)
	);
	
	Fword_Set Fword_Set_inst(
		.clk(sclk),
		.rst_n(rst_n),
		.key(key1),
		.Fword(Fword),
		.Fword_sel(Fword_sel)
	);
	
	assign led = ~wave_sel;
	
endmodule

2.信号产生模块DDS_Module的设计

        累加器是32位的,而数据深度是1024也就是十位的,取累加器的高十位作为读取地址。例化四个ROM模块分别存储不同的波形数据,通过按键按下控制输出的波形。

module DDS_Module(
	input	clk,
	input	rst_n,
	input	EN,
	input	key,
	input	[31:0] 	Fword,/*频率控制字*/
	input 	[9:0] 	Pword,/*相位控制字*/
	output	reg	[1:0]	wave_sel,/*波形选择字*/
	output				DA_Clk,/*DA数据输出时钟*/
	output	reg [13:0] 	DA_Data/*D输出输出A*/
);
	wire 		key_neg, key_out;
	wire	[13:0]	DA_Data1,DA_Data2,DA_Data3,DA_Data4;
	
	key_detect key_detect2(
		.sclk(clk),
		.rst_n(rst_n),
		.key_in(key),
		.key_neg(key_neg),
		.key_out(key_out)
	);

always@(posedge clk or negedge rst_n)
	if(!rst_n)
		wave_sel <= 2'd0;
	else if(key_neg == 1 && !key_out)
		wave_sel <= wave_sel + 1'b1;
	else
		wave_sel <= wave_sel;
		
always@(*)begin
	case(wave_sel)
		0:  DA_Data <= DA_Data1;  //选择正弦波	
        1:  DA_Data <= DA_Data2;  //选择方波
		2:  DA_Data <= DA_Data3;  //选择三角波
		3:  DA_Data <= DA_Data4;  //选择锯齿波
		default: DA_Data <= DA_Data1;
	endcase
end
	
	reg 	[31:0]	Fre_acc;	
	reg 	[9:0]	Rom_Addr;/*rom深度1024*/

/*---------------相位累加器------------------*/	
	always @(posedge clk or negedge rst_n)
	if(!rst_n)
		Fre_acc <= 32'd0;
	else if(!EN)
		Fre_acc <= 32'd0;	
	else 
		Fre_acc <= Fre_acc + Fword;

/*----------生成查找表地址---------------------*/		
	always @(posedge clk or negedge rst_n)
	if(!rst_n)
		Rom_Addr <= 10'd0;
	else if(!EN)
		Rom_Addr <= 10'd0;
	else
		Rom_Addr <= Fre_acc[31:22] + Pword;	

/*----------例化查找表ROM SIN-------*/		
	ddsrom ddsrom_inst(
		.addra(Rom_Addr),
		.clka(clk),
		.douta(DA_Data1)
	);
	
/*----------例化查找表ROM -------*/		
	square square_inst(
		.addra(Rom_Addr),
		.clka(clk),
		.douta(DA_Data2)
	);
	
/*----------例化查找表ROM sawtooth-------*/		
	sawtooth sawtooth_inst(
		.addra(Rom_Addr),
		.clka(clk),
		.douta(DA_Data3)
	);
	
/*----------例化查找表ROM sawtooth_juci-------*/		
	sawtooth_juci sawtooth_juci_inst(
		.addra(Rom_Addr),
		.clka(clk),
		.douta(DA_Data4)
	);

/*----------输出DA时钟----------*/
	assign DA_Clk = (EN)?clk:1'b1;

endmodule

3.频率选择模块Fword_Set的设计

        通过按钮选择频率控制字Fword,信号频率=Fword*2^累加器数据位宽/时钟频率。本来打算加一个扫频的功能,但是效果还没实现。

module Fword_Set(
	input clk,
	input rst_n,
	input key,
	output reg[31:0]Fword,
	output reg[2:0]Fword_sel,
	output reg[19:0]cnt_time
);
	
	parameter HUN_HZ	= 8600,
		  TWOK_HZ	= 171799,
		  ONE_HZ	= 86;
		  
	parameter IDLE		= 2'd0,
		  INCREASE	= 2'd1,
		  PAUSE		= 2'd2;
	
	wire key_neg, key_out;
	reg	[1:0]	state;
	reg	[4:0]	cnt_latency;
	reg		flag;
	wire	[19:0]	divider;
	wire	[19:0]	word;
	assign  word = (Fword>>13)*(Fword>>13);
	
	always @(posedge clk or negedge rst_n) begin
	        if(!rst_n) 
	                cnt_latency <= 'd0;      
	        else if(cnt_latency == 5'd22)
	        	cnt_latency <= 'd0;
	        else
	        	cnt_latency <= cnt_latency + 5'd1;
	end
	
	always @(posedge clk or negedge rst_n) begin
	        if(!rst_n) 
	                flag <= 1'd0;      
	        else if(cnt_time >= divider && cnt_latency == 5'd18)
	        	flag <= 1'd1;
	        else
	        	flag <= 1'd0;
	end
	
	key_detect key_detect1(
		.sclk(clk),
		.rst_n(rst_n),
		.key_in(key),
		.key_neg(key_neg),
		.key_out(key_out)
	);
	
	always @(posedge clk or negedge rst_n) begin
	        if(!rst_n) 
	                cnt_time <= 'd0;      
	        else if((Fword_sel == 3'd0) && (flag == 1'b0))
	        	cnt_time <= cnt_time + 20'd1;
	        else
	        	cnt_time <= 20'd0;
	end
	
	always@(posedge clk or negedge rst_n)
	if(!rst_n)
		Fword_sel <= 3'd0;
	else if(Fword_sel < 3'd5) begin
		if(key_neg == 1 && !key_out)
			Fword_sel <= Fword_sel + 1'b1;
		else
			Fword_sel <= Fword_sel;
		end
	else
		Fword_sel <= 3'd0;
		
	always@(posedge clk)begin
		case(Fword_sel)
			0:  begin
				case(state)
					IDLE:	Fword <= HUN_HZ;
					INCREASE:Fword <= Fword + ONE_HZ;	
					PAUSE:	Fword <= Fword;
					default:Fword <= Fword;
				endcase
			end
			1:  Fword <= 86;	//1Hz 85.89934592 2^32*20/10^9
			2:  Fword <= 16063;	//187kHz
			3:  Fword <= 85999;	//1kHz
			4:  Fword <= 85999346;	//1MHz 
			default:Fword <= Fword;			
		endcase
	end


	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			state <= IDLE;
		else if(Fword_sel == 3'd0 && flag == 1'b1)
			state <= INCREASE;
		else if(Fword_sel == 3'd0 && Fword <= TWOK_HZ)
			state <= PAUSE;
		else 
			state <= IDLE;
	end
	
	div div_inst(//8599*8599*500000/(Fword^2)
		.clk		(clk),
		.dividend       (20'd550000),
		.divisor        (word),
		.quotient       (divider)
	);
	
	
endmodule

三、AD9767模块

        参考了这篇文章,做了以下改进:

1.在调试的过程中,发现原本小的贴片电感在使用过程中发热,不适用于电流大的场景,改成大的绕线电感。

2.原来的小旋扭电位器调节幅值并不方便,改用大旋钮的电位器。

测试视频:b站链接

工程文件:gitee

  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于FPGADDS(Direct Digital Synthesis)信发生器是一种利用数字信号处理技术生成高精度、高稳定性的信号的设备。DDS信号发生器通过数字方式生成信号,可以实现频率、相位和幅度的精确控制,具有较低的相位噪声和频率稳定性。 FPGA作为硬件平台,提供了灵活性和可编程性,可以实现复杂的信号处理算法和实时控制。在基于FPGADDS信号发生器中,通常包含以下几个主要模块: 1. 相位累加器(Phase Accumulator):用于累加相位增量,生成相位信息。 2. 数字控制振荡器(Digital Control Oscillator,DCO):根据相位信息生成数字信号。 3. 数字到模拟转换器(Digital-to-Analog Converter,DAC):将数字信号转换为模拟信号。 4. 滤波器:对DAC输出的模拟信号进行滤波,去除高频噪声。 5. 控制接口:提供外部控制接口,用于设置频率、相位和幅度等参数。 基于FPGADDS信号发生器具有以下优点: 1. 高精度和高稳定性:由于采用数字方式生成信号,可以实现非常精确的频率和相位控制,具有较低的相位噪声和频率稳定性。 2. 灵活性和可编程性:FPGA作为硬件平台,可以根据需求进行灵活的算法设计和功能扩展,适应不同的应用场景。 3. 实时性:基于FPGADDS信号发生器可以实时生成信号,响应速度快。 4. 可靠性:FPGA具有较高的可靠性和抗干扰能力,适用于各种工作环境。 相关问题: 1. 什么是DDS信号发生器? 2. FPGADDS信号发生器中的作用是什么? 3. 基于FPGADDS信号发生器有哪些优点? 4. 如何控制基于FPGADDS信号发生器的频率和相位?

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值