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


前言

本文使用Altera公司生产的EP4CE22F17C6开发板和小梅哥的高速DAC,——ACM9767,基于Quartus18.1实现对DDS信号发生器的设计。

一、DDS是什么?

DDS同 DSP(数字信号处理)一样,是一项关键的数字化技术。DS芯片中主要包括频率控制寄存器、高速相位累加器和正弦计算器三个部分(如Q2220)。DDS是直接数字式频率合成器(Direct Digital Synthesizer)的英文缩写。与传统的频率合成器相比,DDS具有低成本、低功耗、高分辨率和快速转换时间等优点,广泛使用在电信与电子仪器领域,是实现设备全数字化的一个关键技术。

二、设计步骤

1.DDS的基本结构

在这里插入图片描述
DDS的结构主要由相位累加器,相位调制器,波形数据表ROM,DAC组成。相位累加器和相位调制器的存在都是为了生成ROM的地址,与后续ROM的导入结合。

1.相位累加器

相位累加器为计数器对频率字输入的累加,其中加法器与寄存器均为N位(N一般取24~32位),在每个时钟的上升沿将频率控制字与寄存器中数据进行相加,相加结果再与下一个时钟上升沿的频率字相加,如此重复。频率控制字Fword与N决定了DDS输出的步频(频率),f out=Fword*f clk/2^N。

2.相位调制器

若ROM地址为M位,相位调制器则为取出相位累加器的高M位与相位控制字相加。Verilog里难以实现对于小数的除法,故对于缩小步频的操作可以使用结位的方法。Pword用于调制DDS输出相位。

3.ROM数据表的生成

ROM是只读储存器的简称,在ROM中一个地址对应一个输出值,基于相位累加器和相位调制器生成的地址,在ROM数据表内存入所输出波形的输出值。本文使用Quartus自带的IP核生成ROM,其中使用的mif文件使用Mif_maker生成。Mif_maker文件下载
在Mif_maker里可以设置所需波形类型,地址宽度,输出深度,生成mif文件后,在ROM中导入,在后续模块中例化使用。

2.对各结构的代码实现

1.DDS顶层代码`

采用了4个按键实现固定8个频率控制字和8个相位控制字的输入。

module DDS_top(
clk,
clkA,
clkB,
WRTA,
WRTB,
rst_n,
FwordA_sel,
FwordB_sel,
PwordA_sel,
PwordB_sel,
model_selA,
model_selB,
dataA,
dataB

	);
input clk;
output clkA;
output clkB;
output WRTA;
output WRTB;
input rst_n;
input [1:0]model_selA;
input [1:0]model_selB;
output [13:0]dataA;
output [13:0]dataB;
input FwordA_sel;
input FwordB_sel;
input PwordA_sel;
input PwordB_sel;

reg [31:0]FwordA;
reg [31:0]FwordB;
reg [11:0]PwordA;
reg [11:0]PwordB;

assign clkA=clk;
assign clkB=clk;
assign WRTA=clk;
assign WRTB=clk;

reg [2:0]r_FwordA;//使用按键实现8种频率和相位的循环切换
reg [2:0]r_FwordB;
reg [2:0]r_PwordA;
reg [2:0]r_PwordB;


wire [3:0]key_flag;
wire [3:0]key_state;



always@(posedge clk or negedge rst_n)
	if(~rst_n)
		r_FwordA<=0;
	else if(key_flag[0]&&key_state[0]==0)
		r_FwordA<=r_FwordA+1;

always@(posedge clk or negedge rst_n)
	if(~rst_n)
		r_FwordB<=0;
	else if(key_flag[1]&&key_state[1]==0)
		r_FwordB<=r_FwordB+1;

always@(posedge clk or negedge rst_n)
	if(~rst_n)
		r_PwordA<=0;
	else if(key_flag[2]&&key_state[2]==0)
		r_PwordA<=r_PwordA+1;

always@(posedge clk or negedge rst_n)
	if(~rst_n)
		r_PwordB<=0;
	else if(key_flag[3]&&key_state[3]==0)
		r_PwordB<=r_PwordB+1;


always@(*)//85899.34592为1khz计算所得Fword
	case(r_FwordA)
		0:FwordA=85900;//500hz
		1:FwordA=85899;//1khz 
		2:FwordA=858993;//10khz
		3:FwordA=8589935;//100khz
		4:FwordA=85900346;//1Mhz
		5:FwordA=85900346*4;//4Mhz
		6:FwordA=85900346*5;//5Mhz
		7:FwordA=85900346*12;//12Mhz
	endcase

always@(*)
	case(r_FwordB)
		0:FwordB=85900;//500hz
		1:FwordB=85899;//1khz 
		2:FwordB=858993;//10khz
		3:FwordB=8589935;//100khz
		4:FwordB=85900346;//1Mhz
		5:FwordB=85900346*4;//4Mhz
		6:FwordB=85900346*5;//5Mhz
		7:FwordB=85900346*12;//12Mhz
	endcase

always@(*)//总地址为4096
	case(r_PwordA)
		0:PwordA=0;//0度
		1:PwordA=512;//45度
		2:PwordA=1024;//90度
		3:PwordA=1365;//120度,计算所得为1365.333333333333
		4:PwordA=2048;//180度
		5:PwordA=3072;//270度
		6:PwordA=3413;//300度,计算所得为3413.333333333333
		7:PwordA=3641;//320度,计算所得为3640.8888888888889
	endcase

always@(*)
	case(r_PwordB)
		0:PwordB=0;//0度
		1:PwordB=512;//45度
		2:PwordB=1024;//90度
		3:PwordB=1365;//120度
		4:PwordB=2048;//180度
		5:PwordB=3072;//270度
		6:PwordB=3413;//300度
		7:PwordB=3641;//320度
	endcase

DDS_module DDS_moduleA(
	.clk(clkA),
	.rst_n(rst_n),//key_state[2]&key_state[3]调整相位时让两个DDS模块重新加载
	.Fword(FwordA),
	.Pword(PwordA),
   	.data(dataA),
   	.model_sel(model_selA)
	);

DDS_module DDS_moduleB(
	.clk(clkB),
	.rst_n(rst_n),
	.Fword(FwordB),
	.Pword(PwordB),
   	.data(dataB),
   	.model_sel(model_selB)
	);

key_filter inst_key_filter0//对四个按键进行消抖
(
	.clk       (clk),
	.rst_n     (rst_n),
	.key       (FwordA_sel),
	.key_flag  (key_flag[0]),
	.key_state (key_state[0])
);

key_filter inst_key_filter1
(
	.clk       (clk),
	.rst_n     (rst_n),
	.key       (FwordB_sel),
	.key_flag  (key_flag[1]),
	.key_state (key_state[1])
);

key_filter inst_key_filter2
(
	.clk       (clk),
	.rst_n     (rst_n),
	.key       (PwordA_sel),
	.key_flag  (key_flag[2]),
	.key_state (key_state[2])
);

key_filter inst_key_filter3
(
	.clk       (clk),
	.rst_n     (rst_n),
	.key       (PwordB_sel),
	.key_flag  (key_flag[3]),
	.key_state (key_state[3])
);
	

endmodule

2.DDS主要发生模块

用4个拨码开关控制输出正弦波,三角波,方波,锯齿波。

module DDS_module(
	clk,
	rst_n,
	Fword,
	Pword,
   	data,
   	model_sel
	);
input clk,rst_n;
input [31:0]Fword;
input [11:0]Pword;
input [1:0]model_sel;
output reg [13:0]data;

reg [31:0]F_cnt;
reg [11:0]rom_adder;

wire [13:0]sin_data;
wire [13:0]square_data;
wire [13:0]triangular_data;
wire [13:0]sawtooth_data;


always @(posedge clk or negedge rst_n)
	if(!rst_n)
		F_cnt<=0;
	else
		F_cnt<=F_cnt+Fword;

always @(posedge clk or negedge rst_n)
	if(!rst_n)
		rom_adder <=0;
	else
		rom_adder<=Pword+F_cnt[31:20];

always@(*)
	case(model_sel)
	0:data=sin_data;
	1:data=square_data;
	2:data=triangular_data;
	3:data=sawtooth_data;
	endcase




sin_rom sin_rom(
	.address(rom_adder),
	.clock(clk),
	.q(sin_data)
	);

square_rom square_rom(
	.address(rom_adder),
	.clock(clk),
	.q(square_data)
	);

triangular_rom triangular_rom(
	.address(rom_adder),
	.clock(clk),
	.q(triangular_data)
	);

sawtooth_rom sawtooth_rom(
	.address(rom_adder),
	.clock(clk),
	.q(sawtooth_data)
	);

endmodule

3.按键消抖模块

状态机实现按键消抖。

module key_filter (
	input clk,    // Clock
	input rst_n, // Asynchronous reset active low
	input key,
	output reg key_flag,
	output reg key_state
);
reg [1:0]r_key;
wire pose_key;
wire nege_key;

reg [1:0]state;
reg [19:0]cnt;//需要存放20ms的计数单元

always@(posedge clk)
r_key<={r_key[0],key};//实现上升沿或者下降沿的寄存

assign pose_key= (r_key==2'b01);
assign nege_key= (r_key==2'b10);





always @(posedge clk or negedge rst_n) begin 
	if(~rst_n) begin//
		 state<= 0;
		 key_state<=1;
		 key_flag<=0;
		 cnt<=0;
	end 
	else begin//状态1.3属于是计数状态,在跳转其他状态时需要对计数器实现清零
		case(state)
			0:begin 
			key_flag=0;//key_flag触发为一个时钟的脉冲形式
			if(nege_key)
			begin
				state<=1;
			end
			else
				state<=0;
			end
		
			1:
			if((cnt<1000000)&&(pose_key))
			begin
				state<=0;
				cnt<=0;
			end
			else if(cnt>=1000000)
			begin
				state<=2;
				key_flag<=1;
				key_state<=0;
				cnt<=0;
			end
			else begin
				cnt<=cnt+1;
				state<=1;
			end
				
			2:begin
				key_flag=0;
			if(pose_key)
				state<=3;
			else 
				state<=2;
			end

			3:
			if((cnt<1000000)&&(nege_key))
			begin
				state<=2;
				cnt<=0;
			end
			else if(cnt>=1000000)
			begin
				state<=0;
				key_flag=1;
				key_state=1;
				cnt<=0;
			end
			else
			begin
				state<=3;
				cnt<=cnt+1;
			end
			default:begin
				cnt<=0;
				key_flag<=0;
				key_state<=1;
				state<=0;
			end

		 endcase // state
	end
end



endmodule 

3 testbench的编写

`timescale 1 ns/ 1 ns
module DDS_module_tb();
reg clk;
reg rst_n;
reg [31:0]Fword;
reg [11:0]Pword;
reg [1:0]model_sel;
wire [13:0]data;


	DDS_module inst1
		(
			.clk(clk),
			.rst_n(rst_n),
			.Fword(Fword),
			.Pword(Pword),
			.data(data),
			.model_sel(model_sel)
		);

initial clk=1;
always #10 clk=~clk;

initial begin
	rst_n=0;
	Fword=65536;
	Pword=0;
	model_sel=0;
	#201
	rst_n=1;
	#5000000
	model_sel=1;
	#5000000
	model_sel=2;
	#5000000
	model_sel=3;
	#10000000
	$stop;

end




endmodule

4.modelsim仿真波形

在这里插入图片描述

5.整体效果硬件下载结果

1khz时的正弦波
10MHZ时的正弦波
20MHZ时的正弦波,已出现失真现象
100HZ时的方波和三角波
100HZ时的正弦波和锯齿波

总结

DDS是大学学习FPGA以来第一个做的比较完整的模块, 跟着小梅哥的课,总算是有了一些自主能力。前路昭然,你我共进。

  • 4
    点赞
  • 73
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值