DDS信号发生器设计
DDS信号发生器即直接数字频率合成(Direct Digital Frequency Synthesis,简称DDS)是一种利用数字技术生成信号的方法。它通过数字信号处理技术,将数字信号转换为模拟信号,从而生成高质量的正弦波、方波、三角波等信号。
DDS函数信号发生器设计
DDS(Direct Digital Synthesis)信号发生器是一种广泛使用的数字信号处理技术,它能够生成精确的波形,如正弦波、余弦波、方波等。DDS算法利用相位的概念和数字滤波器来合成所需的波形。
DDS算法原理DDS算法基于这样一个事实:任何周期信号都可以表示为一系列正弦波(或余弦波)的加权和。DDS算法通过将信号分解为多个正弦波的加权和,然后通过数字滤波器合成这些波形来生成所需的信号。
DDS算法步骤确定基波形: 选择一个基波形,通常是正弦波或余弦波。
相位计算:计算每个基波形的相位系数。
滤波器设计: 设计一个数字滤波器,用于合成DDS输出。
波形合成: 通过滤波器将所有基波形加权和,生成最终的波形。
DDS产生波形原理
下面以正弦信号产生为例说明dds的工作原理。
虽然正弦波的幅度不是线性的,但是它的相位却是线性增加的,DD正是利用了这一特点来产生正弦信号。因为一个连续的正弦信号其相位是相对时间的线性函数,相位对时间的导数为欧米伽,即
当角频率欧米伽为一定值时,其相位斜率也是一个确定的值。此时正弦信号的相位与时间呈线性关系,根据这一基本关系利用采样定理通过查表法就能产生波形。
设计需求
1)利用DDS技术合成正弦波和方波;
2)输出信号的频率范围为10Hz~5MHz,最小频率分辨率小于1kHz。
设计分析
在FPGA中,常用如图所示框图实现DDS。其中,m为地址加法器的数据宽度,其大小取决于基准时钟频率和所需步进精度。ROMaddr为n位存储器地址,n取决于波形样本个数。r为存储器输出数据的宽度。
地址计数器也称为相位累加器,由地址加法器和地址寄存器组成。加法器有两个数据输入端:一个输入端B与地址寄存器的输出相连;另一个输入端为相位增量K,因为K是决定DDS输出频率的参量,所以被称为频率数据或频率控制字,存放K的寄存器称为频率控制寄存器。频率控制字K是一个二进制数据,K+B(B的初值为0)就是相位累加器的输出Addr,相位控制字P用来调节相对于相位累加器输出相位的一个固定增量,对于一路信号来说没有任何意义,不过对于多路信号来说,可以通过相位控制字来调节多路信号的相位差。
存储器地址ROMaddr截取的是PAddr的高n位,其变化速度取决于K的大小,从而实现了跳跃读数。当相位累加器为最大值时,再来一个时钟脉冲,其输出地址数据出现溢出,自动地从0开始重复先前的过程,这样就可以实现波形连续的输出。因此可以通过K值来改变输出频率。
实现过程
1.相位累加器的设计
相位累加器在时钟的驱动下,将输入的频率控制字转换 为地址并输出,它决定着频率的范围和分辨率。本设计不使用相位调制器,相位累加器采用m=17的二进制累加器和寄存器构成,其结果直接送到后面的存储器。代码如下:
input CPi;
input [12:0] K;
output reg [9:0] ROMaddr;
output reg [16:0] Address;
always @(posedge CPi)
begin
Address =Address +K;
ROMaddr =Address[16:7];
end
endmodule
2.波形存储器ROM的设计
(1)方波模块
由于方波的实现算法相对简单,可以不用ROM表,直接用寄存器来保存方波的输出值。方波只有高低电平两种状态,因此只需要在一个周期的中间位置翻转电平即可。其实现原理如下:由于相位累加器的值是现行累加的,因此地址值也是线性累加的,对地址值Address进行判断,当地址值的最高位为0时,便将存储波形幅值的存储器的每一位赋值为1,否则赋值为0。具体源程序如下:
module squwave(CPi,RSTn,Address,Qsquare);
input CPi;
input RSTn;
input [16:0] Address;
output reg [11:0] Qsquare;
always @(posedge CPi)
if(!RSTn) Qsquare=12'h000;
else begin
if(Address<=17'h0FFFF)
Qsquare=12'hFFF;
else Qsquare=12'h000;
end
endmodule
(2)正弦波形存储器模块
首先编译一个C程序( Dev C++ 或者别的编译器都行),编译之前建议先在对应的Quartus工程目录下新建一个文件夹,将C程序保存在该文件夹内:
/myMIF.c/
#include <stdio.h>
#include <math.h>
#define PI 3.141592
#define DEPTH 1024
#define WIDTH 12
int main(void)
{
int n,temp;
float v;
FILE *fp;
fp=fopen("Sine1024.mif","w+");
if(NULL==fp)printf("Can not creat file!\r\n");
else
{
printf("File created successfully!\n");
fprintf(fp,"DEPTH=%d;\n",DEPTH);
fprintf(fp,"WIDTH=%d;\n",WIDTH);
fprintf(fp,"ADDRESS_RADIX=HEX;\n");
fprintf(fp,"DATA_RADIX=HEX;\n");
fprintf(fp,"CONTENT\n");
fprintf(fp,"BEGIN\n");
for(n=0;n<DEPTH;n++)
{
v=sin(2*PI*n/DEPTH);
temp=(int)((v+1)*4095/2);
fprintf(fp,"%04x : %03x;\n",n,temp);
}
fprintf(fp,"END;\n");
fclose(fp);
}
}
编译运行以上代码,会生成 myMIF.exe文件以及Sine 1024.mif 文件,如下图:
使用Quartus调用LPM_ROM定制正弦波形存储器:点击Tools—>IP Catalog,并在IP Catalog搜索框搜索 ROM ,双击 找到 ROM 1-PORT并双击
在弹出的弹框里面修改命名,如下图所示:
点击OK,会弹出如下界面:
修改以下参数:
取消 ‘q’output port的勾选
点击 Browse…,并选中之前生成好的 Sine1024.mif
之后一直Next,直到 Finish,ROM配置完成
(3)锁相环倍频电路
在IP Catalog 搜索栏中搜索 ALTPLL,选中 ALTPLL 并双击
修改如图所示的参数
之后一直 Next 默认选项,直到出现以下界面,修改Clock multi…的参数
之后也是一直默认选项,直到 Finish
顶层电路设计
module DDS_top (CLOCK_50,RSTn,WaveSel,K,
WaveValue,LEDG,CLOCK_100);
input CLOCK_50;
input RSTn;
input [1:0] WaveSel;
input [12:0] K;
output reg [11:0] WaveValue;
wire [9:0] ROMaddr/* synthesis keep */;
wire [16:0] Address;
wire [11:0] Qsine,Qsquare;
output [0:0] LEDG;
output CLOCK_100;
wire CPi=CLOCK_100;
myALTPLL PLL100M_CP_inst(
.inclk0(CLOCK_50),
.c0(CLOCK_100),
.locked(LEDG[0])
);
addr_cnt U0_instance(CPi,K,ROMaddr,Address);
myROM ROM_inst(
.address(ROMaddr),
.clock(CPi),
.q(Qsine)
);
squwave U1(CPi,RSTn,Address,Qsquare);
always @(posedge CPi)
begin
case(WaveSel)
2'b01:WaveValue=Qsine;
2'b10:WaveValue=Qsquare;
default:WaveValue=Qsine;
endcase
end
endmodule
DE2-115设计实现
配置引脚
可以直接导入DE2_115_pin_assignments.csv文件进行配置,也可以手动进行配置
module DE2_115_DDS_top(CLOCK_50,KEY,SW,GPIO_0,LEDG);
input CLOCK_50;
input [3:3] KEY;
input [17:0] SW;
output [12:0] GPIO_0;
output [0:0] LEDG;
wire CLOCK_100;
assign GPIO_0[12]=CLOCK_100;
wire RSTn=KEY[3];
wire [1:0] WaveSel=SW[17:16];
wire [12:0] K=SW[12:0];
wire [11:0] WaveValue;
assign GPIO_0[11:0]=WaveValue;
DDS_top DE2(CLOCK_50,RSTn,WaveSel,K,WaveValue,LEDG,CLOCK_100);
endmodule
引脚配置完成要再次编译运行
接下来就可以使用SignalTap ll实时测试输出波形的离散数据,选择Tools–>SignalTap ll Logic Analyzer
右键 Setup 上方空白处,点击 Add Node… 进行添加
全部确认无误后,保存文件并编译(要连接开发板)
波形图如下: