前言
在需要输出波形的工程场景中,可以使用 FPGA 构建 DDS 的方案。相比于单片机,FPGA 具有引脚数量多、绑定灵活的优势,适合驱动多位 DA;其并行特性使得输出波形的实时性更强,可使用 PLL 锁相环分频输出高频波形。
FPGA 实现 DDS 也存在短板。FPGA 没有专门的浮点计算单元,定点数处理较复杂,低频高频率下可能出现失真。此外,FPGA 输出的信号频率受时钟主频影响,可能存在微小漂移。由于波形数据通常存储在 ROM 中,难以实时响应不规则输入要求。
一、什么是 DDS?
DDS 的英文是 Direct Digital Synthesis,翻译过来即为直接数字频率合成。使用数字方式产生需要的波形。
相比于模拟的波形产生,DDS 更加灵活,更加可控,更不容易产生噪声,对电路的要求也很低。
当某个工程需要输出某频率的正弦波(或者三角波方波等)的时候,可以使用 DDS 来输出需要的频率和幅值。
二、DDS 基本运行机制
1.地址累加
想要在 DA 上面输出一个想要的正弦波,就需要先有正弦波的数据(查找表)。由于这个正弦波数据并不需要后期改变,所以可以存在一个 ROM 里面。
ROM 就是 Read Only Memory,只读存储器。实际上,在 FPGA 中,就是在一块片上存储空间存了一些数据,这些数据不能改。
向 ROM 提供地址、时钟和使能,即可在输出端口获取到其内部的数据。
现在如果想要输出一个连续的正弦波,其实只需要在 ROM 里面存一个周期的 sin 数据即可。只要不间断地从头到尾扫描整个 ROM,即可输出不间断的正弦波数字量。
假设存储了 1024 个正弦波数据,显然当地址从 0 访问到 1023 后,就可以得到一个周期的正弦波数据。此时将地址重置,再扫一遍拼在之前那个正弦波尾巴上,就是两个周期。
2.频率可调
现在想要将频率提升为 2KHz,显然只需要将一个周期扫描的点数从 1024 降到 512 就行。最简单的方法就是隔一个取一个。实际上就是每次给地址的累加值从 1 变为 2。
但是问题是怎么实现 1.5KHz?总不能在地址上面加小数。那么就需要控制地址增加一个值的时间了。
例如想要输出 1.5KHz 的正弦波,使用的是 1024KHz 的时钟主频,ROM 地址深度为 1024,那么只要每 0.6666...个时钟周期累加一,就可以输出 1.5KHz 的信号。
对于 50MHz 的时钟,每一个时钟周期都对地址加一,那么最终完成一个周期的时间就是:
频率就是:
在这个公式中,显然只有 1024 这个数字是可以修改的。如果每 33,333 个时钟实现遍历一次 ROM 的话,输出频率就是:
问题是怎么能实现每 33,333 个时钟周期遍历一次 ROM?
现在取一个 32 位的寄存器寄存地址的中间值,取高 10 位为真正的地址。
那么假设每一个时钟周期对这个中间值加一,需要 4,294,967,296 个时钟周期后才能把这个 32 位的寄存器塞满。再往上加就会重置到 0。也就是说,4,294,967,296 个时钟周期后,高 10 位会从 11_1111_1111 到 00_0000_0000。实际上就是 ROM 的地址会从 1023 到 0,也就是完成了一个周期的输出。但是这个时候输出的频率就是:

