目标
设计一个DDS信号发生器,利用DDS技术合成正弦波和方波,输出信号的频率范围为10Hz~5MHz,最小频率分辨率小于1kHz,使用嵌入式逻辑分析仪Signal Tap实时测试输出波形的离散数据。
原理
虽然正弦波的幅度不是线性的,但是它的相位却是线性增加的,DDS正是利用了这一特点来产生正弦信号。因为一个连续的正弦信号其相位是时间的线性函数,相位对时间的导数为ω,即
θ(t)=2ftπ=ωt
dθ(t)/dt=2fπ=ω
当角频率ω为一定值时,其相位斜率dθ/dt也是一个确定值。此时,正弦波形信号的相位与时间呈线性关系,即Δθ=ω×Δt。根据这一基本关系,利用采样定理,通过查表法就能够产生波形。
如下是产生正弦信号的原理框图。图中CPi为系统基准时钟源,其周期为Ti,在CPi的作用下,地址计数器(从0~(2^n-1)计数)产生数据存储器所需的地址信号。在时钟作用下,周期性地读出正弦波形存储器中的正弦幅度值,经过D/A转换器及低通滤波器就可以合成模拟波形。

设计方案

电路设计
1.相位累加器设计
module addr_cnt(
input CPi,
input [12:0] K,
input RSTn,
output reg [9:0] ROMaddr,
output reg [16:0] Address
);
always @(posedge CPi or negedge RSTn) begin
if (!RSTn) begin
Address <= 17'h00000;
ROMaddr <= 10'h000;
end else begin
Address <= Address + K;
ROMaddr <= Address[16:7];
end
end
endmodule
2.波形存储器设计
(1)方波模块
module squwave (
input CPi,
input RSTn,
input [16:0] Address,
output reg [11:0] Qsquare
);
always @(posedge CPi) begin
if (!RSTn) begin
Qsquare <= 12'h000;
end else begin
Qsquare <= (Address[16]) ? 12'h000 : 12'hFFF;
end
end
endmodule
(2)正弦波形存储器模块
#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.=if的新文件,允许写入数据,对文件名没有特殊要求,但扩展名必须为,mif*/
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++)
{/*周期为1024个点的正弦波*/
v=sin(2*PI*n/DEPTH);
/*将一1~1之间的正弦波的值扩展到0~4095之间*/
temp=(int)((v+1)*4095/2);//v+1将数值平移到0~2之间
/*以十六进制输出地址和数据*/
fprintf(fp,"%x\t:\t%x;\n",n, temp);
fprintf(fp, "END;\n");
}
fclose(fp);
//关闭文件
}
}
编译运行程序生成mif文件

查看mif文件,主要调用了altsyncram_component这个模块

使用Tool里的IP Catalog搜索ROM

配置文件


导入mif文件

勾选生成

3.锁相环倍频电路设计
操作方法和2一致,但不需要导入mif文件,在IP Catalog里搜索ALTDLL,或者自己做一个简易的。
module PLL100M_CP (
input inclk0, // 50MHz输入时钟
output reg c0, // 100MHz输出时钟
output locked // 锁定指示
);
reg [1:0] phase_counter;
reg [7:0] lock_counter;
initial begin
c0 = 1'b0;
phase_counter = 2'b00;
lock_counter = 8'd0;
end
always @(posedge inclk0) begin
phase_counter <= phase_counter + 1;
// 每两个输入时钟周期翻转一次输出
if (phase_counter == 2'b01) begin
c0 <= ~c0;
end
// 锁定检测
if (phase_counter == 2'b11) begin
if (lock_counter < 8'd255) begin
lock_counter <= lock_counter + 1;
end
end
end
assign locked = (lock_counter == 8'd255);
endmodule
4.顶层电路设计
module DDS_top (
input CLOCK_50,
input RSTn,
input [1:0] WaveSel,
input [12:0] K,
output reg [11:0] WaveValue,
output [0:0] LEDG,
output CLOCK_100
);
wire [9:0] ROMaddr;
wire [16:0] Address;
wire [11:0] Qsine, Qsquare;
wire CPi = CLOCK_100;
// PLL模块
PLL100M_CP PLL100M_CP_inst (
.inclk0(CLOCK_50),
.c0(CLOCK_100),
.locked(LEDG[0])
);
// 地址计数器
addr_cnt U0_instance(
.CPi(CPi),
.K(K),
.RSTn(RSTn),
.ROMaddr(ROMaddr),
.Address(Address)
);
// 正弦波ROM
SineROM Sine1024_INST (
.address(ROMaddr),
.clock(CPi),
.q(Qsine)
);
// 方波生成器
squwave U1 (
.CPi(CPi),
.RSTn(RSTn),
.Address(Address),
.Qsquare(Qsquare)
);
// 波形选择器
always @(posedge CPi) begin
if (!RSTn) begin
WaveValue <= 12'h000;
end else begin
case (WaveSel)
2'b01: WaveValue <= Qsine; // 正弦波
2'b10: WaveValue <= Qsquare; // 方波
default: WaveValue <= Qsine; // 默认正弦波
endcase
end
end
endmodule
最后封装成使用KEY,SW可操控的模式
module WaveformGenerator (
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(CLOCK_50),
.RSTn(RSTn),
.WaveSel(WaveSel),
.K(K),
.WaveValue(WaveValue),
.LEDG(LEDG),
.CLOCK_100(CLOCK_100)
);
endmodule
查看生成RTL

查看细节是否出错

Signal Tap采样分析
完成电路设计后,使用Tool->

添加如下数据

配置抽样时钟

配置待测点信号

观察波形SW16为1,SW17为0时,波形为正弦波

当两者相反时,观察波形为方波

总结
坚持学习
4356

被折叠的 条评论
为什么被折叠?



