FPGA基础之数字信号发生器(DDS)
一、DDS原理
最近在复习至芯培训课程的DDS部分,DDS的核心主要包括频率发生器、相位累加器和波形存储器三部分。波形存储器的作用是存储不同相位对应地址的波形值,相位累加器根据不同的相位产生对应的地址,用于选择不同的波形值,频率发生器根据输入频率产生频率控制字,其大小决定了相位累加器地址累加的速度,不同的累加速度决定了输出不同的频率大小。
二、本工程的相关说明
实现的功能如下:输入有四个按键,分别对应波形选择、幅度调节、频率加和频率减;输出有蜂鸣器、6个动态数码管和输出波形d_wave。当四个按键有任意一个按下时,蜂鸣器响,数码管显示波形、幅度和频率对应的值,d_dds模块输出对应波形。
1、顶层设计
顶层dds模块包括以下几个部分:按键处理模块(key_ctrl)、脉冲处理模块(flag_ctrl)、信号发生器核心部分(d_dds)、蜂鸣器模块(beep)和6个动态数码管显示模块。顶层RTL视图如下。
dds顶层代码如下:
module dds(input wire clk,input wire rst_n,input wire key_sel,input wire key_amp,input wire key_fadd,input wire key_fsub,output wire beep,output wire [9:0] d_wave,output wire [2:0] sel,output wire [7:0] seg
);wire flag_s;wire flag_a;wire flag_fa;wire flag_fs;wire [1:0] wave_sel;wire [1:0] wave_a;wire [19:0] wave_freq;key_ctrl key_ctrl_inst(.clk (clk ),.rst_n (rst_n ),.key_sel (key_sel ),.key_amp (key_amp ),.key_fadd (key_fadd),.key_fsub (key_fsub),.flag_s (flag_s ),.flag_a (flag_a ),.flag_fa (flag_fa ),.flag_fs (flag_fs ));flag_ctrl flag_ctrl_inst(.clk (clk ),.rst_n (rst_n ),.flag_s (flag_s ),.flag_a (flag_a ),.flag_fa (flag_fa ),.flag_fs (flag_fs ),.wave_sel (wave_sel ),.wave_a (wave_a ),.wave_freq (wave_freq ));d_dds d_dds_inst(.clk (clk ),.rst_n (rst_n ),.wave_sel (wave_sel ),.wave_a (wave_a ),.wave_freq (wave_freq ),.d_wave (d_wave )); beep beep_inst(.clk (clk ),.rst_n (rst_n ),.flag_s (flag_s ),.flag_a (flag_a ),.flag_fa (flag_fa ),.flag_fs (flag_fs ),.beep (beep )); show_data show_data_inst(.clk (clk ),.rst_n (rst_n ),.wave_sel (wave_sel ),.wave_a (wave_a ),.wave_freq (wave_freq ),.sel (sel ),.seg (seg )); endmodule
2、各模块相关说明
(1)按键处理模块(key_ctrl)
按键处理包括两个步骤,消抖和边缘检测,需要注意的点是需要对每一个按键进行这两步操作,生成对应的四个脉冲信号:波形选择、幅度调节、频率加和减。顶层代码如下:
module key_ctrl(input wire clk,input wire rst_n,input wire key_sel,input wire key_amp,input wire key_fadd,input wire key_fsub,output wire flag_s,output wire flag_a,output wire flag_fa,output wire flag_fs
);wire key_wave1;wire key_wave2;wire key_wave3;wire key_wave4;key_filter key_filter_inst1(.clk (clk),.rst_n (rst_n),.key (key_sel),.key_wave (key_wave1));edge_check edge_check_inst1(.clk (clk),.rst_n (rst_n),.key (key_wave1),.flag_neg (flag_s),.flag_pos ());key_filter key_filter_inst2(.clk (clk),.rst_n (rst_n),.key (key_amp),.key_wave (key_wave2));edge_check edge_check_inst2(.clk (clk),.rst_n (rst_n),.key (key_wave2),.flag_neg (flag_a),.flag_pos ());key_filter key_filter_inst3(.clk (clk),.rst_n (rst_n),.key (key_fadd),.key_wave (key_wave3));edge_check edge_check_inst3(.clk (clk),.rst_n (rst_n),.key (key_wave3),.flag_neg (flag_fa),.flag_pos ());key_filter key_filter_inst4(.clk (clk),.rst_n (rst_n),.key (key_fsub),.key_wave (key_wave4));edge_check edge_check_inst4(.clk (clk),.rst_n (rst_n),.key (key_wave4),.flag_neg (flag_fs),.flag_pos ());endmodule
(2)脉冲处理模块(flag_ctrl)
本模块根据四个按键产生的脉冲信号,输出波形选择、幅度和频率的控制信号。需要注意的点是幅度和频率变化只能在选中某一波形的前提条件下进行变化,当切换成其他波形时,要保持上一个波形的设定值不变,初始频率设为50k,按键对应频率加减对应为100Hz。代码如下:
module flag_ctrl(input wire clk,input wire rst_n,input wire flag_s,input wire flag_a,input wire flag_fa,input wire flag_fs,output reg [1:0] wave_sel,output reg [1:0] wave_a,output reg [19:0] wave_freq
);parameter FREQ = 50_000;reg [1:0] amp_1;reg [1:0] amp_2;reg [1:0] amp_3; reg [1:0] amp_4;reg [19:0] freq1;reg [19:0] freq2;reg [19:0] freq3;reg [19:0] freq4; always @ (posedge clk, negedge rst_n)beginif (!rst_n)wave_sel <= 0;elseif (flag_s == 1)wave_sel <= wave_sel + 1'b1;elsewave_sel <= wave_sel;end//===================== according to wave_sel, adjust amplitude ===========================always @ (posedge clk, negedge rst_n)beginif (!rst_n)amp_1 <= 0;elseif (wave_sel == 2'b00 && flag_a == 1)amp_1 <= amp_1 + 1'b1;elseamp_1 <= amp_1;endalways @ (posedge clk, negedge rst_n)beginif (!rst_n)amp_2 <= 0;elseif (wave_sel == 2'b01 && flag_a == 1)amp_2 <= amp_2 + 1'b1;elseamp_2 <= amp_2;endalways @ (posedge clk, negedge rst_n)beginif (!rst_n)amp_3 <= 0;elseif (wave_sel == 2'b10 && flag_a == 1)amp_3 <= amp_3 + 1'b1;elseamp_3 <= amp_3;endalways @ (posedge clk, negedge rst_n)beginif (!rst_n)amp_4 <= 0;elseif (wave_sel == 2'b11 && flag_a == 1)amp_4 <= amp_4 + 1'b1;elseamp_1 <= amp_1;end//============== according to wave_sel, adjust frequency ==================================always @ (posedge clk, negedge rst_n)beginif (!rst_n)freq1 <= FREQ;elseif (wave_sel == 2'b00 && flag_fa == 1)freq1 <= freq1 + 20'd100;elseif (wave_sel == 2'b00 && flag_fs == 1)freq1 <= freq1 - 20'd100;elsefreq1 <= freq1;endalways @ (posedge clk, negedge rst_n)beginif (!rst_n)freq2 <= FREQ;elseif (wave_sel == 2'b01 && flag_fa == 1)freq2 <= freq2 + 20'd100;elseif (wave_sel == 2'b01 && flag_fs == 1)freq2 <= freq2 - 20'd100;elsefreq2 <= freq2;endalways @ (posedge clk, negedge rst_n)beginif (!rst_n)freq3 <= FREQ;elseif (wave_sel == 2'b10 && flag_fa == 1)freq3 <= freq3 + 20'd100;elseif (wave_sel == 2'b10 && flag_fs == 1)freq3 <= freq3 - 20'd100;elsefreq3 <= freq3;endalways @ (posedge clk, negedge rst_n)beginif (!rst_n)freq4 <= FREQ;elseif (wave_sel == 2'b11 && flag_fa == 1)freq4 <= freq4 + 20'd100;elseif (wave_sel == 2'b11 && flag_fs == 1)freq4 <= freq4 - 20'd100;elsefreq4 <= freq4;end//=================== according wave_sel, output frequency and amplitude ==========================always @ (*)case (wave_sel)2'b00 : begin wave_freq = freq1; wave_a = amp_1; end2'b01 : begin wave_freq = freq2; wave_a = amp_2; end2'b10 : begin wave_freq = freq3; wave_a = amp_3; end2'b11 : begin wave_freq = freq4; wave_a = amp_4; endendcaseendmodule
(3)DDS核心模块
本模块包括四个部分,频率发生器、相位累加器、波形存储器和幅度调节器。需要注意的点:频率发生器的控制字word的位宽为什么要是64位,相位累加器的基地址和偏移地址的处理,波形存储器模块在rom中写入.mif文件时特别注意文件路径,应放在工程文件夹目录下,幅度调节器部分应注意对所成的幅值进行+1处理。顶层代码如下:
module d_dds(input wire clk,input wire rst_n,input wire [1:0] wave_sel,input wire [1:0] wave_a,input wire [19:0] wave_freq,output wire [9:0] d_wave
);wire [31:0] f_word;wire [9:0] addr_rom1024x8;wire [7:0] wave;freq_ctrl freq_ctrl_inst(.clk (clk),.rst_n (rst_n),.wave_freq (wave_freq),.f_word (f_word));addr_ctrl addr_ctrl_inst(.clk (clk),.rst_n (rst_n),.wave_sel (wave_sel),.f_word (f_word),.addr (addr_rom1024x8));rom1024x8 rom1024x8_inst (.address (addr_rom1024x8),.clock (clk),.q (wave));a_ctrl a_ctrl_inst(.clk (clk),.rst_n (rst_n),.wave_a (wave_a),.wave (wave),.d_wave (d_wave));endmodule
(4)蜂鸣器模块
此模块需要明确的是给什么样的信号蜂鸣器才能发声,本设计使用的蜂鸣器是无源蜂鸣器,给连续高低变化的信号就能发生,当然信号频率有一个范围。需要注意的是按键脉冲信号到来,100ms计数器的启动控制信号的条件是什么,代码如下:
module beep(
input wire clk,
input wire rst_n,input wire flag_s,
input wire flag_a,
input wire flag_fa,
input wire flag_fs,output reg beep
);
parameter T = 10_000;
parameter MAX = 5_000_000;reg [15:0] cnt_10k;
reg wave_10k;
reg [22:0] cnt_100ms;wire wave_100ms;
wire flag_or;assign flag_or = (flag_s == 1 || flag_a == 1 || flag_fa == 1 || flag_fs == 1) ? 1'b1 : 1'b0;
//=================== generate 10k wave ===========================================================
always @ (posedge clk, negedge rst_n)beginif (!rst_n)cnt_10k <= 0;elseif (cnt_10k < T - 1)cnt_10k <= cnt_10k + 1'b1;elsecnt_10k <= 0;endalways @ (posedge clk, negedge rst_n)beginif (!rst_n)wave_10k <= 0;elseif (cnt_10k < T / 2)wave_10k <= 1'b1;elsewave_10k <= 1'b0;end
//================== generate wave_100ms ==========================================================
always @ (posedge clk, negedge rst_n)beginif (!rst_n)cnt_100ms <= MAX - 1;elseif ((cnt_100ms == MAX - 1) && flag_or == 1)cnt_100ms <= 0;elseif (cnt_100ms < MAX - 1)cnt_100ms <= cnt_100ms + 1'b1;elsecnt_100ms <= cnt_100ms;endassign wave_100ms = (cnt_100ms != MAX - 1) ? 1'b1 : 1'b0;
//================= generate beep ===============================================================
always @ (posedge clk, negedge rst_n)beginif (!rst_n)beep <= 1;elsebeep <= ~ (wave_10k && wave_100ms);end
endmodule
(5)6个动态数码管显示模块
前两个数码管分别显示波形选择和幅度,后四个显示频率值。特别需要注意的是波形选择和幅度信号的位宽,以及频率对应需要bin2bcd模块。代码如下:
module show_data(input wire clk,input wire rst_n,input wire [1:0] wave_sel,input wire [1:0] wave_a,input wire [19:0] wave_freq,output wire [2:0] sel,output wire [7:0] seg
);wire [3:0] b_wave_sel;wire [3:0] b_wave_a;wire [27:0] bcd;wire [23:0] show_data;assign b_wave_sel = wave_sel + 1'b1;assign b_wave_a = wave_a + 1'b1; assign show_data = {b_wave_sel, b_wave_a, bcd[23:8]};bin2bcd # (.WIDTH_BIN(20), .WIDTH_BCD(28)) bin2bcd_inst(.bin (wave_freq),.bcd (bcd));seven_tube_drive seven_tube_drive_inst(.clk (clk),.rst_n (rst_n),.show_data (show_data),.seven_tube_seg (seg),.seven_tube_sel (sel));endmodule
3、测试文件tb
这个工程不建议对所有模块进行仿真,因为子模块太多,提供一个简单的,不足之处请谅解。代码如下:
`timescale 1ns/1psmodule dds_tb;reg clk;reg rst_n;reg key_sel;reg key_amp;reg key_fadd;reg key_fsub;wire beep;wire [9:0] d_wave;wire [2:0] sel;wire [7:0] seg;wire [7:0] addr = dds_inst.d_dds_inst.addr_ctrl_inst.p_addr;defparam dds_inst.key_ctrl_inst.key_filter_inst1.T_5ms = 10;defparam dds_inst.key_ctrl_inst.key_filter_inst2.T_5ms = 10;defparam dds_inst.key_ctrl_inst.key_filter_inst3.T_5ms = 10; defparam dds_inst.key_ctrl_inst.key_filter_inst4.T_5ms = 10;defparam dds_inst.beep_inst.T = 10;defparam dds_inst.beep_inst.MAX = 50; dds dds_inst(.clk (clk ),.rst_n (rst_n ),.key_sel (key_sel ),.key_amp (key_amp ),.key_fadd (key_fadd ),.key_fsub (key_fsub ),.beep (beep ),.d_wave (d_wave ),.sel (sel ),.seg (seg ));initial clk = 1;always #10 clk = ~ clk;initial beginrst_n = 0;key_sel = 1;key_amp = 1;key_fadd = 1;key_fsub = 1;# 201rst_n = 1;task_stop;key_sel = 0;task_stop;key_amp = 0;task_stop;key_fadd = 0; task_stop;key_fsub = 0;task_stop;$stop;endtask task_stop;beginrepeat (10)beginwhile (addr != 255) @ (posedge clk);while (addr != 0) @ (posedge clk);endendendtask endmodule
三、个人总结
本人做得还较为顺利,希望给初学者提供一个参考,详细代码见附件。
FPGA基础之数字信号发生器(DDS)相关推荐
- 基于FPGA的高精度数字信号发生器
溪云初起日沉阁,山雨欲来风满楼. sdyang@chd.edu.cn 研究成果 1 研究目标 2 硬件平台 3 实验现象 项目原理及设计过程 1 DDS原理与实现 1. 研究成果 1.1 研究目标 1 ...
- 基于 MATLAB 的数字信号发生器
摘 要:数字信号发生器是基于软硬件实现的一种波形发生仪器.在工工程实践中需要检测和分析的各种复杂信号均可分解成各简单信号之和,而这些简单信号皆可由数字信号发生器模拟产生,因此它在工程分析和实验教学有着 ...
- FPGA基础入门【3】Blink逻辑及仿真
从这一篇开始正式介绍FPGA中的硬件逻辑,第一个目标就是从零开始在NEXYS 4开发板上实现闪烁LED. 软件编程中hello world是初学语言中实现的第一个功能,而硬件编程中blink是同等的地 ...
- 用matlab编程简单电子琴,基于MATLAB的数字信号发生器及简易电子琴设计论文.doc...
基于MATLAB的数字信号发生器及简易电子琴设计论文 基于MATLAB的数字信号发生器及简易电子琴设计 摘 要 数字信号发生器是一种基于软硬件结合实现的函数波形产生仪器.在工程实践中需要检测和分析的各 ...
- (54)FPGA基础编码D触发器(三)
(54)FPGA基础编码D触发器(三) 1 文章目录 1)文章目录 2)FPGA入门与提升课程介绍 3)FPGA简介 4)FPGA基础编码D触发器(三) 5)技术交流 6)参考资料 2 FPGA入门与 ...
- (53)FPGA基础编码D触发器(二)
(53)FPGA基础编码D触发器(二) 1 文章目录 1)文章目录 2)FPGA入门与提升课程介绍 3)FPGA简介 4)FPGA基础编码D触发器(二) 5)技术交流 6)参考资料 2 FPGA入门与 ...
- (52)FPGA基础编码D触发器(一)
(52)FPGA基础编码D触发器(一) 1 文章目录 1)文章目录 2)FPGA入门与提升课程介绍 3)FPGA简介 4)FPGA基础编码D触发器(一) 5)技术交流 6)参考资料 2 FPGA入门与 ...
- FPGA基础入门篇(四) 边沿检测电路
FPGA基础入门篇(四)--边沿检测电路 一.边沿检测 边沿检测,就是检测输入信号,或者FPGA内部逻辑信号的跳变,即上升沿或者下降沿的检测.在检测到所需要的边沿后产生一个高电平的脉冲.这在FPGA电 ...
- FPGA基础资源之IOB的应用
FPGA基础资源之IOB的应用 1.应用背景 在我们做时序约束时,有时候需要对FPGA驱动的外围器件进行input_delay/output_delay进行约束.不知道,大家有没有被以下这种类似的现象 ...
最新文章
- oracle查看数据库字符编码,oracle 查看、批改字符集编码
- android h5状态栏消息,安卓实现系统状态栏的隐藏和显示
- 代码逻辑是分方法写好 还是在一个方法写好_这一团糟的代码,真的是我写的吗?...
- Mysql格式化日期时间
- 【知识星球】模型量化从1bit到8bit,二值到三值
- html 下拉列表美化,JS+CSS实现美化的下拉列表框效果
- python实现倒n字形排列_Leetcode问题库——Z形变换(Python),补充X形变换,字形
- 分享一个不错的表格样式
- python webviewer爬虫_爬虫再也不怕检测浏览器环境了
- 婧婧音乐开发笔记01篇-项目组织结构和布局文件
- bzoj3551 [ONTAK2010]Peaks加强版 kruskal重构树
- Linux基础——Linux 基本指令 touch, cp 和 mv
- 数据结构上机实践第九周项目3 - 利用二叉树遍历思想解决问题
- linux shell 十进制转十六进制_问1得10:shell十进制转换到任意进制,多大事儿,一行搞定!
- oracle mysql数据库管理工具下载_Oracle数据库管理工具PC版-Oracle数据库管理工具下载v15.0.21.0(32/64)-IE浏览器中文网站...
- How fast is a C++ extension by the PHP-CPP liberary?
- PXE配置-Tftpd64
- 毕业设计 : 基于stm32的微信天气机器人 - 天气检测 环境监测 微信机器人
- 清晰理解precision(精确度)和recall(召回度)
- android obb在哪,obb是什么文件 obb文件怎么用