FPGA综合系统设计(四):串口控制的DDS信号发生器

传统DDS原理

这里写图片描述
       DDS 全称 Direct Digital Synthesizer(直接数字合成),是从相位出发,直接采用数字技术产生波形的一种频率合成技术。基本模型如上图所示,主要由时钟频率源fclk、相位累加器、波形存储器、及后级数模转换器(DAC)、低通滤波器(LPF)组成。频率控制字M和相位控制字分别控制DDS输出正(余)弦波的频率和相位。每来一个时钟脉冲,相位寄存器以步长M递增。相位寄存器的输出与相位控制字相加,其结果作为正(余)弦查找表的地址。正(余)弦查找表的数据存放在ROM中,内部存有一个周期的正弦波信号的数字幅度信息,每个查找表的地址对应于正弦波中 0°~360°范围内的一个相位点。查找表把输入的址信息映射成正(余)弦波的数字幅度信号,同时输出到数模转换器DAC的输入端,DAC 输出的模拟信号经过低通滤波器 (LPF),可得到一个频谱纯净的正(余)弦波。
       更详细的DDS原理、优缺点可以参考论文“基于FPGA的多波形信号源研究和仿真_王宁.”。
这里写图片描述
       DDS输出频率的计算公式:Fo=Fs*K/M;Fo为输出频率,Fs为参考时钟(也可以认为是DAC时钟),M为波形rom的深度,K为频率控制字。当K为1时的Fo即为频率分辨率。

环境与设备

    Quartus II 12.1
    ModelSim-Altera 12.1
    MATLAB(生成波形数据)

系统设计

       设计一个系统:串口接收频率、相位控制字,控制的DAC输出波形(正弦波、三角波、锯齿波、方波、直流)。串口通信程序使用本人博客“FPGA基础设计(三):UART串口通信”中的内容。
设计中取DAC输出时钟为50MHz,波形存储深度为512点(取信号的一个周期),用matlab生成mif格式的文件分别存储正弦波、方波、三角波、锯齿波的数据。顶层模块的程序如下所示:

module UART_DDS
(
    input sys_clk,
    input rst_n,   //低电平有效复位按键
    input [7:0] c0,c1,c2,c3,c4,c5,

    //DAC,8bit位宽
    output daclk,
    output [7:0] dadata,

    //串口接收
    input uart_rx
);

//module1
//************  DAC时钟管理  *************
wire clk_da;

assign clk_da = sys_clk;  //DAC采样速率等于系统时钟,50MHz
assign daclk = clk_da;


//module2
//************  串口时钟管理  *************

wire clk_uart;              //波特率的16倍时钟,串口时钟

clkdiv clkdiv_inst          //分频产生串口时钟
(
    .clk50(sys_clk),        //50MHz系统时钟输入
    .clkout(clk_uart)       //分频产生串口时钟
);


//module3
//************       串口数据接收         *************
wire rdsig;
wire [7:0] rxdata;
reg [7:0] ctrli;
reg [7:0] ctrl_data [5:0];   //控制命令字, 0-波形,1-2频率,3直流,4-5初相
wire [8:0]phase_i;

uartrx uartrx_inst (
        .clk                     (clk_uart),            //16倍波特率的时钟 
      .rx                      (uart_rx),             //串口接收
        .dataout                 (rxdata),              //uart 接收到的数据,一个字节                     
      .rdsig                   (rdsig),               //uart 接收到数据有效 
        .frameerror              ()
);


//module4
//************       存储命令控制字         *************
//帧格式,0xFF,D0波形,D1频率高8bit,D2频率低8bit,D3直流,D4初相高8bit,D5初相低8bit

//使用串口时注释掉下面一段,将上面一段的注释取消;进行DDS的仿真时,注释上一段

/* 使用串口时
always @ (negedge rdsig)
    if (rxdata == 8'hFF)  ctrli <= 0;   //0xFF为帧头
    else begin
        ctrl_data[ctrli] <= rxdata;
        ctrli <= ctrli + 1'b1;
    end
*/

// 使用仿真时
always @ (posedge clk_da)   
begin
    ctrl_data[0] <= c0;
    ctrl_data[1] <= c1;
    ctrl_data[2] <= c2;
    ctrl_data[3] <= c3;
    ctrl_data[4] <= c4;
    ctrl_data[5] <= c5;
end


//module5    
//************      DAC输出波形管理       *************

reg [8:0] rom_addr;     //范围 0-511
reg [7:0] dadata_reg;
reg [15:0] freq,freq_h,freq_l;
wire [7:0] sin_data, triangle_data, sawtooth_data, square_data;
reg [7:0] dc_data;
assign dadata = dadata_reg;  

always @ (negedge clk_da or negedge rst_n)
     if (!rst_n) dadata_reg <= 'd0;
     else
         case (ctrl_data[0])     //根据波形控制字选择不同的波形数据输出
              8'h00: dadata_reg <= sin_data;
              8'h01: dadata_reg <= sin_data;        //正弦波
              8'h02: dadata_reg <= triangle_data;   //三角波
              8'h03: dadata_reg <= sawtooth_data;   //锯齿波
              8'h04: dadata_reg <= dc_data;         //直流
              8'h05: dadata_reg <= square_data;     //方波
              default: dadata_reg <= sin_data;
         endcase


//module6    
//************      拼接获取初相控制字       *************
//相位分辨率 f=360°*K/N; 
//                    K为相位控制字;N为相位累加器长度,此处为512(9bit位宽)

reg [15:0] phase,phase_h,phase_l;

always @ (negedge clk_da or negedge rst_n) 
    if (!rst_n) begin
        phase_h <= 'd0;    //初相高8bit
        phase_l <= 'd0;         //初相低8bit
        phase <= 'd0;         //组合为16bit的初相控制字 
    end
    else begin
        phase_h <= ctrl_data[4] << 8;    //初相高8bit
        phase_l <= ctrl_data[5];         //初相低8bit
        phase <= phase_h | phase_l;         //组合为16bit的初相控制字 
    end


//module7    
//************      拼接获取频率控制字       *************
//频率分辨率 f=CLK*K/N; CLK为DAC时钟,此处为50MHz;
//                    K为频率控制字;N为相位累加器长度,此处为512(9bit位宽)

always @ (negedge clk_da or negedge rst_n) 
    if (!rst_n) begin
        freq_h <= 'd0;    //频率高8bit
        freq_l <= 'd0;         //频率低8bit
        freq <= 'd0;         //组合为16bit的频率控制字 
    end
    else begin
        freq_h <= ctrl_data[1] << 8;    //频率高8bit
        freq_l <= ctrl_data[2];         //频率低8bit
        freq <= freq_h | freq_l;         //组合为16bit的频率控制字 
    end 

//module8    
//************      相位累加器       *************

wire [8:0] rom_addr_phase;

always @ (negedge clk_da or negedge rst_n)
     if (!rst_n) rom_addr <= 'd0;
    else if (rom_addr + freq >= 'd512) rom_addr <= rom_addr + freq - 'd512;  
    else rom_addr <= rom_addr + freq;   

assign rom_addr_phase = rom_addr + phase;   //相位累加器输出加上初相控制字

//module8    
//************      输出直流电压       *************

always @ (negedge clk_da or negedge rst_n)
    if (!rst_n) 
        dc_data <= 'd0;
    else
        dc_data <= ctrl_data[3];    


//module9    
//************      1-Port ROM存储波形数据       *************    

rom1    rom1_inst (          //正弦波
    .address ( rom_addr_phase ),
    .clock   ( clk_da ),
    .q       ( sin_data ) 
    );

rom2    rom2_inst (          //锯齿波
    .address ( rom_addr_phase ),
    .clock   ( clk_da ),
    .q       ( sawtooth_data ) 
    );  

rom3    rom3_inst (          //三角波
    .address ( rom_addr_phase ),
    .clock   ( clk_da ),
    .q       ( triangle_data ) 
    );  

rom4    rom4_inst (          //方波
    .address ( rom_addr_phase ),
    .clock   ( clk_da ),
    .q       ( square_data ) 
    );  

endmodule 

       完整的工程奉上(含testbench,已在开发板上验证):https://download.csdn.net/download/fpgadesigner/10438885

ModelSim仿真

       仿真时博主遇到了问题,ModelSim中没有输出的DAC波形数据。考虑应该是存储波形数据的ROM IP核出了问题,网上搜索后发现想要仿真最方便的应该是将存储的mif文件转换为hex文件。在Quartus II中点击“File-Open”打开“.mif”格式的文件,再点击“File-Save as”选择“.hex”格式保存。重新将hex文件导入ROM中综合,再次仿真问题解决。
       其实并不是仿真ROM IP核时不支持mif文件,不过用hex文件操作最简便。仿真结果如下:

1 设置90°初相
这里写图片描述
2 设置不同的频率
这里写图片描述
3 切换不同的波形
这里写图片描述

总结

       传统DDS原理的好处是分辨率可以设置的很高,只要增加相位累加器的位数并截取高位赋值给DAC输出数据即可。不过在设计中最直观的缺点恐怕就是频率分辨率很难做到整数。
       比如在上面的设计中,频率分辨率fclk=50MHz/512,显然不是整数,一个解决方法是将ROM的存储深度改为500个点,这样频率分辨率便是100kHz。根据公式,很明显,增加ROM的存储深度或者减小时钟都可以增加频率分辨率。
       针对传统DDS原理的缺陷,现在很多人提出了改进,比如“基于FPGA的信号发生器研究蔡历鑫”这篇论文中提出了可编程的相位累加器模值和利用CORDIC算法;“基于FPGA的直接数字频率合成器的设计彭昭”这篇论文提出了循环相位累加器的方法,频率调整不仅非常小且保持为整数,不过该方法每个信号周期取点数少,只适合用于正弦波信号的生成。
       对FPGA设计的DDS信号发生器感兴趣的同学可以结合博主的工程,网上下载文中提到的这几篇论文,相信不难理解。

猜你喜欢

转载自blog.csdn.net/fpgadesigner/article/details/80459548
今日推荐