文章目录
任务要求
1)利用DDS技术合成正弦波和方波;
2)输出信号的频率范围为10Hz~5MHz,最小频率分辨率小于1kHz;
3)使用嵌入式逻辑分析仪SignalTap II实时测试输出波形的离散数据。
一、DDS简介
直接数字频率合成(Direct Digital Frequency Synthesis, 简称DDS或DDFS)是一种应用数字技术产生信号波形的方法,它是由美国学者J. Tierney、C.M. Rader和B.Gold在1971年提出的,他们以数字信号处理理论为基础,从相位概念出发提出了一种新的直接合成所需波形的全数字频率合成方法。
DDS产生正弦信号波形的工作原理
虽然正弦波的幅度不是线性的,但是它的相位却是线性增加的,DDS正是利用了这一特点来产生正弦信号。因为一个连续的正弦信号其相位是时间的线性函数,相位对时间的导数为ω,即:
θ ( t ) = 2 π f t = ω t \theta(t)=2\pi ft=\omega t \quad θ(t)=2πft=ωt
d θ ( t ) d t = 2 π f = ω \frac{d\theta(t)}{dt}=2\pi f=\omega \quad dtdθ(t)=2πf=ω
当角频率ω为一定值时,其相位斜率dθ/dt也是一个确定值。此时,正弦波形信号的相位与时间呈线性关系,即Δθ=ω×Δt。根据这一基本关系,利用采样定理,通过查表法就能够产生波形。
图8.7.1是产生正弦信号的原理框图。图中CP为系统基准时钟源,其周期为T。在CP的作用下,地址计数器(从0~(2^N-1)计数)产生数据存储器所需的地地信号。在时钟作用下,周期性地读出正弦波形存储器中的正弦幅值,经过D/A转换器及低通滤波器就可以合成模拟波形。
二、设计过程
1、相位累加器的设计
bash
module addr_cnt(CPi,K,ROMaddr,Address);
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
输入上述代码,选择主菜单中的File→Create/Update→Create Symbol Files for Current File命令,生成该模块的符号,打开其addr_cnt.bsf文件即可查看,如图所示。
2、波形存储器设计
- 方波模块
bash
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
模型符号如图所示。
- 正弦波形存储器模块
C语言程序
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;
/*建立文件名为Sine1024.aif的新文件,先写入数据,对文件名没有特殊要求,但扩展名必须为.aif*/
fp=fopen("Sine1024.aif", "w+");
if(NULL==fp)
printf("Can not creat file\n");
else
{
printf("File created successfully!\n");
/*生成文件头,注意不要忘了"\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, BEGIN\n");
/*以下六行是刷新输出地址和数据指令*/
for(n=0; n<DEPTH; n++)
{
v=sin(2*PI*n/DEPTH); /*sin函数在-1~+1之间的正弦波的值被扩展到0~4095之间*/
temp=(int)(v+1)*2048/2; /*+1将数值平移到0~2之间,*以十六进制输出地址和数据*/
fprintf(fp, %x\t%x\n", n, temp);
}
fprintf(fp, END\n");
}
fclose(fp); //关闭文件
}
以文件名Sine1024.c保存文件,接着进行编译,产生Sine1024.exe文件,双击运行Sine1024.exe,生成Sine1024.mif文件。
接着,验证生成的数据是否正确。用记事本打开生成的.mif文件,同时用Quartus Prime软件打开mif文件,若能成功导入数据且数据一致,则说明生成文件正确。
3、锁相环倍频电路设计
在左侧的IP Catalog搜索ALTPLL(嵌入式锁相环),定制一个名为PLL100M_CP的始终模块。

该模块的输入inclko为50MHz时钟信号,输出c0为100MHz的脉冲信号,占空比为50%,带有相位锁定指示输出端locked。
除此之外其他地方不用改,一直next,最后finish。
4、顶层电路设计
bash
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;
PLL100M_CP PLL100M_CP_inst(
.inclk0(CLOCK_50),
.c0(CLOCK_100),
.locked(LEDG[0])
);
addr_cnt U0_instance(CPi,K,ROMaddr,Address);
SineROM 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
三、设计实现
将该模块设置为顶层模块,全编译后只需配置三个引脚。
SW[16]------PIN_Y24
SW[17]------PIN_Y23
CLOCK_50------PIN_Y2
bash
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 II 实时测试输出波形的离散数据。选择 Tools→SignalTap II Logic Analyzer 命令,选择锁相环输出的 100MHz 信号(GPIO_0[12]) 作为采样时钟,添加待测节点信号,并设置 ROM 地址为 0 时触发采样。然后保存设置,并将该模块添加到当前工程项目中。

四、运行结果

正弦波
SW[16]拨为1,SW[17]拨为0
方波
SW[16]拨为0,SW[17]拨为1
总结
因为初次接触到DDS,所以操作过程中遇到了很多问题,比如逻辑分析仪 SignalTap II 的使用,所以以后会花一些时间去进一步了解。
参考资料
https://blog.csdn.net/weixin_72935906/article/details/147195590
https://blog.csdn.net/changtianFirst/article/details/147097698