自主设计一个DDS信号发生器

article/2025/7/5 11:54:43

DDS发生器

      DDS信号发生器是直接数字频率合成技术,采用直接数字频率合成(Direct Digital Synthesis,简称DDS)技术,把信号发生器的频率稳定度、准确度提高到与基准频率相同的水平,并且可以在很宽的频率范围内进行精细的频率调节。采用这种方法设计的信号源可工作于调制状态,可对输出电平进行调节,也可输出各种波形。

工作原理

相位累加 :在基准时钟的驱动下,频率控制字与累加器输出的累加相位数据相加,结果送至相位寄存器的数据输入端,相位寄存器将累加器在上一个时钟作用后产生的新相位数据反馈到累加器的输入端,使相位累加器进行线性相位累加,当累加满时产生一次溢出,完成一个周期性动作,溢出频率即为输出信号频率。

波形查找与转换 :相位寄存器的输出与相位控制字相加,结果作为正弦查找表的地址,查找表由 ROM 构成,存有一个完整周期正弦波的数字幅度信息,每个地址对应正弦波的一个相位点,把输入地址信息映射成正弦波幅度信号,输出到 D/A 转换器,经转换成模拟量形式信号,再通过低通滤波器衰减和滤除不需要的取样分量,输出频谱纯净的正弦波信号。

基本结构

DDS 主要由相位累加器、相位调制器、波形数据表以及 D/A 转换器构成。

相位累加器:相位寄存器同样由FPGA内部的寄存器资源构成,用于暂存相位累加器的输出结果。其位数与相位累加器相匹配,以便准确地反馈相位累加结果。

在相位累加器完成一次累加后,其输出结果会被送入相位寄存器。在下一个时钟周期,相位寄存器将存储的相位数据反馈到相位累加器的输入端,以实现连续的相位累加操作。

波形存储器(ROM):在FPGA中,波形存储器通常由只读存储器(ROM)实现。可利用FPGA内部的ROM资源,预先将波形的一个周期内的幅度信息以数字形式存储在ROM中。例如,对于正弦波,可以预先计算出一个周期内各个相位点的幅度值,并将其存储到ROM中。

相位寄存器的输出与相位控制字相加后,结果作为波形存储器的地址。FPGA根据该地址从ROM中读取预先存储的波形幅度值,并将其输出到数模转换器(DAC)。波形存储器的容量和精度决定了输出信号的精度和质量,容量越大,可存储的波形点数越多,输出信号的波形越平滑;存储精度越高,输出信号的幅度精度越高。

数模转换器(DAC):FPGA本身不直接包含数模转换器,通常需要外接DAC芯片。通过FPGA的I/O引脚与DAC芯片的接口相连,将数字波形数据传输给DAC进行数模转换。

FPGA按照一定的时序控制,将波形存储器输出的数字幅度信号通过I/O引脚发送到DAC芯片。DAC芯片根据接收到的数字信号,将其转换为对应的模拟信号。转换后的模拟信号通常是一个阶梯状的波形,需要经过后续的低通滤波处理。

低通滤波器(LPF):低通滤波器一般由外部的无源或有源滤波电路实现,FPGA主要负责控制和配置滤波器的相关参数。例如,通过FPGA的I/O引脚输出控制信号来调整滤波器的截止频率等参数。

DAC输出的阶梯状模拟信号包含丰富的高频分量,低通滤波器会滤除这些高频分量,使输出信号变得更加平滑,接近理想的正弦波或其他目标波形。

DFS信号发生器中,相位累加器是核心部件之一,它由N位加法器和N位寄存器组成。每当有一个时钟脉冲到来时,加法器就会把频率控制字和寄存器里当前存着的相位数据加起来,得出的结果又会送回寄存器的输入端。这样,在下一个时钟脉冲来的时候,加法器会再次把频率控制字和寄存器里的新数据相加。这个过程不断重复,相位累加器就在时钟信号的驱动下,持续地对频率控制字进行相加操作。每次相加后,累加器输出的数据就代表了合成信号的相位信息,而且相位累加器发生溢出的频率,就是DFS输出信号的频率。简单来说,相位累加器的作用就是在时钟的作用下,不断累加频率控制字,从而得到合成信号的相位数据,并且这个相位数据会被用作波形存储器的地址,去查出对应的波形采样值,实现从相位到幅度的转换,最后波形存储器输出的数据会被送到D/A转换器,转换成模拟信号输出。

特点

频率分辨率高:输出信号的频率与相位累加器的增量值有关,可以实现非常细微的频率调节。

波形种类丰富:能够生成正弦波、方波、三角波等多种波形,通过改变查找表中的数据即可快速切换。

频率稳定度和准确度高:频率稳定度和准确度可提高到与基准频率相同的水平。

快速转换时间:信号频率转换时间短。

可调制性:可工作于调制状态,对输出电平进行调节。

自己动手设计一个DDS信号发生器

思路

通过查阅相关资料,我们可以从以下几个步骤实现DDS信号发生器的设计:

一、系统设计

确定设计目标

输出信号类型:正弦波和方波。

频率范围:10Hz~5MHz。

最小频率分辨率:小于1kHz。

系统架构划分

相位累加器模块:负责生成相位信息。

波形查找表模块:存储正弦波和方波的波形数据。

数模转换器(DAC)接口模块:将数字信号转换为模拟信号。

控制模块:用于设置频率控制字、波形选择等参数。

时钟模块:提供系统时钟信号。

二、模块设计

相位累加器模块

选择合适的相位累加器位数(如32位)。

根据频率范围和分辨率要求,计算频率控制字的范围。

波形储存器模块

使用ROM IP核存储正弦波和方波的波形数据。

初始化查找表,填充正弦波和方波的数字样本。

控制模块

提供用户接口,用于设置频率控制字、选择波形类型(正弦波或方波)等参数。

将用户输入的参数传递给相位累加器和波形查找表模块。

三、仿真验证

使用ModelSim或其他仿真工具搭建仿真环境。将设计的各个模块集成到仿真环境中,设置仿真时钟信号和测试激励。

实现步骤

根据实验指导,我们可以根据以下结构设计以便理解:

所需正弦波生成

在仿真设计之前,我们需要通过MATLAB生成一个正弦波文件添加到我们的项目中,以便我们后续的操作。

clc, clear, close allF1=1; %信号频率Fs=10^2; %采样频率P1=0; %信号初始相位N=10^2; %采样点数t=[0:1/Fs:(N-1)/Fs]; %采样时刻ADC=2^7 - 1; %直流分量A=2^7; %信号幅度%生成正弦信号s=A*sin(2*pi*F1*t + pi*P1/180) + ADC;plot(s); %绘制图形%创建 coe 文件fild = fopen('sin_wave_100x8.coe','wt');%写入 coe 文件头%固定写法,表示写入的数据是 10 进制表示fprintf(fild, '%s\n','memory_initialization_radix=10;');%固定写法,下面开始写入数据fprintf(fild, '%s\n\n','memory_initialization_vector ='); for i = 1:Ns2(i) = round(s(i)); %对小数四舍五入以取整if s2(i) <0 %负 1 强制置零s2(i) = 0endfprintf(fild, '%d',s2(i)); %数据写入if i==Nfprintf(fild, '%s\n',';'); %最后一个数据用;elsefprintf(fild,',\n'); % 其他数据用,endendfclose(fild); % 写完了,关闭文件F1 = 1;    %信号频率Fs = 10^2; %采样频率P1 = 0;    %信号初始相位N = 10^2;  %采样点数t = [0:1/Fs:(N-1)/Fs]; %采样时刻ADC = 2^7 - 1; %直流分量A = 2^7;       %信号幅度%生成方波信号s = A*square(2*pi*F1*t + pi*P1/180) + ADC;plot(s); %绘制图形%创建 coe 文件fild = fopen('squ_wave_100x8.coe','wt');%写入 coe 文件头%固定写法,表示写入的数据是 10 进制表示fprintf(fild, '%s\n','memory_initialization_radix=10;');%固定写法,下面开始写入数据 fprintf(fild, '%s\n\n','memory_initialization_vector =');for i = 1:Ns2(i) = round(s(i)); %对小数四舍五入以取整if s2(i) <0 %负 1 强制置零s2(i) = 0endfprintf(fild, '%d',s2(i)); %数据写入if i==Nfprintf(fild, '%s\n',';'); %最后一个数据用分号elsefprintf(fild,',\n'); % 其他数据用 ,endendfclose(fild); % 写完,关闭文件

1、相位累加器模块

// 模块声明,名称为squwave// CPi为时钟输入信号,RSTn为复位输入信号,Address为17位地址输入,Qsquare为12位输出寄存器module squwave(CPi, RSTn, Address, Qsquare);input CPi;                  // 时钟信号输入input RSTn;                 // 复位信号输入(低电平有效)input [16:0] Address;       // 17位地址输入output reg [11:0] Qsquare;  // 12位输出寄存器// 时序逻辑块,检测CPi的上升沿触发always @(posedge CPi) begin// 当复位信号RSTn为低电平时,Qsquare输出全0if (!RSTn) beginQsquare = 12'h000;  // 复位时输出12位全0end// 正常工作时,根据Address的值设置Qsquare的输出else begin// 如果Address小于等于17'h0FFFF(即十进制的131071),Qsquare输出全1if (Address <= 17'h0FFFF) beginQsquare = 12'hFFF;  // 输出12位全1end// 否则,Qsquare输出全0else beginQsquare = 12'h000;  // 输出12位全0endendendendmodule

该代码定义了一个名为squwave的数字电路模块,该模块的主要功能是根据输入地址Address的值来生成一个12位的输出信号Qsquare。模块接收一个时钟信号CPi和一个低电平有效的复位信号RSTn。当复位信号RSTn被激活(即处于低电平状态)时,输出Qsquare会被立即清零,以确保模块从一个已知的初始状态开始工作。

在正常工作状态下,模块会在每个时钟周期的上升沿检查Address输入的值。如果Address的值小于或等于0xFFFF(即十进制的65535),则输出Qsquare会被设置为全1(0xFFF),这通常表示一个高电平状态或激活状态。相反,如果Address的值大于0xFFFF,则输出Qsquare会被设置为全0(0x000),表示一个低电平状态或非激活状态。

2、ROM模块

// 定义一个名为rom_sine的模块,用于生成正弦波形的只读存储器(ROM)module rom_sine(input [10:0] address,  // 11位地址输入,用于访问ROM中的波形数据input clock,           // 时钟信号输入,用于同步数据读取output reg [11:0] q    // 12位输出,用于输出ROM中存储的正弦波形数据);// 声明一个12位宽、2048个元素的存储器数组,用于存储正弦波形数据reg [11:0] mem [0:2047];// 初始化块,用于从MIF文件中读取正弦波形数据到存储器数组initial begin$readmemb("Sine1024.mif", mem);  // 从Sine1024.mif文件中读取数据到mem数组end// 时序逻辑块,用于在时钟上升沿更新输出q的值always @(posedge clock) beginq <= mem[address];  // 根据输入地址,从存储器数组中读取对应的正弦波形数据,并赋值给输出qendendmodule// 定义一个名为rom_square的模块,用于生成方波形的只读存储器(ROM)module rom_square(input [10:0] address,  // 11位地址输入,用于访问ROM中的波形数据input clock,           // 时钟信号输入,用于同步数据读取output reg [11:0] q    // 12位输出,用于输出ROM中存储的方波形数据);// 声明一个12位宽、2048个元素的存储器数组,用于存储方波形数据reg [11:0] mem [0:2047];// 初始化块,用于从MIF文件中读取方波形数据到存储器数组initial begin$readmemb("Square1024.mif", mem);  // 从Square1024.mif文件中读取数据到mem数组end// 时序逻辑块,用于在时钟上升沿更新输出q的值always @(posedge clock) beginq <= mem[address];  // 根据输入地址,从存储器数组中读取对应的方波形数据,并赋值给输出qendendmodule

这两个模块分别实现了正弦波和方波的ROM生成器。它们都使用了一个12位宽、2048个元素的存储器数组来存储波形数据,并在时钟信号的上升沿根据输入地址读取对应的波形数据并输出。初始化块使用readmemb系统任务从MIF文件中读取波形数据到存储器数组中。这种设计可以用于数字信号处理应用中,生成所需的波形信号。

3、顶层模块

module DDS_top (input CLOCK_50,       // 输入50MHz时钟信号input RSTn,           // 输入复位信号,低电平有效input [1:0] WaveSel,  // 输入波形选择信号,2位宽,用于选择输出的波形类型input [12:0] K,       // 输入频率控制字,13位宽,用于控制输出波形的频率output reg [11:0] WaveValue, // 输出波形值,12位宽,输出实际的波形数据wire [9:0] ROMaddr,  // 定义一个10位宽的ROM地址信号wire [16:0] Address, // 定义一个17位宽的地址信号wire [11:0] Qsine,   // 定义一个12位宽的正弦波形数据信号wire [11:0] Qsquare,// 定义一个12位宽的方波形数据信号output [0:0] LEDG,   // 输出LED指示信号,1位宽,用于指示PLL锁定状态output CLOCK_100    // 输出100MHz时钟信号);// 定义内部时钟信号CPi,等于输出的100MHz时钟wire CPi = CLOCK_100;// 实例化PLL模块,将50MHz时钟倍频至100MHz,并输出锁定指示LEDGPLL100M_CP PLL100M_CP_inst (.inclk0(CLOCK_50),.c0(CLOCK_100),.locked(LEDG[0]));// 实例化地址计数器模块,根据频率控制字K和内部时钟CPi生成ROM地址ROMaddr和地址Addressaddr_cnt U0_instance (.CPi(CPi),.K(K),.ROMaddr(ROMaddr),.Address(Address));// 实例化正弦波ROM模块,根据ROM地址ROMaddr和内部时钟CPi输出正弦波形数据QsineSineROM ROM_inst (.address(ROMaddr),.clock(CPi),.q(Qsine));// 实例化方波模块,根据地址Address和内部时钟CPi输出方波形数据Qsquaresquwave U1 (.CPi(CPi),.RSTn(RSTn),.Address(Address),.Qsquare(Qsquare));// 根据波形选择信号WaveSel选择输出的波形类型always @(posedge CPi) begincase (WaveSel)2'b01: WaveValue = Qsine;   // 如果选择正弦波,则输出Qsine2'b10: WaveValue = Qsquare; // 如果选择方波,则输出Qsquaredefault: WaveValue = Qsine; // 默认输出正弦波endcaseendendmodule

部分Ip核配置

配置输出位宽、存储容量和存储器类型等:


http://www.hkcw.cn/article/tjsWSWOnby.shtml

相关文章

浏览器网站禁止黏贴,但是要交作业怎么快速黏贴

出现的问题&#xff1a; 写这篇博客的原因&#xff1a;学校最近要求使用 iwrite 写英语作文&#xff0c;但是浏览器禁止黏贴&#xff0c;我们自己只能手动输入&#xff0c;但是作为程序猿的我想到了一个很好的解决方案。 解决思路&#xff1a; 我们直接在浏览器的控制台的源代码…

CAN通讯协议中各种参数解析

1.各种参数缩写 2.多帧传输时间参数解析 - Sender&#xff08;左侧&#xff09; 指的是 多帧数据的发送者&#xff0c;也就是&#xff1a; ECU&#xff08;被测系统 / 响应方&#xff09; - Receiver&#xff08;右侧&#xff09; 指的是 多帧数据的接收者&#xff0c;也就是…

第十二节:第五部分:集合框架:Set集合的特点、底层原理、哈希表、去重复原理

Set系列集合特点 哈希值 HashSet集合的底层原理 HashSet集合去重复 代码 代码一&#xff1a;整体了解一下Set系列集合的特点 package com.itheima.day20_Collection_set;import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; import java.util.…

deepseek原理和项目实战笔记2 -- deepseek核心架构

混合专家&#xff08;MoE&#xff09; ​​混合专家&#xff08;Mixture of Experts, MoE&#xff09;​​ 是一种机器学习模型架构&#xff0c;其核心思想是通过组合多个“专家”子模型&#xff08;通常为小型神经网络&#xff09;来处理不同输入&#xff0c;从而提高模型的容…

迈向分布式智能:解析MCP到A2A的通信范式迁移

智能体与外部世界的桥梁之言&#xff1a; 在深入探讨智能体之间的协作机制之前&#xff0c;我们有必要先厘清一个更基础的问题&#xff1a;**单个智能体如何与外部世界建立连接&#xff1f;** 这就引出了我们此前介绍过的 **MCP&#xff08;Model Context Protocol&…

TCP/IP协议精华总结pdf分享

hi &#xff0c;大家好&#xff0c;应小伙伴们的要求&#xff0c;上次分享了个人的一些学习和职场经验&#xff0c;其中网络协议PDF文档是我之前学习协议的时候总结一些精华知识&#xff0c;网络属于基本功&#xff0c;是互联网必备知识&#xff0c;我深信掌握好核心20%知识&am…

齐次变换矩阵与运动旋量的指数映射

在三维空间中&#xff0c;刚体的位姿&#xff08;位置和姿态&#xff09;可以通过齐次变换矩阵进行描述。齐次变换矩阵是一种 44 的矩阵&#xff0c;其一般形式为&#xff1a; T [ R p 0 1 ] T\begin{bmatrix}R&p\\0&1\end{bmatrix} T[R0​p1​] 其中&#xff0c; R …

MySQL DDL操作全解析:从入门到精通,包含索引视图分区表等全操作解析

目录 一、DDL 基础概述 1.1 DDL 定义与作用 1.2 DDL 语句分类 1.3 数据类型与存储引擎 1.3.1 数据类型 1.3.2 存储引擎差异 二、基础 DDL 语句详解 2.1 创建数据库与表 2.1.1 创建数据库 2.1.2 创建表 2.2 修改表结构 2.2.1 添加列 2.2.2 修改列属性 2.2.3 删除列…

torch.randn vs torch.rand

1 分布类型&#xff1a; randn&#xff1a;生成标准正态分布&#xff08;均值 0&#xff0c;标准差 1&#xff09; rand&#xff1a;生成 [0, 1) 区间的均匀分布 2 数值范围&#xff1a; randn&#xff1a;可能产生负数&#xff08;范围 (-∞, ∞)&#xff09; rand&#xff…

NLP学习路线图(十九):GloVe

自然语言处理&#xff08;NLP&#xff09;的核心挑战在于让机器理解人类语言的丰富含义。词向量&#xff08;Word Embeddings&#xff09;技术通过将词语映射到高维实数空间&#xff0c;将离散的符号转化为连续的向量&#xff0c;为NLP任务奠定了坚实基础。在众多词向量模型中&…

极客时间:用 FAISS、LangChain 和 Google Colab 模拟 LLM 的短期与长期记忆

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

dify应用探索

一个典型的 Agent Multi-Agent 系统 智能导购会根据用户意图分类并传递给相应商品导购Agent&#xff0c;返回商品信息。采用Multi-Agent架构&#xff0c;其中Router Agent负责对用户问题进行意图 分析&#xff0c;并路由到其它商品导购Agent,商品导购Agent负责向厥客收 集商品…

py爬虫的话,selenium是不是能完全取代requests?

selenium适合动态网页抓取&#xff0c;因为它可以控制浏览器去点击、加载网页&#xff0c;requests则比较适合静态网页采集&#xff0c;它非常轻量化速度快&#xff0c;没有浏览器开销&#xff0c;占用资源少。当然如果不考虑资源占用和速度&#xff0c;selenium是可以替代requ…

c++类和对象-继承

参考链接&#xff1a;46 类和对象-继承-继承方式_哔哩哔哩_bilibili 1.概述 作用&#xff1a;提高代码复用率&#xff0c;多个子类和父类有相同之处&#xff0c;又有自己各自的特点。例如基类人有四肢、会走路、说话&#xff0c;不同子类中国人是黑头发&#xff0c;说汉语&am…

MySQL中的锁

MySQL中有哪些锁? 全局锁(FTWRL) 含义&#xff1a;Flush Table with Read Lock的缩写&#xff0c;它会锁定整个数据库实例&#xff0c;让所有表都处于只读状态。 使用全局锁&#xff0c;要执行的命令: flush tables with read lock 之后&#xff0c;整个数据库就处于只读…

探索 Dify 的工作流:构建智能应用的新范式

目录 前言1. 什么是 Dify 的工作流2. 工作流的核心组成2.1 节点&#xff08;Node&#xff09;2.2 连接线&#xff08;Edge&#xff09;2.3 上下文与变量系统 3. 工作流的典型使用场景3.1 多轮对话与智能客服3.2 文档问答系统3.3 多语言营销文案生成3.4 多模型对比与评估&#x…

分词算法BBPE详解和Qwen的应用

一、TL&#xff1b;DR BPE有什么问题&#xff1a;依旧会遇到OOV问题&#xff0c;并且中文、日文这些大词汇表模型容易出现训练中未出现过的字符Byte-level BPE怎么解决&#xff1a;与BPE一样是高频字节进行合并&#xff0c;但BBPE是以UTF-8编码UTF-8编码字节序列而非字符序列B…

小云天气APP:精准预报,贴心服务

在快节奏的现代生活中&#xff0c;天气变化对我们的日常生活、出行安排以及健康状况都有着重要影响。一款精准、便捷且功能丰富的天气预报应用&#xff0c;无疑是提升生活品质的必备工具。小云天气APP正是这样一款为安卓用户量身定制的天气预报应用&#xff0c;凭借其精准的天气…

阿里云服务器ECS详细购买流程

1、打开云服务器ECS官方页面 打开阿里云服务器ECS页面 点击进入阿里云服务器 2、付费类型选择 阿里云服务器付费类型 3、地域节点 阿里云服务器全球28个地域&#xff0c;中国大陆地域如华北2&#xff08;北京&#xff09;、华东1&#xff08;杭州&#xff09;、华南1&#xf…

FastAPI+Pyomo实现线性回归解决饮食问题

之前在 FastAPI介绍-CSDN博客 中介绍过FastAPI&#xff0c;在 Pyomo中线性规划接口的使用-CSDN博客 中使用Pyomo解决饮食问题&#xff0c;这里将两者组合&#xff0c;即FastAPI在服务器端启动&#xff0c;通过Pyomo实现线性回归&#xff1b;客户端通过浏览器获取饮食的最优解。…