用DDS合成信号,经过DAC输出,用ADC采集信号。
使用Signal TAP在时域观察信号
使用 Matlab 在频域观察信号
操作过程:
一.生成分频和倍频锁相环
- 创建一个Megafunction。此在tools/MegaWizard Plug- In Manager,默认选第一项,点Next。
(2)创建PLL。注意以下四点:
1.左侧选择元件类型: I/O选项下ALTPLL;
2.右上方选择FPGA系列;
3.为生成的PLL选择语言,在AHDL,VHDL,Verilog中选择,(在此选择Verilog);
4.为元件命名,此处命名为“pll_DAC_ADC”,在地址栏最后把名字加上去即可,如下:“D:\FPGA\projects\fpga_sdr_lab_dds_dac_adc\lab_dds_dac_adc\project_q72\pll_DAC_ADC"。
效果图如下:
(3)定义PLL。根据自己开发板的时钟信号填写输入,如我的输入时钟是50M,如下图:
接下来将下面的选择框全部取消勾选,如下图:
然后一路next… …
(4)直到进行到定义的关键环节。clock multiplication factor是倍频,clock division factor是分频,在这里选择不同的数值,就会输出不同的分频和倍频的数值。注意系数上限和频率上限,但如果配置不正确,系统会有提示,所以不必太担心。按照设计要求,我们需要一个80M和20M的时钟,配置如下图:
(5)finish 之后,在左侧工程和文件的目录下调出PLL的.v文件,生成symbol即可。
生成过程还可参考新浪博客 《FPGA学习笔记之嵌入式锁相环模块ALTPLL的配置和调用》http://blog.sina.com.cn/s/blog_b351f3660102w2t7.html
二.DDS发生器
理论部分可适量参考《FPGA学习笔记之DDS》,便于理解。网址如下:https://www.cnblogs.com/zhouzheng/p/5793073.html
(1)DDS发生器的module代码如下:
// mc_dds
// multi-cycle dds module
module mc_dds(
CLK , // clock posedge
RST , // reset posedge
FREQIN, // input frequency word
FREQEN, // frequency word input enable
DDSEN , // multi-cycle dds work enable
OUTVD , // dds output valid
DDSOUT); // dds output wave
input CLK ;
input RST ;
input [32-1:0] FREQIN;
input FREQEN;
input DDSEN ;
output OUTVD ;
output [7 :0] DDSOUT;
// work enable delay line
reg ddsen_R1, ddsen_R2, ddsen_R3;
reg [32-1:0] freqin_R ;
reg [32-1:0] phase_acc_R ;
reg [8-1:0] DDSOUT ;
wire [8-1:0] rom_rdW ; // rom read data
wire [7-1:0] rom_raW ; // rom read address
always @ (posedge CLK or posedge RST) begin
if(RST) begin
ddsen_R1 <= 1'b0;
ddsen_R2 <= 1'b0;
ddsen_R3 <= 1'b0;
end
else begin
ddsen_R1 <= DDSEN ;
ddsen_R2 <= ddsen_R1;
ddsen_R3 <= ddsen_R2;
end
end
assign OUTVD = ddsen_R3;
// dds working pipeline
// OUTVD
// DDSEN | ddsen_R1 |ddsen_R2 |ddsen_R3
// | phase_acc_R |
// | rom_raW | rom_rdW | DDSOUT
always @ (posedge CLK or posedge RST) begin
if(RST) begin
phase_acc_R <= 0;
freqin_R <= 0;
end
else begin
if(FREQEN) begin
freqin_R <= FREQIN;
end // if(FREQEN)
if(DDSEN) begin
phase_acc_R <= phase_acc_R + freqin_R;
end // if(DDSEN)
if(ddsen_R2) begin
DDSOUT <= rom_rdW;
end
end
end
assign rom_raW = phase_acc_R[32-1: 32-1-7+1];
sine_rom U_sine_rom(
.CLK (CLK ), // clock
.RA (rom_raW ), // read address
.RD (rom_rdW )); // read data
endmodule // mc_dds();
(关于rom的module指路上一篇博客。主要就是在正弦波上取128等分点,可用matlab计算得到。语句为:x=linspace(0,2*pi,128); y=128*sin(x))
- DDS的RTL Viewer 如下:
sine_rom U_sine_rom的内部RTL Viewer 如下:
- DDS线路图连线
如图所示,1~8路开关控制发生信号频率(第23~30位),经过DDS信号发生器输出信号。具体原理见参考链接。
三.DAC接口模块
(1)DAC接口module代码如下:
DAC 接口模块 转换2补码为无符号数 /
// input is signed , output is unsigned
module DAC_interface(
CLKIN , // input clk
DATIN , // input data
SCALE , // scale factor, right shift the data
DAT2DAC ); // data to dac
input CLKIN;
input [8-1:0] DATIN ;
input [2-1:0] SCALE ;
output [12-1:0] DAT2DAC ;
reg [12-1:0] DAT2DAC ;
reg [12-1:0] datin_R1;
always @ (posedge CLKIN) begin
datin_R1 [11] <= ~ DATIN[7]; // inverse the msb to unsigned
datin_R1 [10: 4] <= DATIN[6:0];
datin_R1 [3 : 0] <= 0;
DAT2DAC <= (datin_R1 >> SCALE);
end
endmodule
(2)原理解释:
关于:
always @ (posedge CLKIN) begin
datin_R1 [11] <= ~ DATIN[7]; // inverse the msb to unsigned
datin_R1 [10: 4] <= DATIN[6:0];
datin_R1 [3 : 0] <= 0;
DAT2DAC <= (datin_R1 >> SCALE);
end
这几行代码,是进行2补码到无符号数转变。AD9762是最大电压对应MAX值、0电压对应0值的无符号DAC器件,有符号补码需要先把高位取反再送给DAC。具体原理如下图(字丑莫怪):
(3)接口的RTL Viewer 如下:
(3)BDF图及引脚分配:
DATIN接DDSOUT,SW调幅,接输入两路开关。具体引脚分配见下面链接:
DE0引脚分配信息:https://wenku.baidu.com/view/ad51386452ea551810a687bc.html
四.ADC接口模块
(1)ADC接口module代码如下:
module ADC_interface(
CLK_ADC , // adc clk
DAT_ADC , // input data, from adc
OTR_ADC , // from adc , signal out of range flag
OTR_OUT , // output otr flag
STBY_ADC , // adc stand by, set 0 make adc running
DOUT ); // data out
input CLK_ADC ;
input [9:0] DAT_ADC ;
input OTR_ADC ;
output OTR_OUT ;
output STBY_ADC ;
output [9:0] DOUT ;
reg [9:0] DOUT ;
reg OTR_OUT;
always @ (posedge CLK_ADC) begin
DOUT <= DAT_ADC ;
OTR_OUT <= OTR_ADC;
end
assign STBY_ADC = 1'b0;
endmodule
(2)接口的RTL Viewer
- BDF图及引脚分配
细节补充:clk信号处的非门:为了保证脉冲在数据稳定的时刻触发动作。
五.总图:
- 该设计由一个PLL从50MHz晶振生成80MHz的DAC时钟、 20MHzADC时钟
- 一个单周期的DDS生成补码正弦波,转成无符号后送至DAC
- 拨码开关SW9 SW8 用于控制DAC的输出幅度
- 其余的SW开关用于控制DDS频率字
六.编译上载
用SMA同轴电缆连接ADC和DAC,注意子卡要加一个电源(USB供电)。
拨动开关调频率字:
Signal tap 如下:
只开第一路开关,001h:
全十路开关,3FFh:
开关按照11000,018h:
开关按照1000011000,218h:
将signal tap 的数据导入matlab生成频域图像:
1) 在Quartus工程中新建Stp文件,编译,正确的在线采集FPGA内部的数据。
2) 如下图所示,在SignalTap窗口中的data下面,右键选择create SignalTapII List file。
3) 经过步骤2之后,工程所在的文件夹中会生成一个my_stp_auto_singalTap_0.txt的文件。此时可以用Quartus打开,如下图所示:
4) 在3步骤的图中,蓝色部分是数据的说明(具体表示某一列代表的某个变量),为了Matlab读取数据方便,我们可以去掉前面的说明文件,只留下感兴趣的数据,重新保存为txt文件。打开Matlab,在file下面选择Import Dta…导入.txt文件即可,效果如下图:
5)在matlab命令窗口手动输入sptool指令
6)在跳出窗口的菜单栏file/import导入需要画图的一组数据
7)在signal里面选中刚才的数据,然后再spectra下面的mtlbse模式下点create
8)在弹出的窗格中, method里面选择FFT,设置上阶数,apply即可。为了方便观察,在窗格的option菜单里选择Frequency Range/[-Fs/2,Fs/2]
参考资料:
https://wenku.baidu.com/view/704ff8bc50e2524de5187eb2.html
可得效果图如下:
DAC_DAT:
ADC_OUT:
可看出ADC底噪高于DAC输出。