基于FPGA的DDS实现

2 篇文章 0 订阅

一、DDS简介


直接数字合成技术(Direct Digital Synthesizer,DDS)诞生于 20 世纪 70 年代,该技术融合数字信号处理理论和方法,从相位的角度进行数字化处理以获得所需要的正余弦波。

DDS结构框图如下所示。其基本结构包括 N 位加法器、N 位相位寄存器、波形存储器、数模转换器、低通滤波器和工作时钟;其中 N 位加法器和 N 位相位寄存器构成 DDS 的相位累加器。
 

基本原理:

在参考时钟的驱动下,DDS 模块开始工作,当每一个参考时钟来临时,相位累加器增加一个频率控制字K,当相位累加器的值大于等于2^N之后,就会对2^N求余数并从头开始循环累加,即可生成对应频率的信号。当频率控制字改变时,N 位相位累加器的单次累加值也会对应改变,输出信号的频率就会对应改变,通过改变频率控制字,即可生成任意频率的信号,DDS 输出信号的频率满足公式f_{out}=K\cdot \frac{f_{c}}{2^N}。DDS 在参考时钟的作用下,通过对频率控制字不停地进行累加,用相位累加器输出的数据作为地址在波形存储器中通过查找地址所对应的幅值表,就可以完成其从相位到幅值之间的转化。通过改变 ROM 表中存放的数据,即可生成任意波形。

频率控制字和相位控制字

​​相位幅值的一一对应关系就好比存储器中地址和存储内容的关系,如果把一个周期内每个相位对应的幅度值存入存储器当中,那么对于任意频率的正弦信号,在任意时刻,只要已知相位Φ(t),也就知道地址,就可通过查表得到s(t)。

比如一个频率为fc的正弦信号:

其时域表达式为:S(t)=Asin(2\pi f_{c} +\theta _{0}),

其相位表达式:\phi (t)=2 \pi f_{c}+\theta_{0},

频率控制字相当于Φ(t)中的2πfc,相位控制字相当于Φ(t)中的\theta_{0}

相位累加器在每个时钟脉冲输入时,把频率控制字累加一次,相位累加器的输出数据就是信号的相位,用输出的数据作为波形存储器(ROM)的相位取样地址,这样就可以把存取在波形存储器内的波形抽样值经查找表查处,完成相位到幅值的转换。

由于相位累加器字长的限制,相位累加器累加到一定值后,其输出将会溢出,这样波形存储器的地址就会循环一次,即意味着输出波形循环一周。故改变频率控制字即相位增量,就可以改变相位累加器的溢出时间,在时钟频率不变的条件下就可以改变输出频率。改变查表寻址的时钟频率,同样也可以改变输出波形的频率。

相位截断误差

为了获得较高的频率分辨率,则只有增加相位累加器的字长N,故一般N都取值较大。但是受存储器容量的限制,存储器地址线的位数W不可能很大,一般都要小于N。这样存储器的地址线一般都只能接在相位累加器输出的高w位,而相位累加器输出余下的(N-W)个低位则只能被舍弃,这就是相位截断误差的来源。

输出频率和分辨率

DDS模块的输出频率fout是系统工作频率fc、相位累加器位数N及频率控制字K满足如下关系

         f_{out}=K\cdot \frac{f_{c}}{2^N}

 频率分辨率,即频率的变化间隔

        \Delta f=\frac{fc}{2^N}

二、实现设计

设计一个单通道的DDS信号发生器,通过改变FPGA开发板上的按键来控制输出的正弦波的频率和相位,输出1MHz,2MHz,....10MHz的波形,最后通DAC芯片输出。

RTL视图如下:

功能实现:

通过“BUTRST”按键可以实现系统的复位,通过“BUTDOWN”和“BUTUP”按键可以改变频率字,输出1MHz,2MHz......10MHz的正弦波。

设计思路:

整体分为按键模块,上电复位模块,频率字生成模块,dds核心模块。

上电复位模块

说明:

1.第一个进程用来延时,当上电后,延时100ms,以保证FPGA内部达到稳定状态;此时RSTOUT始终为1(高电平有效),也就是系统时钟处于复位状态中;

2.当100ms延时结束后,RSTOUT与系统时钟同步释放,即RSTOUT拉低,复位结束,系统开始正常工作。

代码如下:

//power up reset 
module powerup_reset(
  CLK       ,   //clk_50MHz input 
  RSTOUT    );  //powerup_reset  

input CLK;
output RSTOUT;
reg RSTOUT;
 
parameter CNTMAX = 5_000_000 - 1;

reg [31:0] counterR;

always @ (posedge CLK) begin
  if(counterR < CNTMAX) begin
    counterR <= counterR + 1'b1;
  end
  else begin
    counterR <= CNTMAX;
  end
end

always @ (counterR) begin
  if(counterR < CNTMAX) 
    RSTOUT = 1'b1;  // rst enable 处于上电复位状态
  else 
    RSTOUT = 1'b0;  // rst 持续拉低

end

endmodule

按键模块

module button_in_out(
  CLK       ,   // clock
  IN     ,   // input button signal
  OUT    );  // output button signal

// 50MHz clock, T = 20ns = 20*1E-6 ms
// button check time = 20*1E-6 * 2^20, about 20ms 
// set the CNT_WL with your system clock freq
parameter CNT_WL          = 20  ;               // counter word length
parameter CNT_MAX         = 20'h7A120           ;  // counter max value

parameter CNT_MAX68       = (CNT_MAX >> 3) * 6  ; // 6/8 counter max value
parameter CNT_MAX78       = (CNT_MAX >> 3) * 7  ; // 7/8 counter max value

input   CLK, IN;
output  OUT;

reg     button_in_d1R, button_in_d2R;
reg     [CNT_WL-1:0] counterR;    
reg     buttonIn0R, buttonIn1R, buttonIn2R, buttonIn3R, buttonOutR;
assign  OUT = buttonOutR;

always @ (posedge CLK) begin
  button_in_d1R <= IN;
  button_in_d2R <= button_in_d1R;
end
always @ (posedge CLK) begin
  if(counterR == 0)
    buttonIn0R <= button_in_d2R;
  else
    buttonIn0R <= buttonIn0R;

  if(counterR == CNT_MAX68)
    buttonIn1R <= button_in_d2R;
  else
    buttonIn1R <= buttonIn1R;

  if(counterR == CNT_MAX78)
    buttonIn2R <= button_in_d2R;
  else
    buttonIn2R <= buttonIn2R;

  if(counterR == CNT_MAX)
    buttonIn3R <= button_in_d2R;
  else
    buttonIn3R <= buttonIn3R;

end
// different values of button input buffers will start the counter 
always @(posedge CLK) begin
  if(counterR < CNT_MAX)
    counterR <= counterR + 1'b1;
  else
    counterR <= 0;
end
always @(posedge CLK) begin
  if(buttonIn0R == buttonIn1R == buttonIn2R == buttonIn3R )
    buttonOutR <= buttonIn3R;
  else
    buttonOutR <= buttonOutR;
end
endmodule   // module button_in_out() 

频率字生成模块

module generate_freq_word(
  CLK       ,   // input clock
  RST       ,   // input reset 
  BUTUP     ,   // input button upward
  BUTDOWN   ,   // input button downward
  FQWD      ,   // output frequency word
  FWEN      );  // output frequency word update enable

parameter VAL_FREQ_BASE = 32'h051E_B851;  // 50MHz clock, 1MHz out wave freqword value
parameter VAL_FREQ_MAX  = (VAL_FREQ_BASE * 10);  // 50MHz clock, MAX 10MHz out wave freqword value

input CLK,RST;       
input BUTUP, BUTDOWN;
output [31:0] FQWD;      
output FWEN;      
// when push button will cause these DFFs 1 clock cycle high level  
reg butup_enR, butdown_enR; 
reg butup_d1R, butdown_d1R; 
reg [31:0] FQWD;      
reg FWEN;      
/        
// pipeline align
// BUTUP        | butup_d1R    |    butup_enR   | FWEN
// BUTDOW       | butdown_d1R  |    butdown_enR | FQWD
/        
always @ (posedge CLK or posedge RST) begin
  if(RST) begin
    butup_d1R   <= 1'b0; 
    butdown_d1R <= 1'b0; 
    butup_enR   <= 1'b0;    
    butdown_enR <= 1'b0; 
  end
  else begin
    butup_d1R   <= BUTUP    ; 
    butdown_d1R <= BUTDOWN  ;
    if((~butdown_d1R) && (BUTDOWN))
      butdown_enR <= 1'b1;
    else
      butdown_enR <= 1'b0;
    if((~butup_d1R) && (BUTUP))
      butup_enR <= 1'b1;
    else
      butup_enR <= 1'b0;
  end
end
always @ (posedge CLK or posedge RST) begin
  if(RST) begin
    FQWD <= VAL_FREQ_BASE;
    FWEN <= 1'b1;
  end
  else begin
    // BUTTON UP
    if(butup_enR &&(~butdown_enR)) begin
      if(FQWD == VAL_FREQ_MAX)
        FQWD <= VAL_FREQ_BASE;
      else
        FQWD <= FQWD + VAL_FREQ_BASE;
    end
    // BUTTON DOWN
    else if((~butup_enR) &&butdown_enR) begin
      if(FQWD == VAL_FREQ_BASE)
        FQWD <= VAL_FREQ_MAX;
      else
        FQWD <= FQWD - VAL_FREQ_BASE;
    end
    // no valid button push
    else begin
      FQWD <= FQWD;   // hold old value
    end
    FWEN <= (butup_enR || butdown_enR);
  end
end


endmodule

DDS核心模块

module dds_core_sin(
  CLK   ,   // clock, posedge valid
  RST   ,   // reset, high level reset
  FWEN  ,   // frequency word update enable, high level enable
  FWIN  ,   // input frequency word
  CLKOUT,   // output clock
  SINOUT);  // sine signal output, 2's complement format

input           CLK;
input           RST;
input           FWEN;
input [32-1:0]  FWIN;
output[12-1:0]  SINOUT;
output          CLKOUT;

parameter FW_WL = 32;   // frequency word word length in bit
parameter RA_WL = 10;   // rom address word length in bit
parameter RD_WL = 12;   // rom data  word word length in bit

reg   [FW_WL -1:0]  fwin_R;     // freq word DFF
reg   [FW_WL -1:0]  acc_R;      // phase ACC DFF
reg   [RA_WL -1:0]  addr_R;     // rom address DFF
reg   [RD_WL -1:0]  sinout_R;   // sin wave output DFF
wire  [RD_WL -1:0]  romout_W;   // rom data output wire

always @ (posedge CLK or posedge RST) begin
  if(RST) begin
    fwin_R   <= 0;
    acc_R    <= 0;
    addr_R   <= 0;
    sinout_R <= 0;
  end
  else begin
    // update fwin_R DFF
    if(FWEN)
      fwin_R <= #1 FWIN;
    else
      fwin_R <= #1 fwin_R;
    
    // update acc_R
    acc_R <= #1 fwin_R + acc_R;
    // update addr_R, the acc_R high RA_WL is rom address
    addr_R <= acc_R[FW_WL-1:FW_WL-1-(RA_WL-1)];     

    // update output DFF
    sinout_R <= #1 romout_W;
  end
end

DDS_CORE_ROM u_sinrom(
  .CLK    (CLK      ),  // clock
  .RA     (addr_R   ),  // read address
  .RD     (romout_W )); // read data

assign SINOUT = sinout_R;
assign CLKOUT = CLK;

endmodule // module dds_core

其中正弦波存储的幅度表可以由MATLAB实现

% %生成12位单周期正弦波信号,4096个数据
% N = 4096;
% i = 1:1:4096;
% y = 2048*(sin(2*pi/4096*i)+1);
% y1 = dec2bin(y,12);
% fid=fopen('D:\FPGA\FPGA_program\quartus\DDS\rtl\sin.txt','w');              %需要改文件名称的地方
% y2 = zeros(1,N);
% for i=1:1:4096
% %     y2(i)=str2num(y1(i));
%     fprintf(fid,'%s\n',y1(i));          %data:需要导出的数据名称,10位有效数字,保留3位小数(包含小数点),f为双精度,g为科学计数法
% end
% fclose(fid);
clc;
clear;
i=0:2*pi/4095:2*pi;
y=(sin(i)+1)*4095/2;
k=dec2bin(y,12);
fid=fopen('H:\FPGA\FPGA_program\quartus\DDS\rtl\sin.txt','wt');

for i=1:4096%1024行
    for j=1:12%11列
        fprintf(fid,'%s',k(i,j));%输出
            if mod(j,12)==0%判断是否输出了11个字符
                fprintf(fid,'\n');%每输出11个字符也就是输出了一行,输出一个回车
            end;
	 end;
end;
fclose(fid);

顶层模块

//正弦波产生
    
module top_sin_wave(
  CLK       ,   // clock, posedge valid
  BUTUP     ,   // input button freq upward setting
  BUTDOWN   ,   // input button freq downward setting 
  BUTRST    ,   // reset button
  CLKOUT    ,   // clock output
  SINOUT    );  // DDS sine wave out, to DAC
input CLK;
input BUTUP, BUTDOWN, BUTRST;
output CLKOUT;
output[12-1:0]  SINOUT;

wire butup_W, butdown_W, butrst_W;
wire butup_no_jitter_W, butdown_no_jitter_W, butrst_no_jitter_W, rst_W;
wire [32-1:0] fqwd_W;
wire fwen_W;
wire dds_clkout_W;
wire [12-1:0]  dds_out_2c_W; // dds output in 2's complement format
// fpga board button is pull-up to VCC
assign butup_W     = ~BUTUP    ; 
assign butdown_W   = ~BUTDOWN  ;
assign butrst_W    = ~BUTRST   ;
assign rst_W = butrst_no_jitter_W | pu_rst_W;

powerup_reset U_PURST(
  .CLK       (CLK       ),   // clock   
  .RSTOUT    (pu_rst_W  ));  // power up reset

button_in_out U_BUTUP(
  .CLK      (CLK                ),   // clock
  .IN       (butup_W            ),   // input button signal
  .OUT      (butup_no_jitter_W  ));  // output button signal

button_in_out U_BUTDOWN(
  .CLK      (CLK                ),   // clock
  .IN       (butdown_W          ),   // input button signal
  .OUT      (butdown_no_jitter_W));  // output button signal

button_in_out U_BUTRST(
  .CLK      (CLK                ),   // clock
  .IN       (butrst_W           ),   // input button signal
  .OUT      (butrst_no_jitter_W ));  // output button signal

generate_freq_word U_GFQWD(
  .CLK       (CLK                   ),   // input clock
  .RST       (rst_W                 ),   // input reset 
  .BUTUP     (butup_no_jitter_W     ),   // input button upward
  .BUTDOWN   (butdown_no_jitter_W   ),   // input button downward
  .FQWD      (fqwd_W                ),   // output frequency word
  .FWEN      (fwen_W                ));  // output frequency word update enable

dds_core_sin U_DDS(
  .CLK      (CLK            ),   // clock, posedge valid
  .RST      (rst_W          ),   // reset, high level reset
  .FWEN     (fwen_W         ),   // frequency word update enable, high level enable
  .FWIN     (fqwd_W         ),   // input frequency word
  .CLKOUT   (dds_clkout_W   ),   // output clock
  .SINOUT   (dds_out_2c_W   ));  // sine signal output, 2's complement format

assign SINOUT = dds_out_2c_W;
assign CLKOUT = ~dds_clkout_W;

endmodule // module dds_core

三、实验测试

signaltap分析

 

由逻辑分析仪结果可知,基本实现实验要求。从FPGA产生的数字信号,在经过dac转换以及低通滤波,可以实现正弦信号的产生。

示波器测量

FPGA输出的10位数字信号经过DAC器件进行D/A转换 输出模拟信号,再用示波器测量,测量结果如下:

1MHz正弦波输出

 2MHz输出

从上面测量结果,得到的频率基本正确,但是得到的波形并不是正弦波形

原因:AD9762是无符号的DAC器件,当有符号补码需要先把高位取反再送给DAC。

对于二进制补码来说,把它看作有符号数,对最高位取反;相当于把它看作无符号数时,加2^(N-1)  

将最高位取反 ,即将DAC的12位输出与'1_000_0000_0000'进行异或运算

assign SINOUT = SINOUT ^ 12'b1_000_0000_0000;

再次通过示波器观察,随机选取了1MHz,2MHz,10MHz的正弦信号输出:

 

 

 

由示波器可以看出输出的正弦波频率和波形基本正确。但在高频部分有较为明显的失真。

后续可以将DAC输出再加一次滤波电路,应该能够得到比较好的波形,有待进一步测试。

  • 2
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

juajua_Z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值