首先分享两篇有关dds信号产生的文章。
一、
来源:http://www.digilent.com.cn/community/241.html
在开始DDS这个实验之前需要先解释一下DDS原理,摘了一段网上大神的解释:
基本原理框图:
两个关键术语:
a. 相位累加器:Phase = Phase + freq_ctrl,可以暂且理解为i = i + 1一样的东西。
b. 频率控制字:freq_ctrl,这个东西的值直接影响输出信号的频率。
假设系统工作时钟(查表时钟)为150MHz,ROM表深度为4096,存储波形为1个周期(如正弦波每周期抽样量化为4096个点),也就是一个周期的波形由4096个采样点组成,意味着输出波形一个周期最多4096个采样点。比如Data输出10M的正弦波,输出的正弦波每周期只有15个采样点;而输出1M的正弦波,每周期将有150个采样点;我们也可以知道当输出频率小于等于36.621KHz时,输出波形每周期由4096个点构成。输出信号的每周期点越多,阶梯效过越不明显,经过低通滤波器后波形越好看。
如果freq_ctrl为1时,那么输出信号为150MHz/4096=36.621KHz,如果freq_ctrl为2时,那么输出信号为150MHz*2/4096=73.242KHz。因此当需要输出正弦波频率为fout MHz时,
Fout = 150MHz*freq_ctrl/4096,所以freq_ctrl = Fout*4096/150MHz。
如果上面的大家都理解了,那么恭喜你已经完全理解了DDS的核心部分。至于其他DDS相关的内如,比如频率分辨率(因为rom地址必须是整数,所以freq_ctrl必须是整数,所以上例的频率分辨率为),旁瓣抑制比(量化多1bit,多6db = 20lg2)……
本次实验采用官方DDS IP核的形式进行,不同与前三个实验的地方是本次需要进行板上调试,开发过程当中这个也是除了功能仿真很常用的一种调试手段。
功能简介:使用板上一个按键来控制不同输出频率间的切换,按下按键切换输出频率。
1、新建DDS_test工程,找到DDS ip核双击进入设置界面:
注意,采用的时钟时100MHz的输入时钟,通道选择一个通道,操作模式选择标准,另外一种是栅格化方式有兴趣可以试一试。参数选择选择系统参数,这个比硬件参数不同地方就是可以设置信噪比、频率分辨率等参数。
2、实施界面可编程相位增量选择可编程,其他默认即可,之后点击OK:
3、开始新建DDS_test进行代码编写:
复制代码
5、仿真效果图,将输出设置为有符号十进制且信号为模拟信号进行显示,这样就可以看到输出的sin/cos信号为正弦波,注意,由于IP核设置SNR为72,所以有效位为12位,这样的话可以只监控bit0~11即可。由于自己电脑太渣就没能看到后面的波形,卡出翔来快,自己对电脑性能自信的话可以看一下:
6、接下来对引脚进行分配,由于板子上没有DA所以输出引脚就不用了,直接将时钟、复位和按键引脚定义一下就好。
7、进行板级调试前的设置:
8、综合后点击如图set up debug:
9、next,如下图所示添加需要添加的信号,输入信号之后回车即可出现所有匹配的信号,选中需要的添加就行。好友就是时钟域设置,这些具体的用法白的即可,太多不做介绍:
10、设置监控深度和捕获控制,资源多的可以多用点,少的拉倒,完了点击OK即可:
11、编译后生成.bit文件,选中bit和调试ltx文件之后下载就行:
12、出现如图所示的界面,箭头打钩表示可以显示的窗口,自己试:
13、没有设置触发条件,设置输出sine_w、cosine_w为模拟输出,且输出格式为有符号十进制。下图中标识1表示单次监控,标识2表示连续监控,前几次看到波形不是一个完整的正弦波,说明设置监控深度点数不够显示一个完整波形。
总结:本次实验主要介绍vivado下DDSip核的简单实用流程,以及开发过程当中经常使用到的板级调试方法。
按惯例实验程序发上来,有需要的自取。
问一下版主啊,我这开发板为啥没有DDR3的驱动例程啊,按照论坛上的例程Arty - Getting Started with Microblaze Servers建立的工程显示
-----lwIP TCP echo server ------
TCP packets sent to port 6001 will be echoed back
link speed: 1000
DHCP Timeout
Configuring default IP of 192.168.1.10
Board IP: 192.168.1.10
Netmask : 255.255.255.0
Gateway : 192.168.1.1
TCP echo server started @ port 7
不知道相关技术人员能不能给出解答啊!
二、
来源https://www.cnblogs.com/christsong/p/5536995.html
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 = linspace(0,pi*2,depth);
sin_value = sin(index);
sin_value = sin_value * (depth/2 -1); %扩大正弦幅度值
sin_value = fix((sin_value)+0.5);
plot(sin_value);
number = [0:depth];
fid=fopen('sin_table.coe','w+');
fprintf(fid,'memory_initialization_radix=10;\n');
fprintf(fid,'memory_initialization_vector=\n');
for i = 1 : depth - 1
fprintf(fid, '%d,\n', sin_value(i));
end
fprintf(fid, '%d;', sin_value(depth));
fclose(fid);
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)
if(rst)
cnt <= 0;
else
cnt <= cnt + 32'd42950; //计数器步长K
always @(posedge clk or posedge rst)
if(rst)
clk_out <= 1'b0;
else if(cnt < 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_out(clk_out)
);
assign addr = phase[31:22];//addr 10bit
DDS_Table U_DDS_Table(
.clka(clk), // input wire clka
.addra(addr), // input wire [9 : 0] addra
.douta(sine_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
三、几点思考
1.“如果freq_ctrl为1时,那么输出信号为150MHz/4096=36.621KHz,如果freq_ctrl为2时,那么输出信号为150MHz*2/4096=73.242KHz。因此当需要输出正弦波频率为fout MHz时,Fout = 150MHz*freq_ctrl/4096,所以freq_ctrl = Fout*4096/150MHz。” --------文章1
“输出正弦波周期为fo=fc* K/2^N ,最小分辨率为f=fc/2^N。(通过fc和K控制正弦波频率精度) 其中,N 为累加器位宽,K 为步长,fc 为时钟频率。计数模(最大值):M=2^N。” -------文章2
文章1这里面提到的freq_ctrl频率控制字在第二篇文章里应该就是K,同时也是其他文章里的相位增量。
ROM表深度为4096(每周期抽样量化为4096个点)在第二篇文章里面应该就是2^N,就是归一化后能表示的最大相位,2^N还原后就是2pi。
2.“如果freq_ctrl为1时,那么输出信号为150MHz/4096=36.621KHz”从这个表述里其实可以发现,将左右两边式子取倒数,其实就是周期之间的关系。当dds的系统工作时钟确定时,每两次采样的间隔时间则固定了;那么,用我们要产生的信号周期除以每两次采样的间隔时间就能得出一共采样多少点。相位最大值为2^N,用相位总量除以点数就能得出每两个点相隔的相位,即相位增量。由此就可以得出.K(相位增量)=2^N* fo/fc,最终推导出fo=fc* K/2^N。(推倒式子具体看手写图)
3.从fo=fc* K/2^N可以看出,因为K作为相位增量只能是整数,而且不能大于2^N(相位最大值),所以单个DDS产生的信号频率永远是小于fo的。如何突破频率限制,产生大于fo的频率,看下面的手写图。