FPGA学习(五)——DDS信号发生器设计

news2025/4/21 18:23:41

FPGA学习(五)——DDS信号发生器设计

目录

  • FPGA学习(五)——DDS信号发生器设计
    • 一、FPGA开发中常用IP核——ROM/RAM/FIFO
      • 1、ROM简介
      • 2、ROM文件的设置
        • (1)直接编辑法
        • (2)用C语言等软件生成初始化文件
      • 3、ROM IP核配置调用
    • 二、DDS信号发生器设计
      • 1、相位累加器设计
      • 2、波形存储器ROM设计
        • (1)方波模块
        • (2)正弦波形存储器模块
      • 3、锁相环倍频电路设计
      • 4、顶层电路设计
      • 5、最终实现
      • 6、仿真波形

一、FPGA开发中常用IP核——ROM/RAM/FIFO

常见的FPGA存储器有3种,RAM(随机访问内存)ROM(只读存储器)FIFO(先入先出)

这三种存储器的区别如下

其中RAM通常都是在掉电之后就丢失数据,ROM在系统停止供电的时候仍然可以保持数据

可以向RAM和ROM中的任意位置写入数据,也可以读取任意的位置的数据

FIFO的数据先入先出,先进去的数据先出来,只能顺序写入数据,顺序的读出数据,其数据地址由内部读写指针自动加1完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定的地址。

这三种存储器的应用场合

RAM和ROM常用于存储指令或者中间的数据

FIFO常用于数据传输通道中用于缓存数据,避免数据丢失,如不同速率时钟模块间的数据传输就需要用到异步FIFO

1、ROM简介

​ ROM 是只读存储器(Read-Only Memory)的简称,是一种只能读出事先所存数据的固态半导体存储器。其特性是一旦储存资料就无法再将之改变或删除,且资料不会因为电源关闭而消失。而事 实上在 FPGA 中通过 IP 核生成的 ROM 或 RAM(RAM 将在下一节为大家讲解)调用的都是FPGA 内部的 RAM 资源,掉电内容都会丢失(这也很容易解释,FPGA 芯片内部本来就没有掉电非易失存储器单元)。用 IP 核生成的 ROM 模块只是提前添加了数据文件(.coe 格式)(.mif/.hex格式),在 FPGA 运行时通过数据文件给 ROM 模块初始化,才使得 ROM 模块像个“真正”的掉电非易失存储器;也正是这个原因,ROM 模块的内容必须提前在数据文件中写死,无法在电路中修改。

最简单的使用有效时钟CLKA、有效地址ADDRA和有效使能EA,就可以输出DOUTA

单端口ROM:只提供一个独立的地址端口核一个读数据端口

image-20250420092325595

双端口ROM:有两个地址端口,和两个读数据端口

image-20250420092337748

2、ROM文件的设置

(1)直接编辑法

image-20250420092750197

image-20250420092853439

image-20250420093128626

在Edit找到Custom Fill Cells

image-20250420093900022

设置完成后,将文件保存,然后用编辑器打开看一下

image-20250420093933152

image-20250420093952032

(2)用C语言等软件生成初始化文件

当需要初始化的存储单元变多时,上述手工输入数据的方法就不太实用了。在了解mif文件的格式的基础上,可以自己编写C语言程序或者使用MATLAB程序自动生成mif文件。

image-20250420101818222

以下是产生128X8位正弦波形数据的C语言源程序 :

#include<stdio.h>
#include<math.h>
#define PI 3.141592
#define DEPTH 128 //数据深度,即存储单元的个数
#define WIDTH 8 //存储单元的宽度
int main(void)
{ 
int n, temp;
float v;
FILE *fp;
/*建立名为Data_sine.mif的新文件,允许写入数据*/
fp= fopen("Data_sine.mif","w+");
if(NULL==fp)
printf("Can not creat file!\r\n");
else
{printf("File created successfully\n");
/*生成文件头,注意不要忘了分号*/

fprintf (fp, "DEPTH = %d;\n ",DEPTH) ;
fprintf (fp,"WIDTH = %d;\n",WIDTH);
fprintf (fp,"ADDRESS_RADIX = HEX;\n");
fprintf(fp, "DATA_RADIX = HEX;\n");
fprintf(fp,"CONTENT\n");
fprintf (fp," BEGIN\n");
/*以十六进制输出地址和数据 */
for (n=0;n<DEPTH;n++)
{
/*周期为128个点的正弦波*/
v= sin(2*PI*n/DEPTH);
/*将-1~1之间的正弦波的值扩展到0~255之间*/
temp=(int)((v+1)*255/2);//v+1将数值平移到lO~2之间
/*以十六进制形式输出地址和数据*/
fprintf(fp, "%x\t:\t%x;\n",n, temp);
}
fprintf(fp,"END;\n");
fclose(fp);
}
}

编辑上述代码后生成myimf.exe文件,在资源管理器中双击运行exe文件,生成Data_sine.mif文件

image-20250420103042129

image-20250420103342471

3、ROM IP核配置调用

在Tools->IP Catalog->搜索ROM

image-20250420085753544

1-PORT单端口,2-PORT双端口,此次以单端口为例

image-20250420103637506

How wide should the ‘q’ output bus be?

How many x-bit words of memory?

配置ROM的位宽和深度

What should the memory block type be?

使用什么类型的内存

在官方资料中,有提及什么类型的芯片适合用什么类型的内存

image-20250420091525942

寄存器时钟和复位信号的设置,设置输出端q直接输出,不通过寄存器,设置时钟使能端(clken)

image-20250420103659046

Create one dock enable signal for each dock signal. Note: All registered ports are controlled by the enable signal(s)

是否创建一个时钟使能信号

Create an 'adr’asynchronous dear for the registered ports

是否创建一个清零信号

存储器初始化,并指明初始化ROM的数据文件

image-20250420103853939

使用mif文件对ROM进行初始化,文件名为黑色则为添加成功,红色则为失败

image-20250420094801226

ROM子模块参数设置完成后,为了测试其功能,新建一个顶层文件进行功能测试。代码如下:

module Sine_Signal(q_sig,address_sig,clock_sig,clken_sig);

	output [7:0] q_sig;
	output [7:0]  address_sig;
	input clken_sig;
	input clock_sig;
	
IPROM	IPROM_inst (
	.address ( address_sig ),
	.clken ( clken_sig ),
	.clock ( clock_sig ),
	.q ( q_sig )
	);


endmodule

注意实例化部分应该与生成的_inst.v文件内容一致。

image-20250420104345602

仿真波形如下:

image-20250420105902203

二、DDS信号发生器设计

DDS是直接数字式频率合成器(Direct Digital Synthesizer)的英文缩写,是一项关键的数字化技术。与传统的频率合成器相比,DDS具有低成本、低功耗、高分辨率和快速转换时间等优点,广泛使用在电信与电子仪器领域,是实现设备全数字化的一个关键技术。作为设计人员,我们习惯称它为信号发生器,一般用它产生正弦、锯齿、方波等不同波形或不同频率的信号波形,在电子设计和测试中得到广泛应用。
DDS的基本结构主要由相位累加器、相位调制器、波形数据表ROM、D/A转换器等四大结构组成,其中较多设计还会在数模转换器之后增加一个低通滤波器。

image-20250420112036224

1、相位累加器设计

//=====相位累加器和数据锁存器===== 
module addr_cnt(CPi,K,ROMaddr, Address); 
	input CPi; //系统基准时钟(100MHz) 
	input [12:0] K; //13位频率控制字 
	output reg [9:0] ROMaddr; //10位ROM地址 
	output reg [16:0] Address; //17位相位累加器地址信号 
always @(posedge CPi) 
begin 
	Address = Address + K; 
	ROMaddr = Address[16:7]; 
end 
 
endmodule

2、波形存储器ROM设计

(1)方波模块

由于方波的实现算法相对简单,可以不用ROM表,直接用寄存器来保存方波的输出值。方波只有高、低电平两种状态,因此只需要在一个周期的中间位置翻转电平即可。其实现原理如下:由于相位累加器的值是线性累加的,因此地址值(Address)也是线性累加的,对地址值Address进行判断,当地址值的最高位为0时,便将存储波形幅值的存储器的每一位赋值为1,否则赋值为0。具体源程序如下:

//=====方波产生模块:squwave. v ====== 
module squwave(CPi,RSTn, Address, Qsquare); 
	input CPi; //系统基准时钟(100MHz)
	input RSTn;
	//同步清零
	input [16:0]Address;//17位地址输入信号
	output reg[11:0] Qsquare; //输出方波信号,12位
always @(posedge CPi)
if(!RSTn) Qsquare=12'h000;//同步清零
else begin
	if(Address<=17'hOFFFF)
		Qsquare=12'hFFF;//输出高电平
	else Qsquare=12'h000;//输出低电平
end
endmodule
(2)正弦波形存储器模块
#include <stdio.h>
#include <math.h>
#define PI 3.141592
#define DEPTH 1024 //数据深度,即存储单元的个数
#define WIDTH 12//存储单元的宽度
int main(void)
{
	int n, temp;
	float v;
	FILE*fp;
	/*建立文件名为Sine1024.mif的新文件,允许写入数
	据,对文件名没有特殊要求,但扩展名必须为.
	mif*/fp=fopen("'Sine1024. mif", "w+");
	if(NULL== fp)
		printf(" Can not cr eat file!r\n");
	else
	{ 
		printf(" File created successfully!\n");
		/*生成文件头,注意不要忘了“;” */
		fprintf(fp, "DEPTH =%od;\n",DEPTH);
		fprintf(fp, "WIDTH =%od;\n",WIDTH);
		fprintf(fp, "ADDRESS RADIX=HEX;\n");
		fprintf(fp, "DATA RADIX=HEX;(n");
		fprintf(fp, "CONTENT\n");
		fprintf(fp,"BEGIN\n");
		/*以十六进制输出地址和数据*/
		for(n=0;n<DEPTH;n++) 
		{/*周期为1024个点的正弦波*/
		v= sin(2*PI*n/DEPTH);
		/*将-1~1之间的正弦波的值扩展到0~4095之间*/
		temp=(int)((v+1)*4095/2);//v+1将数值平移到0~2之间
		/*以十六进制输出地址和数据*/
		fprintf(fp, "%x\t:t%x;\n",n, temp);
		} 
		fprintf(fp, "END;\n");
		fclose(fp);//关闭文件
	} 
} 

3、锁相环倍频电路设计

使用QuartusPrime软件调用宏模块定制一个100MHz的锁相环模块。其过程是:选择Tool一MegaWizardPlug-InManager命令,启动MegaWizard工具,选择左栏I/O项目下的ALTPLL(嵌入式锁相环),定制一个名称为PLL100M_CP的时钟模块,该模块的输入inclk0为50MHz时钟信号,输出c0为100MHz的脉冲信号,占空比为50%,带有相位锁定指示输出端locked,模块符号如图8.7.9所示。

4、顶层电路设计

将上述各个模块逐个级联起来就可以得到波形产生器的顶层模块,其代码如下:

//=DDS的顶层模块:DDS top.V =
module DDS top(CLOCK 50, RSTn, WaveSel,K,WaveValue, LEDG, CLOCK 100);
input CLOCK 5O;
input RSTn;
input [1:0] WaveSel;
input [12:0] K;
output reg [1l:0] WaveValue;
wire [9:0] ROMaddr;
wire [16:0]Address;
wire[11:0]Qsine,Qquar;
output [0:0]LEDG;
output CLOCK_100;
wire CPi = CLOCK_100;
PLL100M_CP PLL100M_CP_inst (
	.inclk0(CLOCK 50 );
//50MHz时钟输入
	.c0(CLOCK_100),
//100MHz时钟输出
	.locked (LEDG[O])
);
addr_cnt U0_instance(CPi,K,ROMaddr, Address);
SineROM ROM_inst (
	.adres (ROMaddr),
	.clock(CPi ),
	.q(Qsine)
);
	squwave U1(CPi,RSTn,Address,Qsquare);
	always @(posedge CPi)
begin
	case(WaveSel)
	2'b01:WaveValue= Qsine;
	2'b10:WaveValue=Qsquare;
	default:WaveValue= Qsine;
	endcase
end
endmodule

5、最终实现

使用DE2-115开发板来验证上述设计。用析出的50MHz晶振作为时钟输入,用KEY3控制方波清零,用SW12~SW0设置频率控制字,SW7、SW6用来选择输出波形的各类,用LEDGe作为 PLL的相位锁定指示。为方便引脚分配,新建一个顶层文件:

module DE2_115_DDS_top(CLOCK_50,KEY,SW,GPIO_0,LEDG);
	input CLOCK_50;
	input [3:3] KEY;
	input [17:0] SW;
	output [12:0] GPIO_0;
	output [0:0] LEDG;
	wire CLOCK_100;
	assign GPIO_0[12]=CLOCK_100;
	wire RSTn=KEY[3];
	wire[1:0] WaveSel=SW[17:16];
	wire [12:0] K=SW[12:0];
	wire [11:0] WaveValue;
	assign GPIO_0[11:0]=WaveValue;
	DDS_top DE2(CLOCK_50,RSTn,WaveSel,K,WaveValue,LEDG,CLOCK_100);
endmodule

6、仿真波形

image-20250420122810587

image-20250420122821497

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2339599.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenCv高阶(六)——图像的透视变换

目录 一、透视变换的定义与作用 二、透视变换的过程 三、OpenCV 中的透视变换函数 1. cv2.getPerspectiveTransform(src, dst) 2. cv2.warpPerspective(src, H, dsize, dstNone, flagscv2.INTER_LINEAR, borderModecv2.BORDER_CONSTANT, borderValue0) 四、文档扫描校正&a…

性能比拼: Go vs Bun

本内容是对知名性能评测博主 Anton Putra Go (Golang) vs. Bun: Performance (Latency - Throughput - Saturation - Availability) 内容的翻译与整理, 有适当删减, 相关指标和结论以原作为准 我对 Bun 在之前的基准测试中的出色表现感到惊讶&#xff0c;因此我决定将它与 Go …

定制化 Docsify 文档框架实战分享

&#x1f31f; 定制化 Docsify 文档框架实战分享 在构建前端文档平台时&#xff0c;我们希望拥有更友好的用户界面、便捷的搜索、清晰的目录导航以及实用的代码复制功能。借助 Docsify&#xff0c;我实现了以下几个方面的定制优化&#xff0c;分享给大家 &#x1f64c;。 &…

鸿蒙ArkUI之布局实战,线性布局(Column,Row)、弹性布局(Flex)、层叠布局(Stack),详细用法

本文聚焦于ArkUI的布局实战&#xff0c;三种十分重要的布局&#xff0c;线性布局、弹性布局、层叠布局&#xff0c;在实际开发过程中这几种布局方法都十分常见&#xff0c;下面直接上手 线性布局 垂直布局&#xff08;Column&#xff09; 官方文档&#xff1a; Column-行列…

测试基础笔记第七天

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、cat命令二、ls -al命令三、>重定向符号四、>>追加重定向符号五、less/more命令六、grep命令七、|管道符八、clear命令九、head命令十、tail命令十一、…

[Windows] Adobe Camera Raw 17.2 win/Mac版本

[Windows] Adobe Camera Raw 链接&#xff1a;https://pan.xunlei.com/s/VOOIAXoyaZcKAkf_NdP-qw_6A1?pwdpd5k# Adobe Camera Raw&#xff0c;支持Photoshop&#xff0c;lightroom等Adobe系列软件&#xff0c;对相片无损格式进行编辑调色。 支持PS LR 2022 2023 2024 2025版…

开源模型应用落地-Podcastfy-从文本到声音的智能跃迁-Gradio(一)

一、前言 在当今信息呈现方式越来越多样化的背景下&#xff0c;如何将文字、图片甚至视频高效转化为可听的音频体验&#xff0c;已经成为内容创作者、教育者和研究者们共同关注的重要话题。Podcastfy是一款基于Python的开源工具&#xff0c;它专注于将多种形式的内容智能转换成…

Python 深度学习实战 第11章 自然语言处理(NLP)实例

Python 深度学习实战 第11章 自然语言处理(NLP)实例 内容概要 第11章深入探讨了自然语言处理&#xff08;NLP&#xff09;的深度学习应用&#xff0c;涵盖了从文本预处理到序列到序列学习的多种技术。本章通过IMDB电影评论情感分类和英西翻译任务&#xff0c;详细介绍了如何使…

将 DeepSeek 集成到 Spring Boot 项目实现通过 AI 对话方式操作后台数据

文章目录 项目简介本项目分两大模块 GiteeMCP 简介环境要求项目代码核心实现代码MCP 服务端MCP 客户端 DeepSeek APIDockersse 连接ws 连接&#xff08;推荐&#xff09;http 连接 vue2-chat-windowCherry Studio配置模型配置 MCP调用 MCP 项目简介 在本项目中&#xff0c;我们…

《前端面试题之 Vue 篇(第三集)》

目录 1、 nvm的常用命令①.Node.js 版本与 npm 版本的对应关系②Vue2 与 Vue3 项目的 Node.js 版本分界线③版本管理实践建议 2、Vue2 项目搭建&#xff08;基于 vue-cli Webpack&#xff09;① 环境准备② 安装 Vue CLI&#xff08;脚手架&#xff09;③.创建项目&#xff08…

嵌入式C语言位操作的几种常见用法

作为一名老单片机工程师&#xff0c;我承认&#xff0c;当年刚入行的时候&#xff0c;最怕的就是看那些密密麻麻的寄存器定义&#xff0c;以及那些让人眼花缭乱的位操作。 尤其是遇到那种“明明改了寄存器&#xff0c;硬件就是不听话”的情况&#xff0c;简直想把示波器砸了&am…

基于Djiango实现中药材数据分析与可视化系统

中药材数据分析与可视化系统 项目截图 登录 注册 首页 药材Top20 药材价格 产地占比 历史价格 新闻资讯 后台管理 一、项目概述 中药材数据分析与可视化系统是一个基于Django框架开发的专业Web应用&#xff0c;致力于对各类中药材数据进行全面、系统的采集、分析和可视化展示…

stm32(gpio的四种输出)

其实GPIO这个片上外设的功能&#xff1a; 用于控制IO引脚。 CPU就如同大脑&#xff0c;而这些片上外设就如同四肢一样的关系 如图 —————————————————————————————— OK类比了以上 其实GPIO是有 八种工作模式的 这八种工作模式 因为GPIO是面向IO…

Zookeeper 可观测性最佳实践

Zookeeper 介绍 ZooKeeper 是一个开源的分布式协调服务&#xff0c;用于管理和协调分布式系统中的节点。它提供了一种高效、可靠的方式来解决分布式系统中的常见问题&#xff0c;如数据同步、配置管理、命名服务和集群管理等。本文介绍通过 DataKit 采集 Zookeeper 指标&#…

微信小程序三种裁剪动画有效果图

效果图 .wxml <image class"img inset {{status?action1:}}" src"{{src}}" /> <image class"img circle {{status?action2:}}" src"{{src}}" /> <image class"img polygon {{status?action3:}}" src&quo…

C语言笔记(鹏哥)上课板书+课件汇总(结构体)-----数据结构常用

结构体 目录&#xff1a; 1、结构体类型声明 2、结构体变量的创建和初始化 3、结构体成员访问操作符 4、结构体内存对齐*****&#xff08;重要指数五颗星&#xff09; 5、结构体传参 6、结构体实现位段 一、结构体类型声明 其实在指针中我们已经讲解了一些结构体内容了&…

git清理--解决.git文件过大问题

背景&#xff1a;为什么.git比我仓库中的文件大很多 为什么我的git中只有一个1KB的README&#xff0c;但是.git却又1G多&#xff1f;当我想把这个git库push到gitee时&#xff0c;还会报错&#xff1a; 根据报错信息&#xff0c;可看出失败的原因是&#xff1a;有文件的大小超过…

Jetson Orin NX 部署YOLOv12笔记

步骤一.创建虚拟环境 conda create -n yolov12 python3.8.20 注意&#xff1a;YOLOv12/YOLOv11/YOLOv10/YOLOv9/YOLOv8/YOLOv7a/YOLOv5 环境通用 步骤二.激活虚拟环境 conda activate yolov12 #激活环境 步骤三.查询Jetpack出厂版本 Jetson系列平台各型号支持的最高Jetp…

微服务2--服务治理与服务调用

前言 &#xff1a;本文主要阐述微服务架构中的服务治理&#xff0c;以及Nacos环境搭建、服务注册、服务调用&#xff0c;负载均衡以及Feign实现服务调用。 服务治理 服务治理是微服务架构中最核心最基本的模块。用于实现各个微服务的自动化注册与发现。 服务注册&#xff1a;在…

C语言之高校学生信息快速查询系统的实现

&#x1f31f; 嗨&#xff0c;我是LucianaiB&#xff01; &#x1f30d; 总有人间一两风&#xff0c;填我十万八千梦。 &#x1f680; 路漫漫其修远兮&#xff0c;吾将上下而求索。 C语言之高校学生信息快速查询系统的实现 目录 任务陈述与分析 问题陈述问题分析 数据结构设…