FPGA实现频率、幅度、相位可调的DDS以及DDS Compiler IP核的使用验证
文章目录
- 一、DDS介绍
- 二、DDS原理
- 2.1 频率计算
- 2.2 相位改变
- 2.3 波形切换
- 三、Matlab生成波形文件
- 四、FPGA实现DDS
- 4.1 Verilog代码
- 4.2 仿真验证
- 4.2.1 改变频率
- 4.2.2 切换波形
- 4.2.3 相位调节
- 4.2.4 幅度调节
- 五、Xilinx DDS Compiler的使用
- 5.1 功能框图
- 5.1.1 相位累加器
- 5.1.2 SIN/COS LUT
- 5.2 端口说明
- 5.3 工作原理
- 5.3.1 光栅化
- 5.3.2 输出频率计算
- 5.3.3 相位增量Δθ的计算
- 5.3.4 累加器位宽计算
- 5.4 配置通道
- 5.4.1 CONFIG 通道 TDATA 结构
- 5.4.2 输入相位通道TDATA结构
- 5.4.3 输出数据通道TDATA结构
- 5.5 IP配置
- 5.6 仿真验证IP输出
- 5.7 仿真验证IP动态改变频率
一、DDS介绍
DDS(Direct Digital Synthesizer)也叫直接数字式频率合成器,用于生成精确的模拟信号波形。它通过数字方式直接合成信号,而不是通过模拟信号生成技术。DDS通常由相位累加器、波形存储器(如ROM或RAM)、DAC和低通滤波器组成,DDS结构图如下所示:
二、DDS原理
如上图所示,DDS输出给DAC的波形来源于ROM表,相位累加器作为ROM表的输入地址,然后读出数据。这个ROM表里面存放的就是完整的波形数据,例如:正弦波、方波、三角波、锯齿波等等。
2.1 频率计算
假设ROM的深度的位宽为N,所以ROM一共有 2 N 2^N 2N个数据。读取ROM的时钟为 f c l k f_{clk} fclk,如果一个时钟周期读地址+1,那么读出一个完整的波形需要的时间为:
t i m e = 2 N ∗ 1 f c l k time = 2^N * \frac{1}{f_{clk}} time=2N∗fclk1
那么读出的波形频率 f o u t f_{out} fout:
f o u t = 1 t i m e = f c l k 2 N f_{out} =\frac{1}{time} = \frac{f_{clk}}{2^N } fout=time1=2Nfclk
如果每一个时钟周期读地址+2,那么读出一个完整的波形需要的时间为:
t i m e = 2 N ∗ 1 f c l k ∗ 1 2 time = 2^N * \frac{1}{f_{clk}}*\frac{1}{2} time=2N∗fclk1∗21
那么读出的波形频率 f o u t f_{out} fout:
f o u t = 1 t i m e = f c l k 2 N ∗ 2 f_{out} =\frac{1}{time} = \frac{f_{clk}}{2^N } * 2 fout=time1=2Nfclk∗2
我们可以看出,只要控制读rom地址的时间,就能得到不同频率的波形,如果一个时钟周期读地址+B,那么我们可以得到 f o u t f_{out} fout:
f o u t = f c l k 2 N ∗ B f_{out} = \frac{f_{clk}}{2^N } * B fout=2Nfclk∗B
这个B就是DDS结构图中的频率控制字,那么对上式进行变形可以得到:
B = f o u t ∗ 2 N f c l k B= \frac{f_{out} * 2^N}{f_{clk}} B=fclkfout∗2N
2.2 相位改变
已知我们整个波形的采样点是 2 N 2^N 2N个,我们改变读rom的起始地址,就能改变读出来的波形相位,假设每次相位变换为1°,则相位控制字p_word:
p w o r d = 2 N 360 p_{word} = \frac{ 2^N}{360 } pword=3602N
假设每次相位变换为N°,则相位控制字p_word:
p w o r d = N ∗ 2 N 360 p_{word} =N*\frac{ 2^N}{360 } pword=N∗3602N
2.3 波形切换
我们通过波形切换信号来选择波形输出的是正弦波还是三角波还是其他波。
三、Matlab生成波形文件
从上一章我们可以知道,实现DDS最关键的就是ROM,所以我们需要提前在rom里存放一个完整的波形文件,我们就用Matlabl来生成四个声正弦波、方波、三角波、锯齿波的coe文件。深度都是2048、数据位宽根据DA芯片的位宽来选择,我们这里生成的数据位宽是14位无符号数据。三角波matlab代码如下:
% 参数设置 bitWidth = 14; % 位宽 depth = 2048; % 深度 maxValue = 2^bitWidth - 1; % 无符号数的最大值 % 生成三角波 t = linspace(0, 1, depth); triangleWave = round(maxValue * (abs(2 * (t - floor(t + 0.5))))); % 将三角波限制在无符号范围内 triangleWave = uint16(triangleWave); % 打开文件以写入 fileID = fopen('triangle_wave.coe', 'w'); % 写入COE文件头 fprintf(fileID, 'memory_initialization_radix=10;\n'); fprintf(fileID, 'memory_initialization_vector=\n'); % 写入数据 for i = 1:depth fprintf(fileID, '%d', triangleWave(i)); if i
其它波形也同样生成。
四、FPGA实现DDS
4.1 Verilog代码
本次实验实现从10Hz 到10Mhz的DDS,频率分别为 10hz、50hz、100hz、500hz、
1khz、5khz、10khz、50khz、100khz、500khz、1mhz、2mhz、3mhz、4mhz、5mhz、10mhz、
我们来算一下各种频率控制字:我们频率控制字的位宽设置为32位(越大精度越高),给dds读取的时钟频率用50M系统倍频到125M,根据上面的频率控制字的公式,我们算出10hz的频率控制字为:
B = f o u t ∗ 2 N f c l k = 10 ∗ 2 32 125 ∗ 1 0 6 = 344 B= \frac{f_{out} * 2^N}{f_{clk}}= \frac{10 * 2^{32}}{125*10^{6}}=344 B=fclkfout∗2N=125∗10610∗232=344
以此类推可以算出其它频率的频率控制字,整个dds代码如下:
module dds ( inputsys_clk , //输入125M时钟 inputrst , //锁相环lock信号 inputwave_change , //波形切换信号 inputpha_change , //相位改变信号 inputfre_change , //频率改变信号 inputran_change , //幅度改变信号 output reg [13:0] wave_data ); /***************function**************/ /***************parameter*************/ /***************port******************/ /***************mechine***************/ /***************reg*******************/ reg [31:0] fre_word ; //32位宽的频率控制字 reg [31:0] fre_add ; //32位宽的相位累加器 reg [3:0] fword_sel ; //频率选择 reg [10:0] p_word ; //11位宽的相位控制字 reg [1:0] wave_sel ; //波形选择 reg [1:0] ran_sel ; //幅度选择 reg [13:0] r_wave_data ; //输出波形数据存储器 /***************wire******************/ wire [13:0] triangular_wave ; wire [13:0] square_wave ; wire [13:0] sin_wave ; wire [13:0] sawtooth_wave ; wire [10:0] addr ; //一共2048个采样点 /***************component*************/ sawtooth_rom u0_sawtooth_rom ( .clka (sys_clk ), .addra(addr ), .douta(sawtooth_wave ) ); sin_rom u0_sin_rom ( .clka (sys_clk ), .addra(addr ), .douta(sin_wave ) ); square_rom u0_square_rom ( .clka (sys_clk ), .addra(addr ), .douta(square_wave ) ); triangular_rom u0_triangular_rom ( .clka (sys_clk ), .addra(addr ), .douta(triangular_wave ) ); /***************assign****************/ assign addr = fre_add[31 : 21] + p_word; /***************always****************/ //每当改变频率信号拉高时,切换频率控制字 always @(posedge sys_clk) begin if(rst == 1'b0) fword_sel