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

  • 10小时前

大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分。大侠可以关注FPGA技术江湖,在“闯荡江湖”、"行侠仗义"栏里获取其他感兴趣的资源,或者一起煮酒言欢。

今天给大侠带来基于FPGA的任意波形发生器设计,附源码,获取源码,请在“FPGA技术江湖”公众号内回复“   DDS设计源码”,可获取源码文件。话不多说,上货。

设计背景

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

设计原理

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

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

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

根据上图可以看出: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值很小了,所以可以说基本实现了所有频率的调制。

设计架构

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

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

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

设计代码

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

具体操作步骤如下:

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

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

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

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

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 

仿真图:

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

人工客服
(售后/吐槽/合作/交友)

相关资讯

  1. 1.
  2. 2.
  3. 3.
  4. 4.
  5. 5.
  6. 6.
  7. 7.
  8. 8.
  9. 9.
  10. 10.
  11. 11.
  12. 12.
  13. 13.
  14. 14.
  15. 15.
  16. 16.
  17. 17.
  18. 18.
  19. 19.
  20. 20.
查看全部20条内容