一、Direct Digital Synthesizer实现
DDS利用数字累计相位,用相位获得rom地址查询波表,得以借助查询rom地址对应模拟值输出DA,来显示波形。一般作为信号发生器来发生各种波形,三角波,方波,正弦波。
二、DDS结构代码
N:用于指波形字长,或者波形生成精度;
M:频率控制字,步长,当时钟翻转一次前进多少长度;
phase:相位控制,相位改变值;
freq:生成波形频率;
fs:采样频率;
三、代码思路
N为频率精度,我们希望将待生成波形分为2^32份小段以便于采样处理。当然实际没有分成这么多份,想想2M频率分为2^32份,要求每一份都采样一次时钟频率要求会很高。实际上,我们需要的是时钟翻转一次,即实际采样一次,相当于采集了待生成波形多少份,即M,(步长或者说频率控制字)上面说了,一个波形分为2^32份,当我采集了2^32份就说明波形一个周期生成出来了 o(>_<)o。
理想中freq=fs/2^32,即一个周期采样2^32次,实际上fs是固定的,不可能生成一个频率的波形就去改采样频率fs,freq是我要生成的频率,fs采样频率都改不了,2^32次方是我想要的精度,也不可能一直改动,于是引进了M。
实际上freq=M*fs/2^32,还是希望与一个周期采样2^32次,但是我实际采样一次相当于理想中采样了M次,相当于采样频率不足以采样2^32次为了保持生成波形的频率,中间的采样点数因为我频率不够损失掉了。就像你和同学操场跑圈,你速度没他快,为了追上他,用了点抄小道的小手段,你自然没有他跑的远,路程不同但你们位移一样。M就相当于偷懒的距离。从动一下算一步到动一下算多步。
于是当我们知道了freq,fs已知,N设定好了,M自然也知道了。
实际我们生成波形传入M就足够了,不用再根据freq计算了。
四、代码
module ware(
F_word , //频率控制字
clk ,
da_out
);
/*F_word参考值
*26349 对应163M时钟的1k频率 M=1K/(fs/2^32)
*16393 对应1k 262M
*/
input clk ;
input wire [31:0] F_word ; //相当于记录时钟翻转一次对应波形对应的位置
output wire [11:0] da_out ;
wire [11:0] rom_out ; //对应rom表存储的da值输出波形
//内部参数
reg [31:0]counts ; //计数位,用于波形产生,记到M触发
reg [31:0]fword ;
always @(posedge clk)
begin
fword<=F_word;
end
always @(posedge clk)
counts <= counts+fword ; //加频率控制字,对应一个时钟频率应该累加多少份 一个波形的周期被分为2^32份,加到2^32的时候一个周期认为一个周期输出完
assign rom_out=counts[31:20] ; //rom_out输入设为12位,对应着映射rom波表的地址,则只需要前12位 用八位就是 31:24
//rom核使用
rom rom(
.address(rom_out) ,
.clock(clk) ,
.q(da_out)
);
endmodule
4.2 rom波形文件
打开Guagle
查看-全局参数
位宽和长度根据需求
设定波形——选择我们要输出的波形即可
保存在fpga工程目录下
4.3
rom核使用
选择上面生成的波形文件
ok
五、比赛使用感悟
一开始使用的stm32进行采样输出,发现有明显的漂移。后面发现问题,信号发生器产生波形频率测不准。由32采集fft处理,由于和信号发生器不是一个时钟,可能信号发生器产生30khz的波形在32时钟频率下采样是29.99khz输出也要29.99khz,所以要求ad采样和da输出要是一个晶振下完成(32+fpga方案取消),且ad采样精度足够,不然采样不出实际频率,一开始选择的fft点数间隔5k,能采集频率但是采集不到更精准的差别导致漂移。32 dds输出精度够但是ad采样精度不够,刚好fpga有个高速ad,于是采样高速ad,性能大大溢出还可以滤个波完美。再进行输出波形无漂移且稳定。dds控制相位也是相当方便,完美,且不需要锁相。相位控制加在count上就行,微改即可。