600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > FPGA综合系统设计(四):串口控制的DDS信号发生器

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

时间:2023-05-10 17:08:14

相关推荐

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采样速率等于系统时钟,50MHzassign 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 beginctrl_data[ctrli] <= rxdata;ctrli <= ctrli + 1'b1;end*/// 使用仿真时always @ (posedge clk_da) beginctrl_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-511reg [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;elsecase (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) beginphase_h <= 'd0; //初相高8bitphase_l <= 'd0; //初相低8bitphase <= 'd0; //组合为16bit的初相控制字 endelse beginphase_h <= ctrl_data[4] << 8; //初相高8bitphase_l <= ctrl_data[5]; //初相低8bitphase <= 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) beginfreq_h <= 'd0; //频率高8bitfreq_l <= 'd0; //频率低8bitfreq <= 'd0; //组合为16bit的频率控制字 endelse beginfreq_h <= ctrl_data[1] << 8; //频率高8bitfreq_l <= ctrl_data[2]; //频率低8bitfreq <= 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;elsedc_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,已在开发板上验证):/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信号发生器感兴趣的同学可以结合博主的工程,网上下载文中提到的这几篇论文,相信不难理解。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。