文章目录
前言
本文使用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.整体效果硬件下载结果
总结
DDS是大学学习FPGA以来第一个做的比较完整的模块, 跟着小梅哥的课,总算是有了一些自主能力。前路昭然,你我共进。