数字信号处理专题——DDS函数发生器环路Demo
Posted moluoqishi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数字信号处理专题——DDS函数发生器环路Demo相关的知识,希望对你有一定的参考价值。
一、前言
会FPGA硬件描述语言、设计思想和接口协议,掌握些基本的算法是非常重要的,因此开设本专题探讨些基于AD DA数字信号处理系统的一些简单算法,在数字通信 信号分析与检测等领域都会或多或少有应用。我们还是从老生常谈的DDS函数发生器开始,讲解DAC ADC基本使用以及DDS算法原理与设计方式。
二、设计预期
功能:基于ROM的频率可调DDS正弦函数发生器
DAC ADC型号与设计参数:DAC为AD9708,更新速率125MSPS,精度8bit;ADC为AD9280,采样率32MSPS,精度8bit。由于ADC采样率限制,设计使用32MHZ频率时钟更新与采样数据,并将ROM深度定义为1024.
验证手段:MATLAB产生正弦函数,经过8bit量化后存储在ROM中,数据经过DAC 电缆 ADC环回到FPGA,ILA抓取ADC接收数据波形,观察对比发送与接收数据是否相近。
三、DDS原理
这里只介绍DDS的基本思想,关于详细原理,请参考:【图文】DDS原理_百度文库 https://wenku.baidu.com/view/11cfbf85a0116c175f0e4818.html
实际上,DDS的核心就是将正弦或余弦函数存储在ROM中,利用相位累加特性通过采用不同的步长对ROM寻址的方式产生频率可调正弦波。另外需要注意产生信号的频率范围要满足奈奎斯特采样定理,该定理支持若想无失真恢复原始信号,采样频率必须大于等于信号最高频率成分的2倍。反过来说:产生信号的最高频率小于等于最高频率成分的1/2.采样频率即为FPGA是时钟频率。为防止信号混叠,一般取最高频率成分的1/3.
四、MATLAB产生正弦序列.coe文件及ROM初始化
MATLAB产生频率为1/2*pi标准正弦序列。验证无误后,在VIVADO中调用Block Memory Generator IP核,配置为单口ROM,使用刚才产生的系数文件初始化ROM地址数据。
五、DAC ADC驱动
该设计使用的DAC ADC均为为低速并口转换芯片,无需配置,只要FPGA给出时钟信号,并输出/入并行数据即可。根据AD9708 datasheet时序图,其在时钟上升沿采样,故FPGA在输出时钟下降沿更新数据可满足建立与保持时间要求。ADC同样上升沿开始更新数据,接收端在时钟是上升沿采集数据,这样每一时钟周期可以采到上一拍送出的数据。
六、函数发生器及测试工程设计
1 `timescale 1ns / 1ps 2 3 module sin_generator#(parameter FCW_W = 16, 4 DAC_W = 8) 5 ( 6 input clk,//DAC采样时钟 由PLL产生 7 input rst_n, 8 9 input [FCW_W-1:0] fcw, 10 output [DAC_W-1:0] dac_data, 11 output dac_clk 12 ); 13 14 reg [ (FCW_W-1):0] sum ; 15 wire [9:0] addra; 16 //reg [9:0] addra;//地址测试信号 17 wire ena; 18 wire [7:0] douta; 19 20 //相位累加器 21 //时钟下降沿产生数据 DAC上升沿采样 22 always @(negedge clk or negedge rst_n )begin 23 if(rst_n==0) begin 24 sum <= (0) ; 25 end 26 else begin 27 sum <= (sum+fcw) ; 28 end 29 end 30 31 assign addra = sum[FCW_W-1-:10]; 32 33 //rom地址测试 34 /*always @(posedge clk or negedge rst_n )begin 35 if(rst_n==0) begin 36 addra <= (0) ; 37 end 38 else begin 39 addra <= (addra+1) ; 40 end 41 end*/ 42 43 44 blk_mem_gen_0 u_bram ( 45 .clka(clk), // input wire clka 46 .ena(ena), // input wire ena 47 .addra(addra), // input wire [9 : 0] addra 48 .douta(douta) // output wire [7 : 0] douta 49 ); 50 assign ena = 1‘b1; 51 //输出信号 52 assign dac_data = douta; 53 assign dac_clk = ~clk; 54 55 endmodule
函数发生器模块由输入端口fcw数值确定频率控制字。测试工程顶层包括差分时钟转单端时钟原语,用于产生DAC ADC时钟的PLL 函数发生器模块,生成特定频率控制字的VIO IP核,还有接收端ADC数据采样逻辑以及ILA 调试IP核。
1 `timescale 1ns / 1ps 2 3 4 module DDS_Demo_top 5 #(parameter AD_DA_W = 8) 6 ( 7 input sys_clk_p, 8 input sys_clk_n, 9 input rst_n, 10 11 output [AD_DA_W-1:0] DAC_data, 12 output DAC_clk, 13 14 input [AD_DA_W-1:0] ADC_data, 15 output ADC_clk 16 ); 17 18 localparam FCW_W = 16; 19 20 wire sys_clk_ibufg; 21 wire clk_dac,clk_adc; 22 reg [ (AD_DA_W-1):0] data_ad ; 23 24 wire [FCW_W-1 : 0] probe_out0; 25 wire [AD_DA_W*2-1:0] probe0; 26 27 //ADC接口信号 28 //ADC在时钟上升沿后送出数据,FPGA下一个上升沿采样 29 assign ADC_clk = clk_adc; 30 31 always @(posedge clk_adc or negedge rst_n )begin 32 if(rst_n==0) begin 33 data_ad <= (0) ; 34 end 35 else begin 36 data_ad <= (ADC_data) ; 37 end 38 end 39 40 41 /***************************************子模块例化***************************************/ 42 IBUFGDS # 43 ( 44 .DIFF_TERM ("FALSE"), 45 .IBUF_LOW_PWR ("FALSE") 46 ) 47 u_ibufg_sys_clk 48 ( 49 .I (sys_clk_p), 50 .IB (sys_clk_n), 51 .O (sys_clk_ibufg) 52 ); 53 54 clk_wiz_0 u_pll 55 ( 56 // Clock out ports 57 .clk_out1(clk_dac), // output clk_out1 58 .clk_out2(clk_adc), // output clk_out2 59 // Status and control signals 60 .resetn(rst_n), // input resetn 61 .locked(), // output locked 62 // Clock in ports 63 .clk_in1(sys_clk_ibufg)); // input clk_in1 64 65 sin_generator#(.FCW_W(FCW_W), 66 .DAC_W(AD_DA_W)) 67 u_sin_gen 68 ( 69 .clk (clk_dac) ,//DAC采样时钟 由PLL产生 70 .rst_n (rst_n) , 71 .fcw (probe_out0) , 72 .dac_data (DAC_data) , 73 .dac_clk (DAC_clk) //由clk_dac产生 74 ); 75 76 //debug cores 77 vio_0 u_vio ( 78 .clk(clk_dac), // input wire clk 79 .probe_out0(probe_out0) // output wire [15 : 0] probe_out0 80 ); 81 82 ila_0 u_ila ( 83 .clk(clk_adc), // input wire clk 84 .probe0(probe0) // input wire [15:0] probe0 85 ); 86 87 assign probe0[7:0] = DAC_data; 88 assign probe0[15:8] = ADC_data; 89 90 endmodule
最后添加引脚约束文件:
1 #################################clock && reset############################################### 2 create_clock -period 5 [get_ports sys_clk_p] 3 set_property PACKAGE_PIN R4 [get_ports {sys_clk_p}] 4 set_property iosTANDARD DIFF_SSTL15 [get_ports {sys_clk_p}] 5 6 set_property PACKAGE_PIN T6 [get_ports rst_n] 7 set_property IOSTANDARD LVCMOS15 [get_ports rst_n] 8 9 #####################DAC PIN connect J4 expansion interface########################## 10 set_property PACKAGE_PIN H14 [get_ports {DAC_clk}] 11 set_property IOSTANDARD LVCMOS33 [get_ports {DAC_clk}] 12 13 set_property PACKAGE_PIN J14 [get_ports {DAC_data[7]}] 14 set_property IOSTANDARD LVCMOS33 [get_ports {DAC_data[7]}] 15 set_property PACKAGE_PIN H15 [get_ports {DAC_data[6]}] 16 set_property IOSTANDARD LVCMOS33 [get_ports {DAC_data[6]}] 17 set_property PACKAGE_PIN J15 [get_ports {DAC_data[5]}] 18 set_property IOSTANDARD LVCMOS33 [get_ports {DAC_data[5]}] 19 set_property PACKAGE_PIN G13 [get_ports {DAC_data[4]}] 20 set_property IOSTANDARD LVCMOS33 [get_ports {DAC_data[4]}] 21 set_property PACKAGE_PIN H13 [get_ports {DAC_data[3]}] 22 set_property IOSTANDARD LVCMOS33 [get_ports {DAC_data[3]}] 23 set_property PACKAGE_PIN J21 [get_ports {DAC_data[2]}] 24 set_property IOSTANDARD LVCMOS33 [get_ports {DAC_data[2]}] 25 set_property PACKAGE_PIN J20 [get_ports {DAC_data[1]}] 26 set_property IOSTANDARD LVCMOS33 [get_ports {DAC_data[1]}] 27 set_property PACKAGE_PIN G16 [get_ports {DAC_data[0]}] 28 set_property IOSTANDARD LVCMOS33 [get_ports {DAC_data[0]}] 29 30 #####################ADC PIN connect J4 expansion interface########################## 31 32 set_property PACKAGE_PIN D22 [get_ports {ADC_clk}] 33 set_property IOSTANDARD LVCMOS33 [get_ports {ADC_clk}] 34 35 set_property PACKAGE_PIN G21 [get_ports {ADC_data[7]}] 36 set_property IOSTANDARD LVCMOS33 [get_ports {ADC_data[7]}] 37 set_property PACKAGE_PIN G22 [get_ports {ADC_data[6]}] 38 set_property IOSTANDARD LVCMOS33 [get_ports {ADC_data[6]}] 39 set_property PACKAGE_PIN H20 [get_ports {ADC_data[5]}] 40 set_property IOSTANDARD LVCMOS33 [get_ports {ADC_data[5]}] 41 set_property PACKAGE_PIN G20 [get_ports {ADC_data[4]}] 42 set_property IOSTANDARD LVCMOS33 [get_ports {ADC_data[4]}] 43 set_property PACKAGE_PIN J22 [get_ports {ADC_data[3]}] 44 set_property IOSTANDARD LVCMOS33 [get_ports {ADC_data[3]}] 45 set_property PACKAGE_PIN H22 [get_ports {ADC_data[2]}] 46 set_property IOSTANDARD LVCMOS33 [get_ports {ADC_data[2]}] 47 set_property PACKAGE_PIN K21 [get_ports {ADC_data[1]}] 48 set_property IOSTANDARD LVCMOS33 [get_ports {ADC_data[1]}] 49 set_property PACKAGE_PIN K22 [get_ports {ADC_data[0]}] 50 set_property IOSTANDARD LVCMOS33 [get_ports {ADC_data[0]}]
七、实验结果分析
依据之前的参数和DDS信号频率公式,所生成正弦函数频率最好在32/2^16~32/3MHZ之内,使用VIO改变频率控制字数值,观察ILA抓取的发送与接收数据模拟形式波形。任意给出三组频率范围内波形,频率依次由低到高。
总体来讲还是比较简单。搭建好DAC ADC环路,后面可以验证些滤波 同步算法,或者做些数字频率计、示波器之类的实用设计。
以上是关于数字信号处理专题——DDS函数发生器环路Demo的主要内容,如果未能解决你的问题,请参考以下文章