FPGA控制DDS产生1CLK周期误差的分析(二)
前文简短的介绍了DDS的产生原理,其实相当的简单,所以也不需要多做解释,本文详细阐述一下在调试DDS的过程中所产生的一个bug
问题发现
正如上文所述,再用FPGA控制存储在rom中的波形信号输出之后,在上板之前,我们可以先用仿真软件仿真一下,看看波形是否有误。我们的测试条件要求为:从2550Hz开始,每间隔1s的周期以20Hz的步长向下递减至2310Hz的频率,仿真波形如下所示(为节省仿真时间,我们将扫频间隔缩短):
不知大家是否能看清上图中的周期结果分析,按照上图所示,2550Hz、2530Hz除开频率交接处会有一定的抖动之外,在扫频周期中的频率是比较稳定的,但是当我们看到2510Hz的扫频周期过程中却发现,波形的周期呈现为398420ns、398400ns的周期变化,虽然说二者的周期对应基本上都为2510Hz,但是对频率信号敏感的场合是不能接受的。本文便对这一问题进行考究。
问题探究
首先,我们追根溯源,DDS信号发生的来源就是ROM当中存储的波形数据,而波形数据的来源是所输入的地址addr,这么一来我们就将目光放在addr上面。
addr是一个12位的变量,来源于频率信号叠加寄存器的高12位,为什么要设置叠加器为32位,就像前文所述,为了提高但个周期的分辨率,使得输出更加精确。接下来让我们分别来看看398420 ns和398400 ns的波形细节。
我们发现多的20ns(一个CLK周期)来源于高电平期间的差异,所以我们进一步的查看高电平的细节。先来看看199220ns的这个高电平。
可见,高电平的持续时间应该为 20ns × 5 × (2048-0) = 204800ns,这和我们的波形图上实现显示的差了204800ns - 199220ns = 5,580ns。我们记为error1。
然后再来看199200ns的这个高电平。
上面两幅图的高电平的持续时间应该为 20ns × 5 × (2048-0) = 204800ns,这和我们的波形图上实现显示的差了204800ns - 199200ns = 5600ns。我们记为error2。
好了,通过分析波形我们发现逻辑设计上是没有问题的,该在什么时候下降,什么时候上升都符合要求,那么其中的error1、2是从何而来的呢?这时候我们注意到上面的这个5,代表5个时钟周期变化一次地址,那么此时我们不禁要问,这个5个时钟周期变化一次,它真的那么诚实可靠,人畜无害吗,答案是否定的。
像上面两幅这样的问题时序还有很多,相信数量也是5600ns / 20ns = 280个。好了接下来问题就是分析问什么会少一个了。
讲了这么久,终于该回到代码层面了。让我们看一下地址递增的部分代码。
reg [31:0] freq_cnt;
assign addr = freq_cnt[31:20] + phase_offset;
always @(posedge sys_clk or posedge sys_rst)beginif(sys_rst)freq_cnt <= 32'd0;elsefreq_cnt <= freq_cnt + freq_k;
end
从上图中可以看到。此时的freq_k为215607,而我们的地址是从220开始进位的,也就是说计数220 = 1,048,576次之后,addr + 1。那么215607需要加多少次呢?如下表所示:(表中标红表示进位)
上表我想已经表示的很明白了,因为每一轮都会有一个误差叠加,最后不断累积起来就会造成每一个周期输出的周期数不一样,虽然每一次都只是一个CLK的误差,但是总归是有误差的。目前我们给的频率参数是2510Hz,有这个现象,那前面2550、2530Hz有没有这个情况呢?答案是有的,如下图所示:
同样的,2048 × 20 × 5 = 204800ns,但是实际上仿真结果为:196080ns,也是有这个误差的。至于为什么此时没有出现前面那样子一个CLK周期的误差,原因我想就是刚刚好误差的次数为偶数,根据差分原理消除了。不论怎样,这个问题我们都应该消除,下面探讨一下解决办法。
问题解决
知道了问题所在,接下来找找解决办法,既然已经确定这个是算法的问题,就从算法设计上下功夫。首先可以确定的一点是,我们既然已经得到对应的freq_k即每一次叠加的值,那么每一次就应该叠加这么多,也就是说每一次就应该加这么多才能得到正确的输出频率,如果修改了这个输出的频率值也就是错的了。
解决办法1:一开始我想的办法是,依旧按照这个计数值叠加,如果叠加的值超过了220 = 1,048,576马上让叠加的值清0,然后让addr+1。这样子相当于舍弃了每一次的叠加误差。但是后来想了想,这样子的频率输出误差也太大了,分析如下:假设我刚好要计数5次,那么每一次计数的值就是1048576 / 5 = 209,715.2 ≈209716(必须要向上取整);而如若要计数计数6次,对应的就是174,763;4次对应262,144。这样子按照之前根据频率计算次数的公式对应回去的频率分别为:3,051.8Hz、2,441Hz和2,034.5Hz,也就是说,位于这三个点之间的设定频率都会被忽略,然后变成上述的固定点的频率值,有点类似于模数转换中的量化误差。而如若要减少量化误差,办法是降低addr跳变的临界值也就是1048576,即在N不变的前提下,提高ROM的深度,但这总归不是个好办法。
解决办法2:通过上述分析我们知道,叠加误差不能够省略,那么我们又如何让叠加误差“进场”呢?通过思考,我发现叠加误差本来就要进去的,那么转变枪头,将目光从叠加误差转移到波形误差,为什么上一个周期的叠加误差和下一个周期的叠加误差会不同?如何才能让它们变得相同?为什么会不同,因为每一个周期在结束的时候,对应的freq_cnt的不为0,这边就也会有一个误差(freq_cnt就是那个32位的寄存器,取高12位作为ROM的寻址信息)。还是上面那个例子,地址+1所需要的叠加量为1 048 576,然后我们每一次始终到来时给它 +215 607,但是如果每一个周期结束时都会有一个会累加的误差,当这个误差累加到215 607时,就省了一个时钟周期的叠加,此时误差重新回0,这就是为什么会出现周期之间会有一个CLK周期的误差的原因,之所以前面2550Hz和2530Hz没有出现,是因为他们每一次叠加的误差量太小,而仿真的时间太短,还没来得及叠加上去就跳到下一个频率周期了!针对这个周期间的误差,我想到解决办法是,检测到一个周期输出完毕之后,手动的将叠加器变量给清空,也就是说人为消除周期叠加误差。这个误差和上面那个不同,上面那个累加误差决定着输出的精度,这个周期误差的值不会超过一个地址周期,可以忽略不计,虽然精度会受损,但总归是消除了这个误差了。
结语
起始本次设计的bug来源就是算法的问题,最后的解决办法也想但简单就是清个0就好了。啰嗦了这么多总归是讲完了,说实话,当初在调DDS的时候,根本就没想这么多,只觉得输出的时候频率正确即可。也根本发现不了这个问题,毕竟1个CLK周期的误差,真的太难发现了。所幸一位朋友发现了这个问题,在与他交流许久后才做了这次的实验,最终也是不负使命,找出了问题所在,也于此感谢他的细心观察的结果。
资源链接
百度网盘链接:链接:https://pan.baidu.com/s/1LqOMz0f9FBW-6bxZFUzlQA?pwd=wvq6
提取码:wvq6
CSDN链接:FPGA控制AD9767实现DDS信号发生器
相关文章:

FPGA控制DDS产生1CLK周期误差的分析(二)
前文简短的介绍了DDS的产生原理,其实相当的简单,所以也不需要多做解释,本文详细阐述一下在调试DDS的过程中所产生的一个bug 问题发现 正如上文所述,再用FPGA控制存储在rom中的波形信号输出之后,在上板之前࿰…...

这一次,吃了Redis的亏,也败给了GPT
关注【离心计划】,一起离开地球表面 背景 组内有一个系统中有一个延迟任务的需求,关于延迟任务常见的做法有时间轮、延迟MQ还有Redis Zset等方案,关于时间轮,这边小苏有一个大学时候做的demo: https://github.com/JA…...
第一章 信息化知识
1、信息是客观事物状态和运动特征的一种普遍形式,信息的概念存在两个基本的层次,即本体论层次和认识论层次: 本体论层次:就是事物的运动状态和状态变化方式的自我表述认识论层次:就是主体对于该事物的运动状态以及状态…...

如何用matlab工具箱训练一个SOM神经网络
本站原创文章,转载请说明来自《老饼讲解-BP神经网络》bp.bbbdata.com本文展示如何用matlab工具箱训练一个SOM神经网络的DEMO并讲解其中的代码含义和相关使用说明- 01.SOM神经网络DEMO代码 -- 本文说明 -下面,我们先随机初始化一些样本点,然后…...

音视频技术开发周刊 | 285
每周一期,纵览音视频技术领域的干货。新闻投稿:contributelivevideostack.com。GPT-4 Office全家桶发布谷歌前脚刚宣布AI工具整合进Workspace,微软后脚就急匆匆召开了发布会,人狠话不多地祭出了办公软件王炸——Microsoft 365 Cop…...

安装flume
flume最主要的作用就是实时读取服务器本地磁盘的数据,将数据写入到hdfs中架构:开始安装一,上传压缩包,解压并更名解压:[rootsiwen install]# tar -zxf apache-flume-1.9.0-bin.tar.gz -C ../soft/[rootsiwen install]#…...
为工作排好优先级
工作,是干不完的,因此我们需要分清轻重缓急,为它们划分优先级,这样才不至于让自己手忙脚乱。 给手头的事情排上正确的优先级,是一项很重要的工作能力。 优先级有很多考量,并不是简单的先来后到的线性时间…...

超专业解析!10分钟带你搞懂Linux中直接I/O原理
我们先看一张图: 这张图大体上描述了 Linux 系统上,应用程序对磁盘上的文件进行读写时,从上到下经历了哪些事情。 这篇文章就以这张图为基础,介绍 Linux 在 I/O 上做了哪些事情。 文件系统 什么是文件系统 文件系统࿰…...

【C++】面试101,用两个栈实现队列,包含min函数的栈,有效括号序列,滑动窗口的最大值,最小的K个数,倒置字符串,排序子序列,跳跃,数字三角形,蓝肽子序列
目录 1. 用两个栈实现队列 2.包含min函数的栈 3.有效括号序列 4.滑动窗口的最大值 5.最小的K个数 6.倒置字符串 7.排序子序列 8.数字三角形(蓝桥杯,学习一个大佬的博客....) 9.跳跃(蓝桥杯) 10.蓝肽子序列 1. 用…...

WPF 认识WPF
什么是WPF?WPF是Windows Presentation Foundation(Windows展示基础)简称,顾名思义是专门编写表示层的技术。WPF绚丽界面如下:GUI发展及WPF历史?Windows系统平台上从事图形用户界面GUI(Graphic User Interface)已经经历了多次换代,…...
【建议收藏】PHP单例模式详解以及实际运用
PHP单例模式详解以及实际运用 什么是单例模式? 首先我们百度百科他怎么说? 单例模式,属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要,也有可能一个线程中属于单例,如&a…...
【十二天学java】day04-流程控制语句
第一章 流程控制语句 在一个程序执行的过程中,各条语句的执行顺序对程序的结果是有直接影响的。所以,我们必须清楚每条语句的执行流程。而且,很多时候要通过控制语句的执行顺序来实现我们想要的功能。 1.1 流程控制语句分类 顺序结构 判断…...

Pandas 与 PySpark 强强联手,功能与速度齐飞
Pandas做数据处理可以说是yyds!而它的缺点也是非常明显,Pandas 只能单机处理,它不能随数据量线性伸缩。例如,如果 pandas 试图读取的数据集大于一台机器的可用内存,则会因内存不足而失败。 另外 pandas 在处理大型数据…...
【Zabbix实战之部署篇】docker部署Zabbix+grafana监控平台
【Zabbix实战之部署篇】docker部署Zabbix+grafana监控平台 一、Zabbix介绍1.Zabbix简介2.Zabbix的优点3.Zabbix各组件介绍4.Zabbix架构图二、grafana介绍1.grafana简介2.grafana特点三、实践环境规划四、检查本地docker环境1.检查操作系统版本2.检查docker版本3.检查docker服务…...

acm省赛:高桥和低桥(三种做法:区间计数、树状数组、线段树)
题目描述 有个脑筋急转弯是这样的:有距离很近的一高一低两座桥,两次洪水之后高桥被淹了两次,低桥却只被淹了一次,为什么?答案是:因为低桥太低了,第一次洪水退去之后水位依然在低桥之上ÿ…...

stm32-定时器详解
0. 概述 本文针对STM32F1系列,主要讲解了其中的8个定时器的原理和功能 1. 定时器分类 STM32F1 系列中,除了互联型的产品,共有 8 个定时器,分为基本定时器,通用定时器和高级定时器基本定时器 TIM6 和 TIM7 是一个 16 位…...

《硬件架构的艺术》读书笔记:Chapter 1 亚稳态的世界
Chapter 1 亚稳态的世界 一、简介 同步系统中,数据和时钟有固定的因果关系(在同一时钟域(Clock Domains))中,只要数据和时钟满足建立时间和保持时间的要求,不会产生亚稳态(meastable) 静态时序分析(STA) 就是基于同步电路设计模型而出现的&am…...

开箱即用的密码框组件
写了一个小玩具,分享一下 - 组件功能: 初次进入页面时,密码隐藏显示,且无法查看真实密码 当修改密码时,触发键盘,输入框则会直接清空 此时输入密码,可以设置密码的隐藏或显示: …...

ChatGPT能否取代程序员?
目录ChatGPT能否取代程序员?ChatGPT和程序员的工作内容和工作方式ChatGPT和程序员的共同点程序员的优势程序员的实力ChatGPT和程序员的关系结论惊喜ChatGPT能否取代程序员? ChatGPT是一种非常普遍的人工智能(AI)系统,…...

案例分享 | 金融微服务场景下如何提升运维可观测性
云原生环境下金融业务的微服务化改造以及分布式架构的部署,使得业务与开发部门的关联更为紧密,传统运维监控已满足不了业务运营需求,亟需建设具备可观测性的运维体系。所以这次我们以某金融客户的实践案例为例,跟大家说一说在金…...

(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...

GitFlow 工作模式(详解)
今天再学项目的过程中遇到使用gitflow模式管理代码,因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存,无论是github还是gittee,都是一种基于git去保存代码的形式,这样保存代码…...

云原生安全实战:API网关Kong的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关(API Gateway) API网关是微服务架构中的核心组件,负责统一管理所有API的流量入口。它像一座…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程
STM32F1 本教程使用零知标准板(STM32F103RBT6)通过I2C驱动ICM20948九轴传感器,实现姿态解算,并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化,适合嵌入式及物联网开发者。在基础驱动上新增…...