使用LabVIEW FPGA开发数字通信接口

概述

使用不支持或自定义的数字通信协议开发测试应用程序的工程师可使用LabVIEW FPGA模块在基于FPGA的R系列可重配置I/O硬件上快速实现不同的通信接口或进行原型验证。与设计和创建ASIC等自定义硬件或编写在FPGA上运行的VHDL代码不同,使用LabVIEW FPGA Module可方便地测试和调试新功能,无需专门的开发工具。

内容

LabVIEW FPGA

在汽车、航空和其他一系列行业中开发测试系统的一个常见需求是开发或实现测试系统和其他设备之间的数字通信接口。这些设备包括其他测试系统或计算机、仪器、DUT、ECU等底层系统组件。

理想情况下,开发新的测试系统时,需为数字通信协议提供现成的接口,其中包含应用程序开发工具的易于使用的驱动程序。然而,在许多情况下,这不是现实。可能需要开发与外部设备通信的板卡或其他接口。这可以通过开发自己的自定义硬件来实现,例如设计自己的ASIC,或者至少焊接基于现成组件的新板卡。但通过可重配置I/O硬件和LabVIEW FPGA Module,您可以仅使用LabVIEW及其图形化编程工具设计自定义硬件。自定义硬件设计加载至可重配置I/O硬件的FPGA,以创建符合需求的自定义接口板卡。

我们将介绍如何使用LabVIEW FPGA Module在自定义接口板上实现多种通信协议。这些技术适用于插入式PCI和PXI可重配置I/O卡(R系列)以及提供坚固工业就绪外壳的CompactRIO平台。

数字通信协议

测试系统中使用的通信协议范围很广,从极其常见的接口(例如,RS-232和GPIB)到由单个供应商和应用程序开发人员实现的自定义协议。常见但不太为人所知的接口包括SPI(串行外设接口)和I2C(内部集成电路),用于在电子设备和系统中进行通信。这些协议虽然在应用程序中很常见,但可能并不总是为各种平台和软件环境中的通信接口所支持。对于此类需求,LabVIEW FPGA和可重配置I/O平台是提供经济高效的解决方案的理想工具。

数字通信协议可按应用领域、行业以及协议本身的性质和技术规范分组。我们将在以下章节详细讨论不同协议的技术细节和性质,并演示如何使用LabVIEW实现这些协议。关于应用程序和行业,以下是基本类别和范例协议,其中包含使用LabVIEW FPGA解决的通信接口

件/IC通信
  • 电子产品设计:SPI、I2C、JTAG、PS/2等
系统通信
  •   航空航天:MIL-STD-1553、ARINC-429等
  •   汽车:CAN, MOST, KWP, 1939, …
通信
  • 卫星和航空航天通信:PCM/遥测
消费电子产品
  •   数字音频:S/PDIF, I2S
定制
  • 设备特定的实现

协议分类

数字通信协议可根据其技术定义和要求进行分类。以独立于应用程序的方式看待协议有助于专注于使用现有开发工具选择最佳实现时非常重要的细节。本文档的其他部分将重点介绍这些分类标准以及如何在FPGA上实现不同的协议组。

根据发送方和接收方之间的数据信号数量,大多数数字通信接口分为两类:串行和并行。如在一条数据线上传输数据,由于所有数据位均按顺序或串行传输,因此属于串行数据链路。并行数据链路包含多条数据线,通常是8条数据线的倍数。数据并行传输,通常每次一个字节或一个字。传统上,串行(如RS-232)和并行(如GPIB)通信链路都与并行通信链路配合使用,在相同的时钟速率下提供更高的数据传输速率。随着单条数据线上可能的比特率增加至兆位和千兆位范围,串行链路在市场上已变得更加常见和主导,因为它提供了更具成本效益的解决方案,降低了接口组件(如线驱动器)的成本以及布线成本。在串行和并行通信接口中,可能有额外的信号线用于时钟和触发以及控制和命令通信。

物理通信接口的第二个基本分类是数字信号线的电压电平和接地参考。数据位和其他信息由信号线的不同电压电平传输,但不同类型的接口之间使用的电压电平可能有很大差异。在单端数字信号中,信号线的电压电平是相对于公共参考地测量的。差分数字信号由两个无参考信号组成,两个信号线之间的电压电平表示信号值。差分信号对信号线拾取的噪声具有更好的抗扰度,通常可用于传输较长距离的信号。数字通信中使用的特定电压电平由TTL、CMOS和LVDS等标准参考,不在FPGA上的协议实现中处理。可重配置I/O硬件的数字输入和输出使用TTL/CMOS兼容电压电平。 如果需要其他电平,则需要在接口硬件和通信链路之间插入信号转换器/转换器。

信号位带

我们在分析协议和思考其在LabVIEW FPGA中的实现时,需要识别协议使用的不同信号线以及每个信号的用途。协议使用的信号线数量将决定LabVIEW项目中需要配置的硬件资源数量,以及LabVIEW程序框图中使用的数字线数量。下列定时框图(图1)显示了SPI(串行外设接口)协议,显示了该协议实现中需要管理的三条信号线的使用情况。

单数据线SPI协议的简单定时框图

图1:单数据线SPI协议的简单定时框图

在开始实现时,我们认识到要将定时框图转换为相应的LabVIEW FPGA代码,我们需要关注两件事:打开和关闭数字线以及在设置不同数字线的状态之间等待适当的时间。在读取数字协议的应用中,我们关注的是读取数字线的状态,并记录每条信号线上转换的时间间隔。通常,输出数字协议更易于实现,因此,我们首先处理此情况。

时钟

协议分类的下一个标准是协议使用的时钟源。两大类是同步和异步协议。同步协议在协议中包含特定的定时信号,异步协议使用定义的比特率定时。

同步协议的典型范例是上述定时框图中显示的SPI协议。该协议包含一个专用的时钟信号,允许任何接收方将时钟线用作读取或写入数据线的时基。也许最著名的异步协议是许多PC上使用的串行(RS-232)总线。使用串行总线时,需指定与之通信设备的波特率(比特/秒)。发送方和接收方使用波特率以相同的速率更新或读取数据信号。由于通信两端的时间源不完全相同,异步通信数据包的长度受到限制,以避免位错。异步通信需要频繁地重新同步,以允许微小的定时差异。另一方面,同步通信协议可以连续通信,因为它们在每个数据位上同步。

将时钟信号实现为同步协议的一部分时,FPGA时钟用于确定协议的时基并相应地更新时钟信号。

LabVIEW FPGA程序框图生成10 us(100 kHz)时钟信号

图2:LabVIEW FPGA程序框图生成10 us(100 kHz)时钟信号

对于异步协议,不会生成单独的时钟信号,但FPGA时钟和定义的波特率用于确定何时更新或读取数据信号。 

LabVIEW FPGA程序框图更新4800波特的数据线

图3:LabVIEW FPGA程序框图以4800波特更新数据线
(8333 FPGA时钟周期 = 208.3 us = 1/4800 Hz)

编码/解码脉冲代码调制

根据时钟信号或定时规范指定的速率,数据在数据信号上编码或从数据信号读取,然后解码。  数据编码有多种不同的格式。最常用的方法是通过数据信号的一个或多个状态来表示数据位的值。该类编码方法称为脉冲编码调制(PCM)。

PCM方法的子集,其中数据位由数据信号的单个状态表示,称为不归零(NRZ)。 例如,高数据信号表示1位,而低数据信号表示0位。该方法称为NRZ-L,因为数据信号的电平表示数据位。在某些协议(如RS-232)中,该逻辑被反转,高状态表示0,低状态表示1。这称为NRZ-I(逆)。其他NRZ子类型为NRZ-M(标记)和NRZ-S(空格),其中'1'数据位值表示数据信号的变化(NRZ-M),'0'表示数据信号的变化(NRZ-S)。

图4:NRZ(非归零)编码方法

脉冲编码调制包括另一组编码方案,将时钟信号和数据信号合并为一条数据线;每一位由数据信号的多个状态表示。这些编码称为双相位编码,常见的曼彻斯特编码模式是双相位编码的一种。

其他更高级的编码方案包括不同形式的脉冲宽度编码。常用的脉宽调制(PWM)方案将模拟值直接转换为恒定频率脉冲序列的可变脉宽。其他形式的脉冲宽度编码使用两个不同的脉冲宽度来表示传统位序列中的0和1位。

 

LabVIEW FPGA中的实现

既然我们已经了解了描述常见协议的基本术语,现在可以看看如何在LabVIEW FPGA中实现协议。为了在LabVIEW FPGA中实现协议,我们通常首先遍历定时框图,并将不同信号线的状态变化以及这些变化之间的定时转换为相应的LabVIEW函数和结构。数字线的每次状态改变通过FPGA I/O节点实现,定时通过LabVIEW FPGA定时函数(循环定时器和等待)实现。定时框图中重复多次的步骤通过For或While循环实现。对于更广泛的协议,函数和结构组可封装在子VI中,以允许代码重用并使代码更具模块化和可管理性。

SPI输出

作为第一个范例,我们实现了上图1所示的SPI定时框图。 使用SPI协议的通信由两个设备间发送的数据包组成。在SPI通信中,主设备控制ChipSelect信号和时钟信号。可能有一个或多个从设备具有从主设备到每个从设备的专用ChipSelect信号线,以及所有设备的公共数据线。如应用程序包含主设备和从设备之间的双向通信,通常使用两条数据线:主设备输出从设备输入(MOSI)和主设备输入从设备输出(MISO)。在本范例中,我们只关注一条数据线。

两个设备间SPI通信的连线图

图5:两个设备间SPI通信的连线图

数据包传输始终由主设备通过置被寻址的从设备的ChipSelect线有效来启动。通常,ChipSelect线处于活动低电平,因此当系统空闲并被主设备拉低以开始传输时,它将保持高电平状态。在置ChipSelect信号有效后,主设备将更新数据线,然后切换时钟线以传输每个数据位至从设备。数据包的内容取决于应用程序,由开发人员和设备设计师定义。

在相应的LabVIEW FPGA程序框图中(见图6),每次数据传输都是通过设置“写入”布尔控件开始的。这将导致执行True分支。最初,通过驱动数字输出为低,置ChipSelect线(SPI CS*)有效。同时,数据值“数据输出”被转换为相应的比特序列(布尔数组)。1微秒(1us)等待函数为从设备准备后续数据位提供一定时间。然后在For循环中重复16次更新数据线和切换时钟线。布尔数组的每一位被输出至数据线(SPI DOUT),时钟线(SPI SCLK)被设置为先高后低。循环定时器函数允许循环以2 us间隔运行,等待函数控制时钟信号高相位的长度为1 us,生成500 kHz 50%占空比的时钟信号。生成全部16位后,芯片选择和数据线将返回空闲状态,并插入5us最小空闲时间等待函数。此时FPGA已准备就绪,可以执行下一个写入命令。

简单SPI输出实现的LabVIEW FPGA框图

图6:简单SPI输出实现的LabVIEW FPGA框图

根据使用的SPI设备和应用程序规范,任何通信参数(例如,待传输的数据位数、时钟信号的频率等)均可在程序框图上进行调整,或通过LabVIEW FPGA VI前面板上的控件进行动态控制。

如有必要,可使用设置输出启用FPGA I/O方法节点,通过编程设置应用程序中每条数字线的方向(见图7)。

LabVIEW FPGA初始化数字线

图7:LabVIEW FPGA对数字线的初始化

SPI输入

实现协议的输入部分会带来一些独特的挑战,因为代码需要更灵活地检测和处理协议。代码不是更新数字输出线并插入适当的延迟,而是监控不同数字线的状态,并在必要时测量信号线上特定转换之间的时间间隔。

下列范例显示了SPI输入的基本实现,与上一个SPI输出范例相对应。处于空闲状态时,FPGA监控ChipSelect线并检测下降沿。响应下降沿,开始监控时钟线。对于时钟信号的16个上升沿,FPGA读取数据信号并将位值存储在预分配的布尔数组中。数据包结束时,16个时钟周期后,布尔数组被转换为整数数据值,并在VI前面板上显示。

简单SPI输入实现的LabVIEW FPGA框图

图8:简单SPI输入实现的LabVIEW FPGA框图

由于SPI协议的性质,该范例无需进行任何时间测量,因为所有定时都由协议中的边沿直接控制。

实现注意事项

在本节中,我们将讨论使用LabVIEW FPGA开发数字通信协议时需要考虑的其他主题。

电极开路/开路信号线

在上一个SPI范例中,每条信号线仅由一个设备驱动。但是,在许多协议中,根据总线或通信的状态,信号线可能由多个设备驱动或控制。这允许多个设备在总线上发起传输或使用同一数据线发送和接收数据。通常可通过集电极开路/漏极开路实现。在该配置中,设备只能驱动或拉低信号线,但当设备希望将信号线设置为高或不驱动信号线时,设备将允许信号线浮动。除了连接至信号线的每个设备外,信号线还带有一个连接至设定电压的上拉电阻,以在未连接设备将信号驱动为低电平时建立信号的高电压。使用该设置,任何设备都可以将线驱动为低电平,而不会在不同设备间产生电压竞争。此类信号通常被定义为有效低电平,即总线空闲时数据线处于高电平状态,设备将通过将数据线拉低来置信号有效。有效低电平信号通常由信号名称上的横杠或信号名称后的星号表示,如前面范例中使用的ChipSelect* (SPI CS*)信号。

为了在LabVIEW FPGA中实现集电极开路信号,我们使用控制数字线方向的功能在驱动低电平和让它浮动之间切换。在LabVIEW FPGA中,I/O方法节点用于启用或禁用数字输出线。当线被禁用时,FPGA不驱动数字线并允许其高浮起。为了在启用时将线驱动为低电平,我们将输出数据设置为FALSE。输出数据是一个软件寄存器,无论数据线被设置为驱动还是浮动,寄存器的值都保持不变。

下图为LabVIEW FPGA中用于I 2C(集成电路)协议的集电极开路配置范例。I2C用于SPI的类似应用,与EEPROM、ADC、DAC等不同类型的集成电路通信。I2C总线只有两条信号线(时钟和数据),每条都是集电极开路线。要为通信选择正确的接收方,发送方首先发送一个指定接收方设备的唯一设备地址。

要初始化用于集电极开路通信的VI,禁用两条信号线(SCL和SDA),使总线进入空闲状态。每个信号的输出数据设置为FALSE。从此时起,使用设置输出启用方法控制每条线的状态,启用线并将其驱动为低电平,或禁用线并将其浮动为高电平。

图9:配置两条数字线用于集电极开路通信

要在I 2C总线上发起传输,发送方先将数据线(SDA)驱动为低,再将时钟线(SCL)驱动为低,从而发送开始条件。

图10:在总线上实现I2C开始条件

要在I 2C总线上传输数据位(见图11),发送方更新数据线(第1帧),然后将时钟线切换为高(第2帧)和低(第4帧)。

在I 2C协议中,每个数据字节(8位)被传输后,额外的时钟周期被插入通信中,以允许接收设备确认上一个字节已成功接收。为此,接收器在第9时钟周期内将数据线拉低。在本范例中,发送方通过发送高数据位并在时钟周期内检查数据线(第3帧)的实际状态来实现。尽管发送方让数据线浮动到高电平,但实际数据值应该为低,因为接收方将数据线拉低以确认最后一个数据字节。

图11:在I2C总线上传输数据位

OSI参考模型

 

OSI(开放系统互连)参考模型表示通信协议的不同逻辑层,包括使用通信协议的应用程序。它用于更好地定义和理解协议和网络的不同方面。

图12:  OSI参考模型

第1层是处理电气和机械细节的物理层,提供通过外盒发送数据的功能。简单异步数据包在该层运行。

第2层提供位和字节级数据的基本定义,以及NRZ以外的调制和同步。

下几层涉及寻址、数据包的定义、错误检查等。

从范例中可以看到,FPGA运行在第1层物理层,直接与电学上的每条数字输入和输出线交互。这意味着作为LabVIEW FPGA中数字通信协议的开发人员,我们负责与协议和应用相关的参考模型的所有层。对于许多较简单的协议,只需比我们迄今所做的编程稍多一点,即在协议层上添加一个接口,允许应用程序与FPGA交互并使用协议。但是,对于更高级的协议,可能需要进行更多的编程才能提供这些中间协议层,例如总线级别的错误检测和处理、数据包的生成和解析、不同类型数据包的处理等。

分辨率

我们已经看到,定时在开发协议实现中起着非常重要的作用。数据线和时钟线需要以精确的间隔更新。 在解码协议时,精确测量这些相同的间隔非常重要。开发通信协议时务必牢记FPGA的定时特性,以确保实现满足协议的要求和规范。

FPGA使用基准时钟频率,所有定时函数使用相同的时钟频率。对于LabVIEW FPGA,默认的FPGA时钟频率为40 MHz,即每个时钟周期和时间单位为25纳秒(ns)。在LabVIEW FPGA中,我们可以指定定时函数的时间间隔,单位为毫秒、微秒和时钟滴答。每个时钟滴答对应一个时钟周期或25 ns。 这意味着指定延迟或循环迭代时间的最高分辨率为25 ns。虽然这听起来非常准确,但我们取反并确定可以生成的可能频率时,可以看到它在更高速率下的效果。

如需为数字线生成1 MHz的更新速率,可使用循环定时器函数设置40个时钟滴答。

1 MHz => 1 us = 1000 ns
1000 ns / 25 ns每滴答 = 40滴答

如果要1.25 MHz的更新速率,该怎么办?

1.25 MHz =>0.8 us = 800 ns
800 ns / 25 ns每滴答 = 32滴答

对于1.25 MHz,我们使用32个时钟滴答的延迟。如果要1.1 MHz的更新速率,该怎么办?

1.1 MHz => 0.9091 us = 909.1 ns
909.1 ns / 25 ns/滴答 = 36.36滴答

由于无法选择特定的部分时钟周期,我们必须选择36个时钟周期,即900 ns的延迟,对应于1.111 MHz的频率。下一个可使用的较低频率是37时钟滴答或1.081 MHz。可以看到,使用40 MHz FPGA时钟,我们可以在1.081 MHz或1.111 MHz更新数字线,但不能在两者之间更新。

在LabVIEW项目中使用FPGA属性和时钟资源,可选择将FPGA时基频率更改为80或120 MHz。这将以更高的频率操作FPGA并提高定时函数的定时精度。但是,较高的时钟频率也会降低FPGA VI程序框图上使用的复杂度,并成功编译FPGA。

计划实现定时协议时,需确定FPGA的定时分辨率是否足以满足协议和应用程序的需求。这一点对于生成协议所需的定时尤其重要,如上述计算所示。对于读取和解码协议,以高于协议最高更新速率的速率读取信号线就足够了。为确保安全,采样率至少应为更新速率的两倍。

如果我们需要对输入信号进行时间测量以解码协议中包含的数据,则需要进行额外的计算以确定精确解码协议所需的定时分辨率。

状态机

状态机可用于实现数字通信协议的编码或解码。在LabVIEW中,使用While循环和条件结构可方便地实现状态机。 

状态机将定时框图分解为独立的步骤,在LabVIEW FPGA程序框图中可将每个步骤转换为独立的状态,有助于通信协议的开发。这隔离了每个编程步骤,降低了实现中任何一个部分的编程复杂度。此外,它还简化了通用操作(例如,错误和异常处理),允许用户从协议执行的任意位置跳转到特殊的错误处理状态。有时,协议规范是通过状态机编写的,可直接转换为LabVIEW状态图。

对于图1中的SPI定时框图,下列步骤是将定时框图分解为状态机的一种方法。

  • 设置芯片选择低
  • 设置数据(0)
  • 设置时钟为高
  • 设置时钟为低
  • 设置数据(1)
  • 设置时钟为高
  • 设置时钟为低
  • (为位2-15重复数据和时钟)
  • 设置芯片选择高

我们注意到有五个独特的步骤,尽管其中一些步骤对每个数据位重复。 最初,我们在For循环中实现了这些重复步骤。在状态机中,我们为每个步骤创建一个唯一的状态,然后依次执行。对于更新数据线和切换时钟线的三个重复步骤,我们配置状态机在继续最后一步之前重复该序列16次。在LabVIEW状态机中,使用While循环移位寄存器中的计数器实现。

使用周期循环

状态机架构的另一个优点是允许在LabVIEW FPGA单周期定时循环(SCTL)内实现协议实现。SCTL提供更快的LV FPGA程序框图执行速度,允许循环的每个周期在一个时钟周期内执行。这使我们能够更新FPGA时基频率的信号线。SCTL还优化了代码生成,使FPGA上的代码更高效,占用更少的FPGA资源。但是,SCTL内部实现的代码有若干限制。例如,每个SCTL循环只能访问一条信号线一次。因此,我们需要定义状态机的状态,即每个状态仅读取或更新一条信号线。

下图(图13)显示了使用SCTL内部状态机的SPI输出协议的实现。空闲状态等待写入命令开始输出下一个数据包。它还将数据值转换为用于输出操作的布尔数组。下一步设置CS置ChipSelect线有效。然后开始输出数据位。在重置时钟中,我们使用下一个位值更新数据线并重置时钟信号。在设置时钟中,设置触发接收器读取数据信号的时钟信号。在该对状态中,我们递增移位寄存器中的计数器,移位寄存器从布尔数组中选择正确的数据值发送至数据线。输出第16位后,转换至重置CS状态,将所有信号线重置为总线空闲状态。然后返回空闲状态,等待下一个写入命令。

图13:SPI输出在单周期定时循环中的实现

调制

双相编码或调制是许多不同协议中最常见的数据编码类型。它将数据信号和时钟信号合并为一条信号线,减少了连线需求,并在较长的连线距离上显著提高了数据完整性。