​基于Verilog的DDS波形发生器的分析与实现(三角波、正弦波)-FPGA常见问题社区-FPGA CPLD-ChipDebug

​基于Verilog的DDS波形发生器的分析与实现(三角波、正弦波)

基于Verilog的DDS波形发生器的分析与实现(三角波、正弦波)

最近学习了一下关于DDS的相关知识,本篇概要记录一下自己的理解与实现。

DDS信号发生器采用直接数字频率合成(Direct Digital Synthesis,简称DDS)技术,把信号发生器的频率稳定度、准确度提高到与基准频率相同的水平,并且可以在很宽的频率范围内进行精细的频率调节。采用这种方法设计的信号源可工作于调制状态,可对输出电平进行调节,也可输出各种波形。

下图为DDS 的基本结构图

图片[1]-​基于Verilog的DDS波形发生器的分析与实现(三角波、正弦波)-FPGA常见问题社区-FPGA CPLD-ChipDebug

由上图 可以看出,DDS 主要由相位累加器、相位调制器、波形数据表以及 D/A 转换器构成。

相位累加部分控制输出波形频率,相位字输入部分来改变相位,ROM表中存储一个周期波形的幅度值。

 

其中相位累加器由 N 位加法器与 N 位寄存器构成。每来一个时钟,加法器就将频率控制字与累加寄存器输出的相位数据相加,相加的结果又反馈至累加寄存器的数据输入端,以使加法器在下一个时钟脉冲的作用下继续与频率控制字相加。这样,相位累加器在时钟作用下,不断对频率控制字进行线性相位累加。即在每一个时钟脉冲输入时,相位累加器便把频率控制字累加一次。相位累加器输出的数据就是合成信号的相位。相位累加器的溢出频率,就是 DDS 输出的信号频率。(解释:定义一个N位寄存器,一般为32位,如果来一个时钟计一次,那就要计2^32次才满,这样太慢,因此引入频率控制字设为A,以前以1为单位,现在以A为单位计数累加,可以控制计数更新的速度)

 

用相位累加器输出的数据,作为波形存储器的相位采样地址,这样就可以把存储在波形存储器里的波形采样值经查表找出,完成相位到幅度的转换。波形存储器的输出送到 D/A 转换器,由 D/A 转换器将数字信号转换成模拟信号输出。

 

一般32位累加器不会全用来输出作为ROM地址,会根据ROM深度来适当截取高位作为地址,其余位可以作为控制频率。例如现在ROM中存储波形一个周期数据每个数据位宽8位,则数据范围为0-2^8(256),但是要产生一个周期波形需要512个,因为0-256一般是上升期,256-0处于下降期,那么现在ROM深度为512,则地址位宽应为9位,2的9次方=512,则32位累加器只需高9位即可[31:23]寻址,其余位用来控制地址改变的频率。假如现在每来一个时钟地址变一次,那么其余位(即频率控制字A)应设置为32‘h800000即32’b0000_0000_10000000_0000_0000_0000_0000,最高位1其实就是地址的最低位,累加器初始为0,来个时钟沿加一次A,高9位地址变化一次。要想两个时钟变化一次,那么A就是32‘b0000_00000100_0000_0000_0000_0000_0000,两个时钟之后地址的最低位才会变化。这样通过对频率控制字A的设置就可以达到改变地址的变化频率,其实就是输出的频率。不知道这样说看者能否理解。

 

这里相位累加器位数为 N 位(N 的取值范围实际应用中一般为 24~32),相当于把正弦信号在相位上的精度定义为 N 位,所以其分辨率为1 /2𝑁 。若 DDS 的时钟频率为𝐹𝑐𝑙𝑘,频率控制字 fword 为 1,则输出频率为𝐹𝑜𝑢𝑡 = 𝐹𝑐𝑙𝑘/2𝑁 ,这个频率相当于“基频”。若 fword 为 B,则输出频率为𝐹𝑜𝑢𝑡 = 𝐵 × 𝐹𝑐𝑙𝑘/2𝑁 。因此理论上由以上三个参数就可以得出任意的𝑓𝑜输出频率。且可得出频率分辨率由时钟频率和累加器的位数决定。当参考时钟频率越高,累加器位数越高,输出频率分辨率就越高。

代码:

module dds(

clk,

rst,

Fword, //频率控制字A

Pword, //相位控制字

data

);

input clk,rst;

input [31:0] Fword;

input [8:0] Pword;

output [7:0] data; //8位数据

 

reg [31:0] r_Fword;

reg [8:0] r_Pword;

reg [31:0] cnt;

wire [8:0] rom_adder;

 

always@(posedge clk)

begin

r_Fword<=Fword;

r_Pword<=Pword;

end

 

always@(posedge clk or negedge rst)    //累加器部分

begin

if(!rst)

cnt<=32'd0;

else

cnt<=cnt+r_Fword;

end 

 

assign rom_adder=cnt[31:23]+r_Pword;     //ROM地址

 

rom  rom(                              //例化一个ROM ip核

   .address(rom_adder),

   .clock(clk),

   .q(data)

);

 

endmodule

tb:

`timescale 1ns/1ns

module dds_tb;

 

reg clk,rst;

reg [31:0] Fword;

reg [8:0] Pword;

wire [7:0] data;

 

dds u0(

.clk(clk),

.rst(rst),

.Fword(Fword),

.Pword(Pword),

.data(data)

);

 

initial clk=1;

always #10 clk=~clk;

 

initial begin

rst=0;

Fword=32'h800000;

Pword=9'd0;

#101;

rst=1;

#500;

$stop;

end

 

endmodule

图片[2]-​基于Verilog的DDS波形发生器的分析与实现(三角波、正弦波)-FPGA常见问题社区-FPGA CPLD-ChipDebug图片[3]-​基于Verilog的DDS波形发生器的分析与实现(三角波、正弦波)-FPGA常见问题社区-FPGA CPLD-ChipDebug

ROM中值,用来比对,在例化ip是输出加了一个寄存器,因此会延时一拍输出。主时钟50M,下图把data转换为模拟值,两个黄线之间为频率97.65khz,A为32‘h800000=32’d8388608,50_000_000乘以A等于419430400000000,除以2^32等于97656.25khz验证正确。

图片[4]-​基于Verilog的DDS波形发生器的分析与实现(三角波、正弦波)-FPGA常见问题社区-FPGA CPLD-ChipDebug

更改ROM中mif文件,添加三角波,ROM中三角波这里一共有256个数值,0-127,127-0,地址位宽为8位,那么[31:24]作为地址,其余位作为频率控制字。只需改动程序中地址位宽就可以了,A暂时设为32‘h01000000,下图为相位控制字为8‘h0,8’h5;可以看出相位控制字不会改变输出频率.

图片[5]-​基于Verilog的DDS波形发生器的分析与实现(三角波、正弦波)-FPGA常见问题社区-FPGA CPLD-ChipDebug

图片[6]-​基于Verilog的DDS波形发生器的分析与实现(三角波、正弦波)-FPGA常见问题社区-FPGA CPLD-ChipDebug

附加:如果此时知道主时钟50M,想生成一个25M的波形,那么首先根据公式计算出A=2147483648即二进制1000_0000_0000_0000_0000_0000_0000_0000.仿真如下图为方波,因为根据地址变化只取0、127.

图片[7]-​基于Verilog的DDS波形发生器的分析与实现(三角波、正弦波)-FPGA常见问题社区-FPGA CPLD-ChipDebug

总结:本文内容也是在看相关视频后自己更改ROM深度,更换波形仿真后得出,由于叙述水平有限,其中原理可以自行查看其他文章内容了解,关于本文如有不懂之处可以联系我,共同再探讨。同时其中主要思想可以用来作为任意分频设计,后续再做。

文末附上一个mif文件生成器,不然手动输入ROM值太慢了

链接: https://pan.baidu.com/s/1FpaecaNVjBjDoEd4KQUtXg?pwd=tnx7 

请登录后发表评论

    没有回复内容