前言:
DDS:直接数字频率合成,正弦波0-2pi周期内,相位到幅度是一一对应的(这里我们使用放大后的整数幅度)。
主要思路:
个人理解,FPGA不擅长直接做数字信号计算,那样太占用片上逻辑资源,所以需要事先建立 正弦波相位-幅度 表,然后在时钟下,通过相位累加并用相位作为地址索引来查询正弦波信号表。
正弦波相位-幅度 表:
存储的是量化的正弦波在一个周期的幅度信息(幅度的地址即相位)。
幅度的地址数目决定了相位量化的误差。
而存储每一个幅度的比特数决定了幅度的量化误差。
可以通过matlab以及Xilinx的IP核向导创建。
Verilog编写的DDS模块主要由三部分组成,
相位累加器,用于决定输出信号频率的范围和精度;
正弦函数模块,用于存储经量化和离散后的正弦函数的幅值;
查表模块,对相位累加器的输出地址查表。
两种方法可以改变输出信号的频率:
改变查表寻址的时钟频率,从而改变输出波形的频率。
改变寻址的步长来改变输出信号的频率。
步长即为相位增量。
由累加器对相位增量进行累加,
累加器的值作为查表地址。
相位累加器是 DDS 的核心所在,前面在低于时钟频率的任意频率生成相位累加器)中我们已经进行了叙述。
正弦函数模块包含一个周期正弦波的数字幅度信息,每个地址对应正弦波中0-2pi范围的一个相位点。查表模块把输入的地址相位信息映射成正弦波幅度的数字量信号。相位寄存器每经过 2^N/K 个时钟后回到初始状态,相应地正弦查询表经过一个循环回到初始位置,输出一个正弦波。
输出正弦波周期为fo=fc* K/2^N ,最小分辨率为f=fc/2^N。通过fc和K控制正弦波频率精度) 其中,N 为累加器位宽,K 为步长,fc 为时钟频率。计数模(最大值):M=2^N。
一般正弦波表幅度地址位宽与累加的查表地址位宽不同,按前者位宽取后者对应高位的位宽即可。(具体见实例)
先用matlab生成1024点的正弦波数据:
clc;clear;
N = 10; %储存单元地址线
depth=2^N; %存储单元;
widths=N; %数据宽度为8位;
index = linspace0,pi*2,depth);
sin_value = sinindex);
sin_value = sin_value * depth/2 -1); %扩大正弦幅度值
sin_value = fixsin_value)+0.5);
plotsin_value);
number = [0:depth];
fid=fopen'sin_table.coe','w+');
fprintffid,'memory_initialization_radix=10;
');
fprintffid,'memory_initialization_vector=
');
for i = 1 : depth - 1
fprintffid, '%d,
', sin_valuei));
end
fprintffid, '%d;', sin_valuedepth));
fclosefid);
Verilog程序
1、adder.v文件,相位累加模块
`timescale 1ns/1ps
/***************************************
晶振频率 fc = 100MHz
输出频率 fo = 1kHz(根据需要可以设为任意值)
控制参数 K = fo*2^N)/fc = 42950
参数 N = 2^32,32为计数器的位宽)
****************************************/
module PHASE_ADDER
input clk,
input rst,
output reg [31:0] cnt,
output reg clk_out
);
always @posedge clk or posedge rst)
ifrst)
cnt <= 0;
else
cnt <= cnt + 32'd42950; //计数器步长K
always @posedge clk or posedge rst)
ifrst)
clk_out <= 1'b0;
else ifcnt < 32'h7FFF_FFFF)
clk_out <= 1'b0;
else
clk_out <= 1'b1;
endmodule
2、dds_top.v顶层设计
`timescale 10ns /1ns
module dds_top
input rst,
input clk,
output signed [15:0] sine_o
);
wire [31:0] phase; //32bit内部连接线,传递相位增量
wire clk_out;
wire [9:0] addr; //10bit相位信息
PHASE_ADDER U_PHASE_ADDER
.clk clk ),
.rst rst ),
.cnt phase ),
.clk_outclk_out)
);
assign addr = phase[31:22];//addr 10bit
DDS_Table U_DDS_Table
.clkaclk), // input wire clka
.addraaddr), // input wire [9 : 0] addra
.doutasine_o) // output wire [15 : 0] douta
);
endmodule
3、仿真测试文件
`timescale 1ns/1ps
module TB;
reg clk;
reg rst;
wire clk_out;
dds_top U_dds_top
.clk clk ),
.rst rst )
);
initial begin
clk = 0;
rst = 0;
#4 rst = 1;
#3 rst = 0;
end
always #5 clk = ~clk;
endmodule