草庐IT

数字集成电路仿真实验与设计——基于FPGA的DDS信号源设计

То́карев 2024-07-12 原文

写在之前的话:这是刚结束的课设的实验报告,代码多有参考,希望可以帮到你。(源码在文末)

一、课设目的和要求

1.加深学生对课程所学知识的理解,训练学生提高工程应用能力、设计与调测能力,最终提高分析与解决问题的能力,了解先进的EDA芯片使用方法;
2.理解dds硬件模块电路工作原理;
3.掌握硬件描述语言设计方法;
4.掌握系统调试测量方法,实现输出信号并且频率可调,实现正弦波、三角波、方波等多种信号源输出;
5.运用EDA系统软硬件工具解决工程问题的能力;

二、课设主要软硬件环境

硬件:PC机。
软件:Windows系统平台,装有Quartus、Modelsim等软件编译环境。

三、内容(包括研究背景、研究现状、思路、不同实现方案比较等)

1.研究背景
信号发生器作为一种常用的信号源设备,在测试测量领域有着广泛的应用。其为测试电路提供频率和幅度可调的测试信号,常用于电子、通信产品测试过程中。传统的信号发生器采用模拟电路构成。这种信号发生器的电路十分复杂,功能和精度相对较低,且容易受外界干扰影响。近年来,随着数字电路技术的不断发展,基于直接数字频率合成(direct digital frequency synthesis, DDS)技术的信号发生器逐步产生。DDS技术改变了传统信号的产生方式,以数字量的形式生成模拟信号,利用微控制器进行控制,大大简化了电路结构,并具有较高的灵活性。由DDS芯片构成的信号发生器具有信号种类丰富、频率及幅度调节范围广、信号稳定度高等特点。
2.研究现状
信号发生器又称信号源或振荡器,在生产实践和科技领域中有着广泛的应用。各种波形曲线均可以用三角函数方程式来表示。能够产生多种波形,如三角波、锯齿波、矩形波(含方波)、正弦波的电路被称为函数信号发生器。函数信号发生器在电路实验和设备检测中具有十分广泛的用途。例如在通信、广播、电视系统中,都需要射频(高频)发射,这里的射频波就是载波,把音频(低频)、视频信号或脉冲信号运载出去,就需要能够产生高频的振荡器。在工业、农业、生物医学等领域内,如高频感应加热、熔炼、淬火、超声诊断、核磁共振成像等,都需要功率或大或小、频率或高或低的振荡器。DDS信号发生器采用直接数字频率合成 (Direct Digital Synthesis,简称DDS)技术,把信号发生器的频率稳定度、准确度提高到与基准频率相同的水平,并且可以在很宽的频率范围内进行精细的频率调节。采用这种方法设计的信号源可工作于调制状态,可对输出电平进行调节,也可输出各种波形。一个完整周期的函数波形被存储在上面所示的存储器查找表中。
3.不同实验方案比较
方案一:采用模拟锁相环实现
模拟锁相环技术是项比较成熟的技术。应用模拟锁相环,可将基准频率倍频,或分频得到所需的频率,且调节精度可以做到相当高、稳定性也比较好。但模拟锁相环模拟电路复杂,不易调节,成本较高,并且频率调节不便且调节范围小,输出波形的毛刺较多,得不到满意的效果。
方案二:基于FPGA的dds波形发生器
设计思路:
系统模块设计:

采用直接数字频率合成,用 FPGA 器件作为核心控制部件,精度高稳定性好,得到波形平滑,特别是FPGA的高速度,能实现较高频率的波形。控制上更方便,可得到较宽频率范围的波形输出,步进小,外围电路简单易实现。因此采用方案二。

四、源程序(原理图)与结果讨论

DDS全称为直接数字频率合成(Direct Digital Synthesis),其基本原理是在一个周期波形数据下,通过选取其中全部数据或抽样部分数据组成新的波形,由奈奎斯特采样定理可知,最低两个采样点就可以组成一个波形,但实际上最少需要4个点。其原理框图如下:

其主要由相位控制字、频率控制字、相位累加器、波形存储器几部分组成。
波形存储器:存储一个周期波形的离散信号;
频率控制字:用以控制生成的波形频率;
相位累加器:用来控制波形的相位累加,组成完整的波形显示;
相位控制字:用以控制波形起始位置。

实验代码:

1.DDS模块

module DDS(
	input		clk,
	input		rst_n,
	input		[25:0]	f_word,
	input		[1:0]	wave_c,
	input		[11:0]	p_word,
	input		[4:0]	amplitude,
	output	reg	[11:0]	dac_data	
	);
	localparam	DATA_WIDTH = 4'd12;
	localparam	ADDR_WIDTH = 4'd12;
	reg		[11:0]	addr	 ;
	wire	[11:0]	dac_data0;
	wire	[11:0]	dac_data1;
	wire	[11:0]	dac_data2;
	wire	[11:0]	dac_data3;
	//波形选择
	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			dac_data <= 12'd0;
		end
		else begin
			case(wave_c)
				2'b00:dac_data <= dac_data0/amplitude;	//正弦波
				2'b01:dac_data <= dac_data1/amplitude;	//三角波
				2'b10:dac_data <= dac_data2/amplitude;	//锯齿波
				2'b11:dac_data <= dac_data3/amplitude;	//方波
				default:;
			endcase
		end
	end
	//相位累加器
	reg	[31:0]	fre_acc;
	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			fre_acc <= 0;
		end
		else begin
			fre_acc <= fre_acc + f_word;
		end
	end
	//生成查找表地址
	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			addr <= 0;
		end
		else begin
			addr <= fre_acc[31:20] + p_word;
		end
	end
	//正弦波
	sin_rom #(
		.DATA_WIDTH(DATA_WIDTH),
		.ADDR_WIDTH(ADDR_WIDTH)
	) inst_sin_rom (
		.addr (addr),
		.clk  (clk),
		.q    (dac_data0)
	);
	//三角波
	sanjiao_rom #(
			.DATA_WIDTH(DATA_WIDTH),
			.ADDR_WIDTH(ADDR_WIDTH)
		) inst_sanjiao_rom (
			.addr (addr),
			.clk  (clk),
			.q    (dac_data1)
		);
	//锯齿波
	juchi_rom #(
			.DATA_WIDTH(DATA_WIDTH),
			.ADDR_WIDTH(ADDR_WIDTH)
		) inst_juchi_rom (
			.addr (addr),
			.clk  (clk),
			.q    (dac_data2)
		);
	//方波
	fangbo_rom #(
			.DATA_WIDTH(DATA_WIDTH),
			.ADDR_WIDTH(ADDR_WIDTH)
		) inst_fangbo_rom (
			.addr (addr),
			.clk  (clk),
			.q    (dac_data3)
		);
endmodule

2.频率设置模块

module F_word_set(
	input				clk		,
	input				rst_n	,
	input				key1_in	,

	output	reg	[25:0]	f_word	
	);
	
	wire		key_flag	;
	wire		key_state	;
	reg	[3:0]	cnt			;

	key_filter fword_key (
			.clk       (clk),
			.rst_n     (rst_n),
			.key_in    (key1_in),
			.key_flag  (key_flag),
			.key_state (key_state)
		);

	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			cnt <= 4'd0;
		end
		else if (key_flag) begin
			if (cnt==4'd10) begin
				cnt <= 4'd0;
			end
			else begin
				cnt <= cnt + 1'b1;
			end
		end
	end

	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			f_word <= 0;
		end
		else begin
			case(cnt)                     //x=(2^32)*20/T=(2^32)*20*f(hz)/1000000000
				4'd0:f_word = 26'd86;		//1Hz
				4'd1:f_word = 26'd859;		//10Hz
				4'd2:f_word = 26'd8590;		//100Hz
				4'd3:f_word = 26'd42950;	//500Hz
				4'd4:f_word = 26'd85899;	//1kHz
				4'd5:f_word = 26'd429497;	//5kHz
				4'd6:f_word = 26'd858993;	//10kHz
				4'd7:f_word = 26'd4294967;	//50kHz
				4'd8:f_word = 26'd8589935;	//100kHz
				4'd9:f_word = 26'd17179869;	//200kHz
				4'd10:f_word = 26'd42949673;//500kHz
				default:;
			endcase
		end
	end
endmodule

3.按键模块

module key_filter(
    input                   clk         ,//系统时钟50MHz
    input                   rst_n       ,//系统复位
    input                   key_in      ,//按键输入
    output   reg            key_flag    ,//输出一个脉冲按键有效信号
    output   reg            key_state    //输出按键状态,1为未按下,0为按下
);
parameter IDLE      = 4'b0001      ;//空闲状态,读取按键按下的下降沿,读取到下降沿转到下一个状态
parameter FILTER1   = 4'b0010      ;//计数20ms状态,计数结束转到下一个状态
parameter STABLE    = 4'b0100      ;//数据稳定状态,等待按键松开上升沿,读取到上升沿转到下一个状态
parameter FILTER2   = 4'b1000      ;//计数20ms状态,计数结束转到空闲状态
parameter TIME_20MS = 20'd1000_000 ;
reg   [  3: 0]         state_c      ;//寄存器改变状态
reg   [  3: 0]         state_n      ;//现在状态
wire                   IDLE_to_FILTER1  ;//IDLE状态转到FILTER1状态条件
wire                   FILTER1_to_STABLE;//FILTER1状态转到STABLE状态条件
wire                   STABLE_to_FILTER2;//STABLE状态转到FILTER2状态条件
wire                   FILTER2_to_IDLE  ;//FILTER2状态转到IDLE状态条件
reg                    key_in_ff0   ;
reg                    key_in_ff1   ;
reg                    key_in_ff2   ;
wire                   key_in_pos   ;//检测上升沿标志
wire                   key_in_neg   ;//检测下降沿标志 
reg   [ 19: 0]         cnt          ;
wire                   add_cnt      ;
wire                   end_cnt      ;
//状态机第一段,状态转换
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        state_c <= IDLE;
    end
    else begin
        state_c <= state_n;
    end
end
//状态机第二段,状态转换条件
always  @(*)begin
    case(state_c)
        IDLE   :begin
                    if(IDLE_to_FILTER1)
                        state_n = FILTER1;
                    else
                        state_n = state_c;
                end
        FILTER1:begin
                    if(FILTER1_to_STABLE)
                        state_n = STABLE;
                    else
                        state_n = state_c;
                end
        STABLE :begin
                    if(STABLE_to_FILTER2)
                        state_n = FILTER2;
                    else
                        state_n = state_c;
                end
        FILTER2:begin
                    if(FILTER2_to_IDLE)
                        state_n = IDLE;
                    else
                        state_n = state_c;
                end
        default:state_n = IDLE;
    endcase
end 
//状态转换条件
assign IDLE_to_FILTER1   = key_in_neg   ;
assign FILTER1_to_STABLE = state_c==FILTER1 && end_cnt;
assign STABLE_to_FILTER2 = key_in_pos   ;
assign FILTER2_to_IDLE   = state_c==FILTER2 && end_cnt;
//打两拍,防止亚稳态
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        key_in_ff0 <= 1;
        key_in_ff1 <= 1;
        key_in_ff2 <= 1;
    end
    else begin
        key_in_ff0 <= key_in;
        key_in_ff1 <= key_in_ff0;
        key_in_ff2 <= key_in_ff1;
    end
end
//下降沿和上升沿检测
assign key_in_pos = (state_c==STABLE) ?(key_in_ff1 && !key_in_ff2):1'b0;
assign key_in_neg = (state_c==IDLE) ?(!key_in_ff1 && key_in_ff2):1'b0;
//计数20ms
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt <= 0;
    end
    else if(add_cnt)begin
        if(end_cnt)
            cnt <= 0;
        else
            cnt <= cnt + 1'b1;
    end
    else begin
        cnt <= 0;
    end
end
assign add_cnt = state_c==FILTER1 || state_c==FILTER2;       
assign end_cnt = add_cnt && cnt== TIME_20MS-1;
//key_flag按键按下输出一个脉冲信号
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        key_flag <= 0;
    end
    else if(state_c==FILTER1 && end_cnt) begin
        key_flag <= 1;
    end
    else begin
        key_flag <= 0;
    end
end
//key_state按键按下状态信号
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        key_state <= 1;
    end
    else if(state_c==STABLE || state_c==FILTER2) begin
        key_state <= 0;
    end
    else begin
        key_state <= 1;
    end
end
endmodule

4.顶层模块

module DDS_top(
	input					clk			,
	input					rst_n		,
	input					key0_in		,
	input					key1_in		,
	input					key2_in		,
	output	wire	[11:0]	dac_data	
	);
	wire	[1:0]	wave_c		;
	wire	[25:0]	f_word		;
	wire	[4:0]	amplitude	;
	DDS inst_DDS
	(
		.clk      (clk),
		.rst_n    (rst_n),
		.f_word   (f_word),
		.wave_c   (wave_c),
		.p_word   (12'd0),
		.amplitude(amplitude),
		.dac_data (dac_data)
	);
	F_word_set inst_F_word_set 
	(
		.clk(clk), 
		.rst_n(rst_n), 
		.key1_in(key1_in), 
		.f_word(f_word)
	);
	wave_set inst_wave_set 
	(
		.clk(clk), 
		.rst_n(rst_n), 
		.key0_in(key0_in), 
		.wave_c(wave_c)
	);
	amplitude_set inst_amplitude_set(
		.clk	(clk)		,
		.rst_n	(rst_n)		,
		.key2_in(key2_in)	,
		.amplitude(amplitude)	
	);
endmodule

5.结果与讨论
编译结果:

仿真结果:
正弦波:

三角波:

锯齿波:

方波:

由图可知,按键改变后,各个波形的频率也随之改变,可见设计成功。

报告里只贴了部分代码,这里是我上传的源码,可以直接运行通过。
链接:https://pan.baidu.com/s/1DzySQriSSNpIZ5H0rfgVgQ
提取码:b7mu
–来自百度网盘超级会员V5的分享

有关数字集成电路仿真实验与设计——基于FPGA的DDS信号源设计的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  3. ruby - 查找字符串中的内容类型(数字、日期、时间、字符串等) - 2

    我正在尝试解析一个CSV文件并使用SQL命令自动为其创建一个表。CSV中的第一行给出了列标题。但我需要推断每个列的类型。Ruby中是否有任何函数可以找到每个字段中内容的类型。例如,CSV行:"12012","Test","1233.22","12:21:22","10/10/2009"应该产生像这样的类型['integer','string','float','time','date']谢谢! 最佳答案 require'time'defto_something(str)if(num=Integer(str)rescueFloat(s

  4. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

    导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

  5. 区块链之加解密算法&数字证书 - 2

    目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非

  6. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  7. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  8. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  9. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

  10. ruby-on-rails - 设计注册确认 - 2

    我在我的项目中有一个用户和一个管理员角色。我使用Devise创建了身份验证。在我的管理员角色中,我没有任何确认。在我的用户模型中,我有以下内容:devise:database_authenticatable,:confirmable,:recoverable,:rememberable,:trackable,:validatable,:timeoutable,:registerable#Setupaccessible(orprotected)attributesforyourmodelattr_accessible:email,:username,:prename,:surname,:

随机推荐