当前位置:   article > 正文

基于FPGA的简易DDS信号发生器的设计与验证_fpga的dds信号发生器实现

fpga的dds信号发生器实现

一,理论介绍

DDS 是直接数字式频率合成器(Direct Digital Synthesizer)的英文缩写,是一项关键的数字化技术。作为设计人员,我们习惯称它为信发生器,一般用它产生正弦、锯齿、方波等不同波形或不同频率的信号波形,在电子设计和测试中得到广泛应用。
DDS 的基本结构主要由相位累加器、相位调制器、波形数据表 ROM、D/A 转换器等四大结构组成,其中较多设计还会在数模转换器之后增加一个低通滤波器
在这里插入图片描述
实现的原理如下图所示:
在这里插入图片描述
系统时钟 CLK 为整个系统的工作时钟,频率为 fCLK ;频率字输入 F_WORD,一般为整数,数值大小控制输出信号的频率大小,数值越大输出信号频率越高,反之,输出信号频率越低,后文中用 K 表示;相位字输入P_WORD,为整数,数值大小控制输出信号的相位偏移,主要用于相位的信号调制,后文用 P 表示;设输出信号为 CLK_OUT,频率为 fOUT。
相位累加器是整个 DDS 的核心,在这里完成相位累加,生成相位码。相位累加器的输入为频率字输入 K,表示相位增量,设其位宽为 N,满足等式 K = 2 N ∗ f O U T / f C L K K=2^{N} *f_{OUT}/f_{CLK} K=2NfOUT/fCLK
波形数据表 ROM中存有一个完整周期的正弦波信号。假设波形数据 ROM的地址位宽为 12 位,存储数据位宽为 8位,即 ROM有 2 12 2^{12} 212 = 4096个存储空间,每个存储空间可存储 1字节数据。将一个周期的正弦波信号,沿横轴等间隔采样 2 12 2^{12} 212 = 4096 次,每次采集的信号幅度用 1 字节数据表示,最大值为 255,最小值为 0。将 4096 次采样结果按顺序写入 ROM的 4096 个存储单元,一个完整周期正弦波的数字幅度信号写入了波形数据表 ROM 中。波形数据表 ROM 以相位调制器传入的相位码为 ROM 读地址,将地址对应存储单元中的电压幅值数字量输出。
在这里插入图片描述

补充:举例理解

设:ROM 存储单元个数为 4096,每个存储数据用 8 位二进制表示。即,ROM 地址线宽度为 12,数据线宽度为 8;相位累加器位宽 N = 32。
根据上述条件可以知道,相位调制器位宽 M = 12(需要进行相位选择,因此其位宽与ROM地址位宽一致),那么根据 DDS 原理。那么在相位调制器中与相位控制字进行累加时,应用相位累加器的高 12 位累加。而相位累加器的低20 位只与频率控制字累加。我们以频率控制字 K = 1为例,相位累加器的低 20位一直会加 1,直到低 20位溢出向高 12 位进位,此时 ROM 为 0,也就是说,ROM 的 0 地址中的数据被读了 2 20 2^{20} 220 次,继续下去,ROM中的 4096个点,每个点都将会被读 2 20 2^{20} 220次,最终输出的波形频率应该是参考时钟频率的 1 / 2 20 2^{20} 220,周期被扩大了 2 20 2^{20} 220倍。同样当频率控制字为 100 时,相位累加器的低 20 位一直会加 100,那么,相位累加器的低 20 位溢出的时间比上面会快 100 倍,则 ROM 中的每个点相比于上面会少读 100次,所以最终输出频率是上述的 10 倍。
假设有1000个点,从0+1+2+…+1000,对比0+100+200+…+1000前者数1000次,后者数10次,相差100倍。因此就是100倍的差距。

二,代码实现

1,实验目标

使用questasim实现DDS波形的显示,显示四种波形,分别为正弦波方波,三角波与锯齿波。先使用MATLAB生产.coe文件然后在使用ISE14.7进行代码的书写。实验仿真结果如下
在这里插入图片描述

2,MATLAB代码

%%  wave_16384x8
clc ;           %清除命令行
clear all ;     %清除工作区变量,释放内存空间
F1 = 1 ;        %信号频率 sin(2*pi*f*t + a)
Fs = 2^12 ;     %采样频率
P1 = 0 ;        %信号初始初始相位
N = 2^12 ;      %采样点数
t = [0 : 1/Fs:(N - 1)/Fs]; %采样时刻
ADC = 2^7 - 1;  %直流分量
A = 2^7;        %信号幅度
s1 = A*sin(2*pi*F1*t + pi*P1/180) + ADC ; %正弦波信号
s2 = A*square(2*pi*F1*t + pi*P1/180) + ADC; %方波信号
s3 = A*sawtooth(2*pi*F1*t + pi*P1/180,0.5) + ADC; %三角波信号
s4 = A*sawtooth(2*pi*F1*t + pi*P1/180) + ADC; %锯齿波信号
%创建coe文件
plot(s4)
fild = fopen('wave_16384x8.coe','wt');
%写入coe头文件
fprintf(fild, '%s\n','MEMORY_INITIALIZATION_RADIX=10;');%十进制
fprintf(fild, '%s\n','MEMORY_INITIALIZATION_VECTOR=');
for j = 1:4
    for i = 1:N
    	if j == 1 %打印正弦信号数据
            s0(i) = round(s1(i)); %对小数四舍五入以取整
        end 
        if j == 2 %打印方波信号数据
            s0(i) = round(s2(i)); %对小数四舍五入以取整
        end
        if j == 3 %打印三角波信号数据
            s0(i) = round(s3(i)); %对小数四舍五入以取整
        end
        if j == 4 %打印锯齿波信号数据
            s0(i) = round(s4(i)); %对小数四舍五入以取整
        end
        if s0(i) <0 %1 强制置零
            s0(i) = 0
        end
        if j == 4 && i == N
            fprintf(fild, '%d',s0(i)); %数据写入
            fprintf(fild, '%s',';'); %最后一个数使用分号结束
        else
            fprintf(fild, '%d',s0(i)); %数据写入
            fprintf(fild, '%s\n',','); %逗号,换行
        end
    end
end
fclose(fild)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

3,verilog代码及实现思路

实际上就是读.coe文件里的数据。但是由于我们产生了四个波形,因此需要对地址进行控制,其次为了控制频率与初相。
首先是对于波形输出的选择。这里控制相位累加器单次累加值可以控制频率,控制相位偏移量可以控制波形的初相。这里生成500HZ,初相为pi/2的波形。
在这里插入图片描述
核心代码如下

`timescale 1ps / 1ps
//-----------------------------------------------------------------------------------
// Copyright :This document is only for personal learning reference and research.
// Module:  dds
// File:    dds.v
// Author:  meng guodong
// E-mail:  823300630@qq.com
// Time :   2021-02-20 10:15:32
// Description: 
// Revision: 1.0
//-------------------------------------------------------------------------------------------------
module dds (
    //system signals
    input                   clk             ,
    input                   rst_n           ,
    //others signals
    input   [3:0]           wave_select     ,
    output  [7:0]           data_out        
);

//========================================================================\
// =========== Define Parameter and Internal signals =========== 
//========================================================================/
parameter   sin_wave = 4'b0001 , //正弦波
            squ_wave = 4'b0010 , //方波
            tri_wave = 4'b0100 , //三角波
            saw_wave = 4'b1000 ; //锯齿波
parameter   FREQ_CTRL = 32'd42949 , //相位累加器单次累加值
            PHASE_CTRL = 12'd1024 ; //相位偏移量

reg [31:0] fre_add; //相位累加器
reg [11:0] rom_addr_reg; //相位调制后的相位码
reg [13:0] rom_addr ; //ROM 读地址
//需要输出的波形频率为 500Hz,初相位为π/2的信号
//=============================================================================
//**************    Main Code   **************
//=============================================================================
//fre_add:相位累加器
always @ (posedge clk or negedge rst_n) 
begin  
    if(!rst_n)
        fre_add <= 'd0;
    else
        fre_add <= fre_add + FREQ_CTRL;
end 
//rom_addr_reg
always @ (posedge clk or negedge rst_n) 
begin  
    if(!rst_n)
        rom_addr_reg <= 'd0;
    else
        case (wave_select) 
            sin_wave    :   rom_addr_reg <= fre_add[31:20] + PHASE_CTRL;
            squ_wave    :   rom_addr_reg <= fre_add[31:20] + PHASE_CTRL;
            tri_wave    :   rom_addr_reg <= fre_add[31:20] + PHASE_CTRL;
            saw_wave    :   rom_addr_reg <= fre_add[31:20] + PHASE_CTRL;
            default :   ;
        endcase
end 
                
//rom_addr
always @ (posedge clk or negedge rst_n) 
begin  
    if(!rst_n)
        rom_addr <= 'd0;
    else
        case (wave_select) 
            sin_wave    :   rom_addr <= rom_addr_reg + 14'd4096*0   ;
            squ_wave    :   rom_addr <= rom_addr_reg + 14'd4096*1   ;
            tri_wave    :   rom_addr <= rom_addr_reg + 14'd4096*2   ;
            saw_wave    :   rom_addr <= rom_addr_reg + 14'd4096*3   ;
            default :   ;
        endcase
end 

dds_inst dds_inst_0 (
  .clka(clk), // input clka
  .addra(rom_addr), // input [13 : 0] addra
  .douta(data_out) // output [7 : 0] douta
);
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81

关注公众号:果冻空间 回复:A-003 即可获取工程源码

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/666906
推荐阅读
相关标签
  

闽ICP备14008679号