本帖最后由 abner_ma 于 2020-12-25 20:02 编辑


用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信号处的非门:为了保证脉冲在数据稳定的时刻触发动作。


五.总图:


  • 该设计由一个PLL50MHz晶振生成80MHzDAC时钟、 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输出。