前言
上一篇的锁相环的配置是基于Intel公司的Altera-FPGA的,是Intel公司将已经写好的例程植入到Quartus的软件当中的用户可直接使用的模块程序。这次的正弦信号合成也是类似的。
之前参加电子设计竞赛的一个队友和我说,FPGA是不需要DDS的(当然我始终对他说的话保持质疑)。后来我去研究了一下,发现他说的可能是对的,因为利用这个正弦信号合成的模块,就可以仅使用DAC就能输出比较完整的正弦波信号(方波和三角波自己设计一下比较简单,但是正弦信号的模拟量不好把控,所以需要外部的辅助)。这个正弦波信号的合成模块就是NCO。
一、目的
熟悉NCO并会配置,方便后续的使用。
二、NCO的配置
首先,要在菜单栏的工具(Tools)中找到IP Catalog(Alt+7),右侧会跳出IP Catalog窗口,在搜索栏里输入"NCO",找到其中的"ALTPLL",如图(所有的IP核查找过程都是这个)。

仔细观察可以发现,这个NCO被归类到了DSP的Signal generation中,也就是数字信号处理中的信号生成。双击打开,并输入文件名保存(不展示了)。由于不同人电脑的问题,打开的界面存在显示错位的情况,将窗口全屏就能解决。先看构建(Architecture)这栏。

一般情况下,generation algorithm(生成算法)选择的是small ROM,因为这个比较平衡;当需要较好的波形状况的时候,并且不担心内存资源占用的情况下,选择large ROM;当资源极度紧张的情况下,用cordic;利用DSP乘法器进行独特的运算,来匹配特殊的目标,选择Multiplier-Based。四种运算方式的幅频特性曲线分别如下四图所示。从这四张图的幅频特性曲线中就能看出,从精度的角度来说,large ROM ≈ multiplier-based > small ROM > cordic。




由于其他三种运算方式属于特殊情况,接下来我仅用small ROM的较为平衡的算法来举例说明。Outputs中的单双输出选项是指单输出的正弦输出和双输出的正弦余弦双输出。
再往下number of channels理论上是指几个独立通道的输出,即几个不同频率不同相位的通道输出。就目前我的观察来看,更改这一栏以后的模块芯片引脚数量不会变,功能上,依我自己的实践经验来看,之后的配置可以实现(只要资源够)各种频率相位的正弦信号。
number of bands的原理是一个通道,变换不同的频率和相位实现一个通道的输出。这里数值的变化从模块芯片上能够清晰地发现通道数量的增加,但是我没有具体尝试过,我还是习惯多配置几个不同频不同相的,利用选择器选择接接,或者是直接更改输入(后续会讲,这里可能比较抽象)。
换到下一页frequency(频率)的界面,如图。

还是从上往下看。phase accumulator precision(相位累加精度)这个的位数其实代表的是输入频率的精度(我的理解)。假设我设置32bits的精度,我输入的100MHz的时钟频率,那我输出的正弦信号的频率分辨率就是100*(10^6)/(2^32)=0.0233Hz。
angular resolution(角分辨率)具体改变的是什么我不清楚,因为从我自己的每次实践的时候来看,是没有什么变化的。magnitude resolution(幅度分辨率)这个的位数直接影响了合成的精度,类似于ADC转换的时候,相同参考电压下12位ADC的精读明显弱于24位的ADC(也可以拿DAC来举例,效果相同)。甚至在有些高速的DAC上,12位并行的DAC配置12位magnitude resolution的NCO,就能直接讲数据一一对应这输出了。
phase dithering(相位抖动)不怎么需要管。接下来的就比较重要了。Clock rate(时钟速率)这里的时钟速率尽量确定好就不要动了,50MHz的时钟就写50,100MHz的时钟就写100。desired Output frequency(期望输出频率)这栏其实是不需要填写的,这一栏的填写仅仅是帮助用户了解自己想要的频率转换成输入应该等于多少,即下面的phase increment value(相位增量)。这个相位增量是不能自己改的,但是改了期望输出频率,这个相位增量就会自己变,并同时变化的还有再往下的real Output frequeny(实际输出频率)。为什么我说这个期望输出频率不用填写呢,因为这个最后生成的模拟芯片里,要输入的其实是相位增量,位数为最开始设置相位累加精度的位数,因此,这个相位增量的计算为:(期望输出频率 / 时钟速率)*(2 ^ 相位累加精度),最后取整转换成二进制。
翻到最后一页,optional ports(端口选项)。frequency modulation(频率调制的这个我仍然不清楚具体意义,不勾选);phase modulation(相位调制)这个都勾选并更改里面的位数值直接影响相位变化的精度,这里勾上,方便后续的演示。最后确认一下一个重要的细节:1.频率精度(相位累加精度)32位;2.相位精度(相位调制)18位。

三、实例化
确认无误,记录一下模拟芯片的输入输出引脚,点击右下角的generate HDL...,跳出窗口点击generate,等新窗口都生成完毕,再点击窗口的close后,最后点击finish退出。然后我们要将配置后的文件加入到工程中。右键点击工程,选择settings(设置),点击add all,就能看到nco_ip加入到了工程当中来了。




点击apply(应用),再点击close(关闭)。然后开始写代码,代码如下:
module nco(
input wire clk,
input wire rst,
output wire [17:0] sine,
output wire value
);
parameter clk_freq = 100_000_000; // 设置时钟输入频率100MHz
parameter desire_f = 100_000; // 假定自己需要100kHz的正弦信号
wire [63:0] freq = desire_f * (2^32) / clk_freq; // 实际相位累加值为4294967(与理论相同)
parameter phmax = 360; // 最大相位360°
parameter desire_ph = 90; // 需要相位90°
wire [31:0] ph = desire_ph * (2^18) / phmax; // 实际相位输入65536
nco_ip u_sin1(
.clk (clk), // 时钟输入
.reset_n (rst), // 复位信号输入
.clken (1), // 时钟有效(使能)
.phi_inc_i (freq[31:0]), // 需要频率大小
.phase_mod_i(ph[17:0]), // 需要相位大小
.fsin_o (sine), // 输出信号
.out_valid (value) // 输出有效
);
endmodule
用程序员计算器计算一下频率(相位增量),如下图,结果与系统计算的一样,这也就说明了,想用多大的频率,其实只要更改一下输入时钟的频率、输入的相位增量,就能随便更改输出的频率的。输出的信号相位偏差也是如此。这也就是为什么之前说第一页architecture中的number of channel 和number of bands里面不用太在意,这里都是可以自己随便更改频率大小,输出通道数也可以用多次实例化来解决。

总结
上文的很多关于我实践的说法,都是基于我实际使用DAC、示波器、FPGA板看到的和总结 的,当然也存在可能有概念混淆的问题,也请多多包涵和讨论。
技术是开拓、完善、传承的过程。开拓的过程是艰苦,完善的过程是漫长的,传承的过程是延续的。愿技术能在传承的基础上开拓,能在开拓的道路上完善,能在完善的成果中继承,生生不息,永无止尽!
感谢阅读!