下冰雹 · 1月2日

基于 FPGA 的任意波形发生器(DDS)设计(附源工程)

设计背景

DDS(Direct Digital Synthesizer)直接数字式频率合成器,是一种新型频率合成技术,具有低成本、低功耗、高分辨率、相对带宽大和频率转换时间短等优点。较容易实现频率、相位以及幅度的数控调制,广泛应用在电信与电子仪器和通信领域。波形发生器是一种数据信号发生器,在调试硬件时,常常需要加入一些信号,以观察电路工作是否正常。加入的信号有:正弦波、三角波、方波和任意波形等。

设计原理

相位(phase)是对于一个波,特定的时刻在它循环周期中的位置:一种它是否在波峰、波谷或它们之间的某点的标度。相位描述信号波形变化的度量,通常以度 (角度)作为单位,也称作相角。当信号波形以周期的方式变化,波形循环一周即为 360°。那么相位可调也可以简单的理解为:改变初始相位。

频率,是单位时间内完成周期性变化的次数,是描述周期运动频繁程度的量,常用符号 f 或 ν 表示,单位为秒分之一,符号为 s-1,频率可调也就是改变单位时间内完成周期性变化的次数。

本设计采用 DDS 技术设计相位频率可调的波形发生器,已经知道了相位和频率可调分别代表什么,那么接下来就要知道怎样依靠 DDS 技术实现波形发生器,并且相位和频率可以调控。DDS 的基本结构如下图所示:

image.png

根据上图可以看出:DDS 主要由相位累加器、波形数据表(ROM)、D/A 转换器构成,本设计暂时不涉及 D/A 转换部分。相位累加器位宽为 N,波形数据表的大小为 2^P,累加器的高 P 位则用于寻址波形数据表,即 ROM,从 ROM 中输出的数据则是产生的波形。如果累加器在系统时钟(CLOCK)的作用下,以步进为 M 累加直至溢出,则 M 为频率控制字(即图中的 FWORD),相位控制字(PWORD)则作为累加器的输入初始值。这里的累加器,也可以理解为 ROM 的地址发生器。

上段所述我们可具体理解为:改变地址的初值(PWORD)就可以改变初始的相位,由于我们设计中,ROM 的数据为 256,所以 PWORD 的值在 0~255 之间,PWORD= 256*(初始相位/360 度)。

我们设计的系统时钟(CLOCK)为 50MHz,周期为 20ns,而正弦波被分成了 256 个点,波形发生器的频率就是 195.31KHz。若想要输出别的频率,则可通过改变输出的点的个数,即改变有效地址的数量。我们用位宽为 N 位的累加器,假设 FWORD 为 1,要产生一个完整波形的周期则为 20ns*2^N,则产生波形的频率=系统时钟/2^N,即 Fout = Fclk/2^N,如果 FWORD 为 B,每次步进的间隔提高了 B 倍,所以计满一个波形周期的时间就缩小了 B 倍,即频率就提高了 B 倍。则波形频率的公式为:Fout = B*(Fclk/2^N)。之后我们取累加器的高 8 位,去寻址波形数据,对应点的还是个数一样的。本设计中我们将 N 取为 32,当 B=1,Fout 约为 0.012Hz,0.012 就相当于最小精度,所以我们就实现了频率为 0.012 倍数的调制,但因为 0.012 值很小了,所以可以说基本实现了所有频率的调制。

设计架构

根据上述的原理图分析,本设计的架构如下图:

image.png

架构图中的端口功能描述如下表:

image.png

dds_addr 模块是实现相位累加器的模块,这里用参数来调制 FWORD 和 PWORD 的值,累加之后,将地址高八位(addr_out)输出到 rom 模块,从而产生波形数据。

设计代码

在具体写代码之前,我们需要先制作载有波形数据的 mif 文件,这时需要一个小软件(Mif_Maker2010),软件的安装包和源码一起,大侠可以去公众号内获取。

具体操作步骤如下:

打开 Mif_Maker2010,在查看中点击全局参数,如下图:

image.png

将全局参数设置如下图:数据长度为 256,数据位宽为 8,数据格式为无符号 10 进制,采样频率为 1000。

image.png

点击设定波形,选择想要生成的波形,这里我们以正弦波为例,如需要其他波形,都可进行修改:

image.png

之后点击保存,则可生成 mif 文件,这里我们命名为 sin.mif。打开 sin.mif 后,如下图所示:

image.png

dds_addr 模块代码:

这里我们以初始相位为 180 度,频率为 5KHz 为例:

module dds_addr (clk, rst_n, addr_out);

  input clk, rst_n;   //系统时钟复位
  output [7:0] addr_out;  //输出的地址,对应到ROM内的数据
   
  parameter N = 32;
  parameter PWORD = 128;  //相位控制字 (x/360)*256
  parameter FWORD = 429497; //频率控制字F_out=B*(F_clk/2**32),fword=B
  //5KHZ
  reg [N-1:0] addr;  //32位累加器
  
  always @ (posedge clk or negedge rst_n)
  begin
    if (!rst_n)
      begin
        addr <= 32'd0;  //相位控制字
      end
    else
      begin
  /*每隔fword的大小,输出一位地址,若频率控制字FWORD等于2,那么地址计数器输出的就依次是0,2,4.....*/    
        addr <= addr + FWORD;
      end    
  end 
  /*将累加器器的地址的高八位赋值给输出的地址(ROM的地址*/
  assign addr_out = addr[N-1:N-8] + PWORD;

endmodule 

rom 模块为调用的 IP 核,该 rom IP 核中存储了 sin.mif 的数据。

dds 顶层模块代码:

module dds (clk, rst_n, q);

  input clk, rst_n;   //系统时钟复位
  output [7:0] q;     //输出波形数据
  
  wire [7:0] addr_out;  //8位地址,对应到ROM内的数据
  
  /*****相位累加器模块*****/
  dds_addr dds_addr_inst(
    .clk(clk), 
    .rst_n(rst_n),
    .addr_out(addr_out)
  );
  
  /*****波形数据模块*****/
  rom  rom_inst (
    .address ( addr_out ),
    .clock ( clk ),
    .q ( q )
  );

endmodule

仿真测试

dds_tb 顶层模块的测试模块:

`timescale 1ns/1ps

module dds_tb;

  reg clk, rst_n;
  wire [7:0] q;
  
  initial begin
    clk = 1;
    rst_n = 0;
    #200.1
    rst_n = 1;
    
    #50_000_000 $stop;
  end 

  dds dds_dut(
    .clk(clk), 
    .rst_n(rst_n),
    .q(q)
  );
  
  always #10 clk = ~clk;

endmodule 

仿真图:

image.png

根据上图可知,我们的设计正确。并且可以实现相位和频率可调。

END

作者:FPGA佚名侠客
来源:FPGA技术江湖

相关文章推荐

更多 FPGA 干货请关注 FPGA 的逻辑技术专栏。欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。
推荐阅读
关注数
10617
内容数
588
FPGA Logic 二三事
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息