概述
NI 的可重配置 I/O (RIO) 硬件使开发人员能够创建自定义硬件,以在坚固耐用、高性能和模块化架构中执行许多任务,而无需了解低级 EDA 工具或硬件设计。使用 RIO 硬件轻松实现的此类任务之一是模拟波形生成。本教程介绍了使用 CompactRIO 硬件和 LabVIEW FPGA 模块输出线性波形的不同方法。
线性波形描述
线性波形是可以由具有线性斜率的方程定义的波形。线性波形可以用两个点来描述:起点和终点,以及位于连接这两点的线上的任何点。这些波形可以通过 CompactRIO 模拟输出模块逐点输出。例如,线性波形的一个示例是以 1V 增量从 -10V 斜坡到 10V,更新之间间隔 1 秒。
CompactRIO 的线性波形应用
CompactRIO 为工程师和科学家提供了可定制、坚固耐用的便携式硬件解决方案,用于生成线性波形。LabVIEW FPGA 模块与 CompactRIO 硬件配合使用,为开发人员提供了最大的灵活性。开发人员可以使用 CompactRIO 构建硬件模块,提供自定义任务、定时和触发。输出线性波形的能力使工程师能够在任何需要电压斜坡的应用中利用线性函数。例如,工程师可以通过生成电压斜坡来对被测设备的可接受电压范围进行测试。凭借 CompactRIO 的灵活性,他们还可以开发自定义模拟输入模块来分析响应。
CompactRIO 的灵活性使开发人员能够自定义其生成,以在执行过程中不断更改更新速率、增益甚至波形。此外,LabVIEW FPGA 模块还能够在同一 CompactRIO 硬件上以 25 纳秒的分辨率轻松自定义 FPGA 功能的定时和触发。如果应用需要线性响应与另一个因素(例如外部信号)的紧密同步,则此功能非常有用。此外,针对特定应用的 CompactRIO 定制通常可为开发人员和最终用户带来显着的成本效益。
CompactRIO 模拟输出模块的电压输出
LabVIEW FPGA 模块利用整数数学进行所有数学运算;因此,CompactRIO 的模拟输出操作必须利用所需输出电压的二进制代码表示。所需电压的二进制代码表示是通过将所需电压除以模块的代码宽度来确定的。模块的码宽(或步长)定义为系统可以输出的最小电压变化。码宽根据以下公式计算:
例如,cRIO-9263模拟量输出模块具有16位模拟量输出通道,范围为-10V至10V。因此,模块的码宽为 0.305176mV/步(20V / 2^16 步)。因此,要输出 5.0 伏,您需要将 5.0 V 除以 0.305176mV。该除法产生二进制值 16384。NI在每个模拟输出模块中包含一个 VI,可以为您执行此转换。该VI将被命名为Convert to Binary (cRIO-XXXX),其中XXXX是模块的型号。它将包含在位于\examples\FPGA\CompactRIO\cRIO-XXXX\目录中的 cRIO-XXXX 支持文件 LLB 中。该VI(图2)通常在FPGA VI中使用,将所需的电压值转换为该电压的二进制代码表示形式。您将向其提供您想要在标称值 (V)端子中输出的电压。校准终端是一个包含两个不同控件的集群:偏移 (nV)和LSB 权重 (nV/LSB)。这些控件用于确定电压的二进制代码表示形式,该电压通过二进制值端子返回。LSB权重 (nV/LSB)是模块的代码宽度,偏移量 (nV)对应于您可能想要识别的任何偏移量。
通过阵列运算生成线性波形
在 CompactRIO 中执行线性波形生成的方法有很多种。不同的方法范围从数组操作到更高级的内存读写操作。这一系列不同的方法为开发人员提供了许多可以在各种应用中使用的不同选项。生成任何波形的一种快速、简单的方法是将点包含在数组中并以特定时间间隔对这些点进行索引。
LabVIEW FPGA 模块中的数组
可以使用 LabVIEW FPGA 模块创建数组;但是,您只能使用固定大小的一维数组。您可以通过右键单击数组索引并选择“设置维度大小”,将任何数组常量、控件或指示器设为固定大小。如果您的值始终保持不变,请使用常量数组,因为它们在 FPGA VI 中效率更高。注意:为了优化编译时间,请避免使用超过 32 个元素的数组,因为它们需要 FPGA 上的空间。
创建 LabVIEW FPGA VI 以输出数组
在 LabVIEW FPGA 模块中开发任何应用程序的第一步是开发将在 FPGA 上运行(或综合)的 VI。该 VI(数组输出 VI)利用 LabVIEW 的自动索引功能对数组进行逐点索引。从数组中提取输出值,然后在 cRIO-9263 模拟输出模块的模拟输出通道 0 上输出。
该程序使用 LabVIEW FPGA 模块用户手册中推荐的序列结构来进行定时 I/O 操作。在第一个序列中,我们使用循环定时器功能来控制模拟输出的更新速率。对于CompactRIO,所有硬件时序都在我们的软件中定义,因此我们可以通过简单地更改更新周期(毫秒)控制中的值来更改执行期间的更新速率。该程序中的循环将以 1/更新周期 (mSec)定义的速率执行。cRIO-9263 向一个模拟输出通道输出数据时,最小更新周期为 3 微秒。这相当于每秒 333,333 次更新的最大更新速率。在程序的第二个序列中,我们使用模拟输出功能将所需电压输出到 cRIO-9263 上的通道 0。
创建主机VI来执行目标VI
大多数LabVIEW FPGA应用程序的第二步是创建一个主机接口VI,以与下载到硬件目标的FPGA VI进行通信。如图 4 所示,执行阵列输出 VI 打开对阵列输出 VI 的引用,该 VI 已通过“打开 FPGA 参考”功能下载到 FPGA 控制器。然后使用读/写控制功能设置VI的输出点数和更新周期(毫秒)控件的值。然后,通过使用“调用方法”功能和“运行”命令,该 VI 在 FPGA 上实际运行。它会等到VI运行完毕,然后使用关闭FPGA VI引用函数关闭引用并检查错误。
对于由相对少量的点定义的波形来说,通过数组进行索引来生成波形是完全可以接受的。请记住,数组中的每个元素都会消耗 FPGA 上的许多门;因此,大型数组对于 LabVIEW FPGA 模块来说并不实用。然而,FPGA 有 16KB 内存可用于存储。可以使用位于Functions>>FGPA Device I/O>>Advanced FPGA Device I/O面板上的 Memory Read 和 Memory Write VI 来访问该存储器。
使用 LabVIEW FPGA 模块进行线性插值
LabVIEW FPGA 模块为开发人员提供了一种非常强大的方法来定义线性波形,而无需创建值数组。借助 LabVIEW FPGA 模块,您可以使用线性插值 VI 轻松计算线性波形上的值。线性插值 VI具有三个输入:y0、y1和x (小数)。y0是插值间隔的下限,y1是插值间隔的上限。x(小数)表示区间[ y0 , y1 )内的评估点。x(小数)是一个无符号 16 位整数,表示 [0,1) 范围内的小数,对应于 [ y0 , y1 ) 创建的区间。例如,x(小数)值0将给出y0处的值(在y终端返回),x(小数)值 32767 将给出y0和y1之间的中间值。
使用该VI,您只需提供起点和终点以及插值评估点即可定义波形。例如,如果您想输出总共 5 个点,则可以输出y0 、 y0和y1之间的 1/4、1/2 和 3/4 处的值,然后输出 y1。该波形可以通过使用x(小数)值 0、16383、32767、49151 执行线性插值,然后输出y1处的值来创建。y1处的值被显式输出,因为线性插值在 [ y0 , y1 )区间上求值,该区间最后不包括y1 。因此,x(小数)值 65535 不一定会输出y1 处的值。
通过线性插值生成线性波形
您可以使用带有 LabVIEW FPGA 的线性插值 VI,通过提供y0(起点)和y1(终点)、要插值的点数以及x(小数)值来生成线性波形。插值位置可以通过编程方式确定,通常使用运行求和。输出线性波形 VI(如图 6 所示)就采用了这种方法。该VI将通过基于起点和终点控件的插值输出在点数控件中定义的点数。线性插值最初将在x(小数)值为 0时完成,并且该值将在每次迭代中增加插值增量值。
您可以通过手动提供控制值并运行VI来执行该VI。您还可以开发一个在主机上运行的VI,该主机以编程方式执行该VI并与之通信。执行 - 输出线性波形 VI(如图 7 所示)是在主机上开发和运行的。它使用“打开 FPGA 参考”、“读/写控制”、“调用方法”和“关闭 FPGA 参考”功能。这些函数提供对 FPGA 的所有必要控制,例如下载、读取或写入数据,以及启动或停止 VI。此方法允许您快速更改执行所需的控件的值。您甚至可以将此 VI 应用于其他 VI,以在 cRIO-9263 上执行输出,作为更大项目的一部分。
插值算法示例
前面的示例重点介绍了基于线性插值生成线性波形的通用方法。您可以编写自己的算法来确定将沿起点和终点定义的线输出的值。一种常见的算法允许您在起点和终点之间添加均匀间隔的点,以输出更高分辨率的波形。可以使用主机 VI 来确定 处的评估点,该 VI 将确定 0 到 65535 之间的间距,以适应起点和终点之间的点。您还可以在FPGA VI中使用一种算法,以编程方式确定评估点。该方法使您能够简单地告诉 FPGA 代码在起点和终点之间放置多少个点,从而确定波形的采样率。
利用整数的二进制表示和线性插值VI可以轻松实现这样一种算法。如果您回顾一下使用 LabVIEW FPGA 模块进行线性插值部分,我们讨论了x(小数)控制。它是一个 16 位整数,表示将发生插值的两点之间的位置。回想一下,值 0 表示起点 ( y0 ),值 32767 表示y0和y1之间的中间点。x(小数)的完整范围是 65535。该值的一半是 32767,四分之一是 16383,八分之一是 8191。如果我们查看这些数字的二进制表示形式(图 8),我们会看到二进制这些数字的表示只是前一个数字向右移动一位(以二进制形式分成两半)。
我们可以利用这种二进制移位来快速确定x(小数)的值应该是什么,以在y0和y1之间添加 1、3、7、(2^n -1) 等点。请注意,如果我们要添加 1 个点,则x(小数)值为 32767(65535 右移一次)。如果我们添加 3 个点,则x(小数)值将分别为 16383、32767 和 49151,分别表示y0和y1 之间的 1/4、1/2 和 3/4。如果仔细观察这些数字的二进制表示形式,您会注意到 32767 是 16383 + 16383,在最低有效位进行 1 或运算,49151 是 32767 + 16383,在最低有效位进行 1 或运算。对于要在起点和终点之间添加的任何2^n - 1 个点,该图案表面相同。因此,如果您想在起点和终点之间添加2^n-1 个点,您可以将 65535 右移n 个位置以获得第一个插值点。然后,通过添加该初始值并与值 1 进行“或”运算来确定其余点。当达到可插值的最大值 65535 时,插值值将替换为终点处的值。进行此替换的原因是因为插值间隔 [ y0 , y1 ) 不包括结束值,因此我们将输出配置为最终更新的结束点。
线性输出 - 添加点 VI(如图 9 所示)使用该算法输出起点、中间的 2^n -1(对于 n = 0:15)点以及终点。要添加的点(2^n-1)控件确定起点和终点之间包含的点数。请注意,它被取反,然后值 65535 移动了这个量,相当于右移n位。while循环内的Sequence结构包括两个序列。第一个序列(未显示)有一个循环定时器VI和一个周期更新(毫秒)。该序列用于创建点的定时更新率,类似于本教程中的其他两个示例。第二个序列是完成实际输出的地方。每次迭代,插值在传递到x(小数)的位置完成线性插值VI的终端。该值在 cRIO-9263 模块的通道 0 上输出。通过将初始移位 65535 的结果相加来计算下一个插值,然后将该结果与 1 进行或运算并连接到移位寄存器以进行下一次迭代。在最后一次迭代时(当插值点为65535时),输出End Point控件中的值,程序结束。
您可以再次开发一个可在主机上运行的 VI,该主机将以编程方式执行该 VI。下面的 VI,执行线性输出 - 添加点,在主机上开发并运行。通过主机上的该VI,您可以多次调用该VI,而无需手动与FPGA上的VI连接,并且可以将该VI用作其他VI中的子VI。
使用线性插值生成分段线性波形
分段线性波形是由线性波形组合构成的波形。整个波形不是线性的,但各个部分包括线性波形。您可以使用前面几节中描述的方法来输出分段线性函数。您可以轻松创建一个通过各个点描述波形的数组,并且可以使用“使用数组操作生成线性波形”部分中所述的数组输出VI输出该波形。但是,请记住,数组会消耗 FPGA 上的门,因此您不应创建超过 32 个元素的数组。因此,您可以使用阵列方法来输出波形,但请记住,输出点数是有限的。
您可以组合使用“使用阵列运算生成线性波形”和“使用线性插值生成线性波形”部分中使用的 VI 来创建能够输出数千个点的波形发生器。我们已经描述了如何通过起点和终点定义线性波形,并通过线性插值确定这两点之间的点。您可以将此方法扩展到分段线性波形,因为您可以通过每个线性段的起点和终点定义分段线性波形。然后,您可以通过在线性段的起点和终点之间进行插值来确定每个段中的点。
插值的位置可以通过许多不同的方式来确定。我们描述了一种利用 x(小数)值的二进制表示来确定插值位置的方法。我们可以利用该算法通过将其应用于每个线性段来输出重复的分段线性波形。该应用的一个示例可以在分段线性输出 - 添加点 VI 中看到,如图 所示,它输出连续波形。
该VI包含两个子VI,计算X分数和计算索引,使其能够循环遍历每个线性段并在每个线性段的起点和终点之间输出点。计算 X 分数 VI 接受插值位置(当前 X 分数)和插值位置之间的距离(逐点增量)。计算 X 分数 VI 添加逐点增量,该增量是通过将 65535 移动n位来确定的。然后将此加法与 1 进行“或”运算,就像之前的算法一样。当我们输出单个线性波形时,当x(小数)值达到 65535 时,我们会输出终点 ( y1 ) 。但是,我们现在输出连续的分段线性波形,因此我们可以输出下一个波形的起点如果我们假设波形中没有不连续性,则分段。该值实际上应该与之前的y1位于相同的数组位置。因此,当我们到达插值位置 65535 时,我们只需将插值位置重置为 0,并将起点和终点加 1。计算 X 分数 VI 通过检查添加增量的结果是否等于 65535 来考虑此翻转。如果是,则它将下一个插值位置(下一个 X 分数)重置为 0,并通过翻转?返回 TRUE 值。终端,连接到计算索引VI。
计算 X 分数 VI
计算索引VI使用计算X分数VI 的结果来确定插值的下一个起点和终点(低索引(输出)和高索引输出)。如果发生翻转(意味着最后一段中的所有点都已输出),则低索引和高索引将递增。但是,由于我们输出重复波形,因此必须将索引回滚到数组的开头。当每个传递数组中的最后一个元素时,通过将低索引和高索引重置为 0 来完成此翻转。此更改是通过将值与数组的大小进行比较来完成的。
计算索引 VI
您可以轻松修改此示例,通过识别高索引何时位于波形点数组 中的最后一个点来输出波形一次。然后,您可以在输出该段中的最后一个点后将while 循环上的终止条件设置为TRUE 。请记住,您还可以使用 FPGA 上的存储器来存储分段线性波形中的点。如果波形中有许多线性段,可以轻松修改此示例以从内存中读取起点和终点。
FPGA上使用DFD和Coergen实现多相插值FIR滤波器
在不增加采样率的情况下将采样率从 50M 提高到 250M 或更高不是很好吗……通过数字滤波器设计 (DFD) 工具集和 Coregen 可以做到。
项目详情:
- 正弦波生成频率为 100 kHz,采集数据为 50 MHz(500 个样本/周期)
- 项目目标 - 在 250MHz 下每周期 2500 个样本(无法更改 50M 的硬件采样)以实现 0.0004 秒的精度。
假设我们有一个周期信号,每个周期仅包含 50 个点(见下图)。我需要检测哪个样本在 0.0004 秒内超过了某个阈值
原始信号:
有多种方法可以实现此行为:
1. 您可以对原始信号进行零填充,然后将零填充信号与有限脉冲响应(FIR) 滤波器抽头进行卷积以生成信号。
零填充无卷积
零填充卷积
2. 将 FIR 生成的滤波器抽头抽取为“x”插值因子。一旦滤波器的设计符合我们的规格,我们就会按照我们希望插值的因子来减少滤波器抽头的数量。例如,在本例中,我们以 5 倍插值,将采样率从 50MHz 提高到 250MHz。其概念是,如果每组滤波器抽头对同一数据点进行操作,则卷积的加权平均值会产生点的投影。下面列出了结果的图像。
单点多个 FIR 滤波器
框图实现
**注意:滤波器抽头的数量应能被插值整除,否则点的投影将不那么准确。
多个 FIR 滤波器使用单点的结果
一旦我们在 Windows 仿真中确定滤波器工作正常,我们就可以使用 FIR 滤波器中的抽取滤波器抽头供Coregen在 FPGA 上获得类似的结果。为了使用 FIR 抽头,我们需要将它们写入各自的 .coe 文件(将系数写入文件 VI 包含在 .zip 中)。在此示例中,由于我们插值了 5 倍,因此我们将有 5 个独立的 .coe 文件。生成 .coe 文件后,我们就可以在 Coregen 中使用它们。
**注: 在本例中,我们将使用用于 National Instruments LabVIEW 2010 VI 的 Xilinx IP。
在 Coregen 中使用 .coe 的步骤:
1. 创建/打开 LabVIEW 项目。
2. 使用或模拟FPGA 目标。
3. 在 FPGA 目标上打开一个新 VI。
4. 在框图上导航至“Programming >> Xilinx Coregen IP >> Digital Signal Processing >> Filters >> FIR Compiler”
5. 双击 FIR Compiler VI 以启动 Coregen UI。
6. 选择 FIR 滤波器的名称以及您希望 Coregen 生成的文件所在的位置。
7. 单击启动 Xilinx 核心生成器。
8. 单击选择源并选择 COE 文件。
- 单击浏览...导航至 Set1 (\\DFD and COREGen Explanation\LabVIEW Code\FPGA\Coefficents\Set1.coe)。
9. 过滤器类型:单速率。
10. 设置输入采样频率和时钟频率(均使用顶级默认时钟 = 50MHz)。
Coregen 第 1 页
11. 单击下一步移至第 2 页。
12. 单击下一步移至第 3 页。
13. 选中新数据 (ND) 和时钟启用 (CE)。
Coregen 第 3 页
**注: 根据编译标准,CE 可能会因高扇出和更高的时钟速率而导致时序违规。CE 终端不是必需的,但可用于在 IP 核执行时进行门控。您还可以在 IP 核执行时使用 ND 进行门控。
14. 单击“下一步”移至第 4 页。
15. 单击“生成”
- Coregen 将编译过滤器
16. 单击 Xilinx FIR Compiler v5.0 Node Properties 的 Next Page 2
17. 单击 Xilinx FIR Compiler v5.0 Node Properties 的 Next Page 3
18. 更改数据输入 (din) 和数据输出 (dout) 数据类型
- 根据要过滤的输入信号相应调整输入数据类型 (I16)
** 您需要调整 dout 以适应字和整数长度 (FXP<+/-32,17>)
Xilinx FIR 编译器 v5.0 节点属性第 4 页
19. 对每个 .coe 文件重复步骤 4-18
完成的 FPGA 框图
**注意: 单周期定时循环(SCTL)不支持模拟输入 IO 节点,除非它位于 FlexRIO 目标上。
20. 在编译 FPGA 之前,您可以测试在 Coregen 中设计的滤波器。
- 参见 FPGA_Main_Simulation.vi
- 将VI从FPGA Target移动到Windows Target进行仿真
Windows Coregen 模拟
前面板
框图
21. 验证仿真后,您可以根据需要调整 FPGA VI 并进行编译。
**编程注意事项:
1. 在另一台机器上打开 Coregen FIR 块时,您需要重新编译它。
2. DFD 生成的滤波器抽头数量很大程度上取决于滤波器类型及其特性。
3. 默认情况下,Coregen 将使用板载 DSP,但是如果您用完 DSP,则可以将滤波器架构更改为使用分布式算法。
4. 循环延迟由Coregen决定,应注意确保所有滤波器同时输出(可能需要向反馈节点添加延迟)。
结论
CompactRIO 为工程师和科学家提供了可定制、坚固耐用且便携式的线性波形生成解决方案。LabVIEW FPGA 模块用于对 CompactRIO 硬件进行编程,为开发人员提供了最大的灵活性。它使开发人员可以自由地创建具有硬件定时功能的独特模块。通过利用 LabVIEW FPGA 模块中的数组运算和线性插值功能,开发人员可以轻松创建输出线性波形的波形发生器。LabVIEW FPGA 模块具有更多功能,可让您执行非线性、周期和任意波形的输出。
PC 端 Sinc 插值实例
下列有香农插值公式实现了一个简单的Sinc插值算法。
给定实数序列x [ n ],连续函数
在左图中,灰色曲线显示了时域中的函数 f(t),该函数以稳定增加的采样率进行采样(黑点)并重建以产生金色曲线。右图中,红色曲线显示的是原函数f(t)的频谱,没有变化。频谱中的最高频率是整个频谱宽度的 1/2。稳定增加的粉红色阴影代表重建函数的频谱,随着采样率的增加,它逐渐填充更多的原始函数的频谱。当重构函数的频谱包含原始函数的整个频谱时,其宽度是最高频率的两倍,此时重构波形与采样波形相匹配。
梯度数组插值
VI计算元素之间的差异,从起始值到结束值进行线性插值。这有助于计算颜色梯度或预测线性尺度上数据集的缺失值。
图形插值-x轴值不相等
使用随机数生成器进行模拟,并将它们组合成定制的波形图。使用数组 max 和 min.vi 提取 x 轴的最大和最小值,这些值被传递到 Ramp Pattern.vi 。该VI的输出与原始采集数据一起用于插值1d.vi,将所需数据输出到构建波形函数,然后该函数输出波形。为了使绘图彼此相减,dt 值(x 轴)通常需要匹配,在这种情况下,对齐波形(单次)。vi 获取数据并允许进行减法。此方法允许进行减法,而不会在减法后导致意外的数据输出。
对Nan数组值进行数据插值
接受一个包含NaN(不是数字)值的数组,并插入新值来填充NaN数据。
该VI设计用于处理具有恒定增量X值的数据,该算法不适用于生成数组中的第一个或最后一个数据点。 此示例很有用,因为使用 NaN 值插入数组中的新值。
使用 对于迭代每个数组值,索引数组将查找 NaN 值。While 循环继续迭代数组,直到找到有效数据(查找长 NaN 链)。之后,For 循环生成缺失值(最后一个已知值 + 步长*(i+1)),最后用数组子集替换 NaN 值。