17 通道逻辑分析仪
带串行分析仪的数字示波器,用于并行端口。
特点
- 数字示波器/逻辑分析仪,最多支持 **17个输入线**
- 使用 **并行(打印机)端口** 进行输入。
- 虽然逻辑分析仪通常非常昂贵,但这款是 **免费的**
- 采用速度优化的 **C++** 编写,以获得最大可能的采样率
- 该程序是一个 **独立的** 单一EXE文件,不需要任何额外的DLL或任何框架。开箱即用
- 运行在 **所有Windows平台** 上(从Win 95到Win 10)
- 运行在 **32位** Windows和 **64位** Windows上
- 首次运行时,安装一个 **驱动程序** 以访问所有NT平台上的硬件IO端口
- 通过 **PC扬声器播放声音**,以证明驱动程序正常工作
- 在图形用户界面的LED中实时显示 **输入线的实际状态**
- 每秒捕获高达 **500,000个样本** (取决于您的硬件和操作系统)
- **捕获到内存** (而不是磁盘) 以获得最大速度
- 捕获始终以硬件允许的 **最大频率** 运行
- **内存节省捕获技术** 只存储输入线的变化,而不是每次采样都存储所有线路
- 捕获过程与数据分析独立,因此您可以 **尝试各种设置** 以最佳方式显示捕获的数据
- 分析仪生成带有栅格的彩色 **示波器图**
- 输出写入一个 **HTML** 文件和一个或多个 **GIF** 文件
- 因此,分析仪结果可以轻松地与无需安装任何程序即可查看专有二进制格式的其他人 **共享**
- 您可以为每个分析仪输出写入 **单独的标题**,以便稍后在众多文件中进行区分
- 分析数据时,您可以选择删除旧的分析仪输出或 **保留旧文件**
- 时间轴以 **微秒** 精度显示绝对或相对时间(性能计数器)
- 自动 **检测不活跃的输入线**,只显示有活动的通道
- 自动 **检测空闲时间**,切除长时间不活动阶段
- 自动 **检测输入频率**,像真实示波器一样选择合适的栅格单位(10 µs, 20 µs, 25 µs, 50 µs, 100 µs等)
- 自动 **检测操作系统的上下文切换**
- **解码的串行数据**(起始位、奇偶校验位、应答位、停止位)及其代表的字节可以打印到图中和/或打印到HTML中,并可以复制到剪贴板。
- 解码的串行数据(起始位、奇偶校验位、应答位、停止位)及其代表的字节 **写入图中**
- 非常 **简洁的源代码**,带有适当的错误处理和大量注释
版本
- 版本 1.0:初始版本
- 版本1.1:改进了异步信号的串行分析仪
- 版本1.2:支持 **9位** 异步协议
- 版本1.3:新选项:只显示 **解码的字节为HTML**(非常快)或显示在图形下方。
- 版本1.4:选择一个时间间隔,用于在大量捕获数据中启动和停止 **部分分析**
- 版本1.5:带有 **进度显示** 的状态栏
- 版本1.7:错误修复
为什么仍然使用老旧的并行端口?
显然,并行端口比 **串行端口** (RS232) 快得多。此外,它提供更多的输入线。
那么USB呢?
市场上有几款 **专业的逻辑分析仪** 作为PCI卡或USB设备。但它们非常昂贵(> 350美元)。如果您必须测量非常高的频率或者您需要超过17个输入通道,您将不得不购买其中一个。但对于高达500 kSamples/s的频率,并行端口就足够了。最大的优点是它是 **免费的**。您只需要一根打印机电缆和ElmüSoft LogicAnalyzer。
如果您的PC不再有并行端口,您可以 **订购一个PCI适配器**,大约10美元。这是拥有数字示波器最便宜的解决方案。
为什么不使用USB适配器?
您可能会想到订购一个例如Dimax的 **SUB-20** 设备。这是一个带有32个 **通用输入/输出** 线(GPIO)和更多功能的USB适配器。
但是如果您需要数字示波器,SUB-20 **不能** 替代并行端口。原因是 **USB速度极慢**。
- USB首先被设计成 **廉价的**。速度并不是一个主要的设计目标。USB电缆只包含 **一对 双绞线**,不允许双向数据传输。当一个数据包从PC完全发送出去后,传输方向必须反转才能从USB设备接收响应包。USB不能像RS232那样同时在两个方向上传输数据,RS232有一根用于RxD的电缆和另一根用于TxD的电缆。
- 这种原始设计不允许在USB设备有数据等待传输时通知PC。相反,PC必须以特定的调度 **轮询** 所有连接的USB设备,这显然会导致最差的性能,并且即使所有设备都处于空闲状态,也会浪费处理器电源。
- USB总线的设计目的不是传输少量数据。USB是一种 **基于数据包的** 协议,因此传输10兆字节相对较快。当USB从网络摄像头传输视频时,图像延迟1毫秒并不重要。但是如果您读取SUB-20的GPIO端口,您只读取32位 = 4字节。(没有FIFO可用)USB数据包大小更大,数据包的其余部分必须用虚拟零填充。
- 由于USB协议的高度复杂性,USB **驱动程序** 不可能简单,因此您会在驱动程序中额外损失时间。
并行端口 | SUB 20 | |
连接到电脑 | 内置 | USB |
价格 | 免费 | $80 - $120 |
单次读取操作的速度 | 1.3 微秒 | 1 毫秒 |
最大采样率 | 500,000 样本/秒 | 1,000 样本/秒 |
如果使用两对双绞线,并且至少有两条状态线允许 **硬件握手**,就像RS232和Centronix等旧协议中那样,那么设计一个更快的USB总线会很容易。但这会使电缆和USB芯片的成本增加到荒谬的程度。如今,低价比高质量更重要,USB总线的设计者决定节省这些微小的额外成本,并用较慢的软件取代较快的硬件。
如果您需要用于低频的模拟或数字输入,或者必须与慢速I2C总线、SPI总线、RS232、RS485、ModBus或SMBus通信,而时序不太重要,那么SUB-20的微控制器可以为您完成。但您无法使用SUB-20或类似的USB设备构建有用的数字示波器或与高速串行总线通信。如果您需要这些功能,唯一的选择就是 **PCI适配器**。
并行端口
与其用一大堆废话解释并行端口的逻辑,我创建了一张图表。
正如俗话所说:一图胜千言
注意在某些计算机(HP)上,LPT端口的引脚21未接地。
您必须将其接地,否则LPT端口将被禁用,甚至不会出现在控制面板中。
请注意,此图已简化:缺少存储处理器写入IO端口数据的D触发器。
根据硬件配置...
- 输出可以实现为 **推挽式** 输出,提供稳定的低电平或高电平,也可以实现为 **开漏** 输出,由一个上拉电阻和一个下拉晶体管组成。
- **上拉电阻** 可能不存在或值可能不是2.2kΩ。
- 输出的高电平可能是 **+3V** 而不是 **+5V**。
每当您启动 ElmüSoft LogicAnalyzer 或更改 **端口组合框** 时,选定的端口将立即 **初始化**,这意味着通过写入高电平来释放所有开漏晶体管,并通过将控制端口的 **位5** 设置为高电平来将数据端口输出驱动器切换到三态模式。因此,端口必须以“字节模式”工作,该模式在 **扩展控制寄存器** (ECR 端口) 中设置。
并行端口通常位于地址0x378。您可以在控制面板 -> 硬件 -> LPT端口中查看分配的端口号。
- **状态端口** 是一个只读端口。它将始终毫无问题地工作。
- **控制端口** 通常实现为开漏。但在某些硬件上,控制端口可能是带推挽输出的只写端口。
- **数据端口** 通常实现为三态推挽输出。输出驱动器大多可以关闭。
要找出您的硬件具有哪种输出类型,首先启动LogicAnalyser,选择端口组合框,然后测量电压!
如果输出为高电平,用330Ω电阻接地
- 如果电压从+5V降至+0.5V或从+3V降至+0.4V,则您有开漏输出(正常)
- 但是如果电压仅从+5V降至+4.5V或从+3V降至+2.7V,则您有推挽输出(不能用作输入)
如果输出为低电平,用10kΩ电阻连接到+5V。
- 如果电压从0V升高到+5V,则您有一个处于三态模式的输出,它没有上拉电阻(OK)
- 如果电压保持0V,则输出未禁用(不能用作输入)
当改变线路电压时,LogicAnalyzer GUI中相应的 **LED** 将改变(**绿色或红色**)。输入缓冲器区分低电平和高电平的阈值通常在0.8V,具体取决于硬件。
注意
注意不要用跳线直接将任何线路短接到地或+5V,以避免损坏!测试时始终使用电阻器!
注意
建议首先使用 **状态** 线进行实验。在将任何外部信号连接到 **数据** 或 **控制** 线之前,分析输出类型。如果您无法使用330Ω电阻接地或10kΩ电阻连接到+5V来改变电压,则此线不能用作输入。
注意
在您的BIOS中,您应该将并行端口设置为“**ECP和EPP模式**”运行。ElmüSoft LogicAnalyzer既不使用ECP模式也不使用EPP模式(硬件握手),但通过此BIOS设置,**扩展控制寄存器** (ECR) 将可用,允许手动配置操作模式。
在“**Documentation**”文件夹中,您会找到3份关于在 **SPP**、**EPP** 和 **ECP** 模式下编程并行端口的详细PDF文档。
缓冲输入信号
在许多情况下,您可以直接将输入信号连接到并行端口。如果信号来自使用+5V的芯片,例如MAX485,就是这种情况。如果您得到奇怪的结果,请将模拟示波器连接到输入线,看看它们是否干净,或者它们是否受到打印机电缆容量的影响。
如果您的输入信号较弱,打印机电缆较长(> 1米)或在高频下工作,则可能需要缓冲输入信号。这可以使用 **74HC541** 轻松完成。
![]() | ![]() |
注意
**74HC541** 或 **74HCT541** 不能用 **74LS541** 或 **74S541** 替换!
HC芯片具有极高的输入阻抗(高速CMOS技术),而LS芯片(双极晶体管技术)不适用于缓冲敏感的输入信号。
注意
您必须将74HC541所有未使用的输入接地,以避免杂乱的振荡。
驱动程序
在 **Windows 95、98、ME** 的旧时代,您可以使用命令 _inp(Port)
和 _outp(Port, Value)
直接在C++程序中读写硬件IO端口。
在所有 **NT平台**(NT、2000、XP、Vista、Win7)上,这是禁止的。试图读写IO端口的程序将因异常而退出。只有驱动程序才允许访问硬件。缺点是,通过驱动程序,硬件访问速度比直接使用 _inp()
和 _outp()
慢。为了避免这个缺点,LogicAnalyzer 使用了未公开的API调用,例如 Ke386IoSetAccessProcess()
,它修改了进程的 **I/O权限位图**。设置权限图可以在NT平台上直接在EXE中使用 _inp()
和 _outp()
命令。IOPM的修改是通过开源的 **PortTalk** 内核驱动程序完成的。
在 **64位Windows** 上,PortTalk驱动程序不幸无法使用,因为不存在64位版本的驱动程序。因此,在64位Windows上,LogicAnalyzer使用开源的 **WinRing0** 内核驱动程序。尽管它无法修改IOPM,但它可以通过IOCTL驱动程序调用 DeviceIoControl()
访问IO端口。这虽然较慢,但这是使其在64位Windows上工作的唯一方法。此外,微软要求在64位Windows上,所有驱动程序都必须签名。最新版本的WinRing0满足此要求。
Windows 95,98,ME | 32位Windows NT,2000,XP,2003,Vista,7,2008 | 64位Windows XP,2003,Vista,7,2008 |
无驱动程序 (快) | PortTalk (快) | WinRing0 (慢) |
有关驱动程序的详细信息,请参阅“Documentation”文件夹!
首次运行 LogicAnalyzer 时,您必须以 **管理员身份** 启动它。否则 PortTalk 或 WinRing0 驱动程序将无法安装。一旦安装,任何普通用户都可以使用它。微小的驱动程序文件以嵌入资源的形式编译到 EXE 中,并复制到 Windows 驱动程序文件夹。在 64 位 Windows 上,LogicAnalyzer 通过 Wow64DisableWow64FsRedirection()
关闭 **系统文件重定向**,否则文件将被复制到 **Wow64** 文件夹,而驱动程序将无法加载。
为了检查一切正常,LogicAnalyzer会通过 **PC扬声器播放“嘟”声**。要通过PC扬声器播放声音,必须访问几个IO端口。(这如何工作超出了本文的范围。请研究源代码!)因此,当您启动LogicAnalyzer的发布版本并听到PC扬声器发出“嘟”声时,您就知道驱动程序正常工作,并且您已准备好使用该程序。
如果您没有听到“嘟”声,您可能正在使用LogicAnalyzer的调试版本(该版本是静音的),或者您正在使用 **笔记本电脑**。笔记本电脑从来没有PC扬声器。笔记本电脑的内置扬声器连接到声卡。有些笔记本电脑会模拟PC扬声器,但前提是声卡的驱动程序支持此功能。
最高速度
最大速度显然取决于您的硬件。LogicAnalyzer有意用C++编写,以最大可能的速度运行。
在 **32位Windows** 上,您可以以更高的采样率捕获,因为PortTalk驱动程序允许直接访问IO端口。
在 **64位Windows** 上,访问通过WinRing0驱动程序调用进行。每次进行IOCTL调用以访问端口时,处理器必须从ring 3切换到ring 0,然后切换回来。
奇怪的是,即使直接访问端口,_inp()
命令执行得 **非常慢**。C++的 _inp()
命令直接编译成以下汇编代码
没有明显的理由说明这应该很慢。但它确实如此
一个单独的 _inp()
命令花费的时间与以下运行500次的循环相同
_inp(0x378);
| DWORD D=0;
for (DWORD L=0; L<500; L++)
{
D = (D + 3) * 2;
}
| |
执行时间:1.3µs | 执行时间:1.3µs |
结果是最大捕获速度也取决于您启用了多少个端口。对于每个端口(Status,Control,Data),必须执行一个 _inp()
运行纯捕获循环 | 捕获1个端口 | 捕获2个端口 | 捕获3个端口 |
2.4兆循环/秒 | 520千样本/秒 | 300千样本/秒 | 200千样本/秒 |
这些值对应于我的机器(2 GHz)使用PortTalk驱动程序。您可以使用“Measure Speed”按钮检查您的硬件速度。使用WinRing0驱动程序,在相同的硬件上速度大约减半。
在我的电脑上,我分析一个以 **192 kBaud** 运行的RS485总线,没有问题。
类
![]() |
|
源代码
您将找到一份由经验丰富的程序员编写的非常清晰的源代码,其中包含适当的错误处理和大量注释。您可以重用代码,例如 CGraphics
类来绘制任何类型的自定义图表,或者 CDriver
和 CPort
类,它们可以在其他应用程序中用于访问IO端口。
LogicAnalyzer非常复杂。在这里,您免费获得了几周编码的成果!
捕获数据
重要提示
在连接任何输入信号之前,请 **仔细** 阅读“并行端口”一章!
在将任何输入信号连接到 **数据端口** 或 **控制端口** 之前,您 **必须始终** 启动 LogicAnalyzer 并从端口组合框中选择要扫描的正确端口!线路可能会被切换为输出!LogicAnalyzer 将它们切换为输入模式。**状态端口** 没有问题,因为它不能作为输出工作。当连接了实时输入信号时,请勿关闭电脑!
GUI始终在LED中显示输入线的 **真实状态**。您不必担心线路是否在硬件上反转。如果LED为 **绿色**,电平为+3V或+5V。如果LED为 **红色**,则为0V。如果为 **灰色**,则端口被禁用。
在捕获时,LED显示屏会关闭,以便将全部处理器能力用于捕获线程。该线程始终以硬件允许的 **最大速度** 运行。捕获的数据存储在 **内存** 中,这比写入磁盘更快。
内存使用经过优化:捕获循环不会将每个样本写入内存。只有当任何输入线改变电平或发生毛刺(见下文)时,才会将11字节的捕获包写入内存。因此,您需要的内存量主要取决于输入线的活动情况。
分析仪
分析器会创建一个HTML文件和一个或多个GIF文件。GIF文件包含1到17条彩色示波器线。分析器会检测不活动的线路,这些线路将不会显示。您可以选择让分析器自动检测栅格单位和空闲间隔。如果您想放大或缩小,可以手动修改GUI中的栅格单位和空闲间隔设置,然后再次单击“分析”。
重要提示
GUI和示波器图使用更 **用户友好的位编号**。如果状态端口的输出从位3开始,那会很奇怪。请不要将GUI和图表中显示的位与IO端口上的实际位混淆!
“状态0”表示第一条状态线,对应于引脚10(如GUI中所示),尽管它实际上读取状态IO端口的位6!
串行协议
CSerial
类将输入数据解释为串行总线上的活动。有几种串行总线可以进行分析
- I2C总线、SMBus、ModBus
- PS/2总线
- SPI总线
- 异步(RS232、RS422、RS485)
- 红外遥控
- 智能卡
有些协议已经实现,其他的需要您添加。如果您添加了新协议,请给我发送电子邮件。如果代码编写清晰,我将将其添加到类中并在CodeProject上发布。
重要提示
无论您将输入信号连接到数据、控制或状态端口的哪个位都没有关系。但 **第一条活动线** 被解释为时钟线,**第二条活动线** 被解释为PS/2和I2C协议的数据线。
示例:您可以将PS/2时钟连接到状态端口线2(引脚12),将PS/2数据连接到状态端口线3(引脚13),并将其他输入接地或连接到+5V。显然,您可以连接超过2个输入信号,但它们必须位于时钟和数据线之后。您将立即在HTML输出中看到是否连接正确。
异步注意事项
虽然PS/2、I2C、SPI等协议有自己的时钟信号,但 **异步** 协议只有在您正确输入 **波特率**、**奇偶校验** 和 **停止位** 后才能正确检测到。如果波特率偏差几个百分点,这没有关系,因为每次起始位时,串行分析仪都会重新同步到数据流。
PS/2注意事项
如果您测量PS/2键盘,您将永远不会在线路上看到ASCII码传输。传输的字节是 **扫描码**。当您按下某个键时,其扫描码会发送到计算机。当您释放该键时,相同的扫描码会再次发送,并带有一个 **0xF0** 字节指示该键已被释放。Windows API将扫描码转换为 **虚拟键码**,然后转换为 **ASCII** 或 **Unicode**。请参阅MSDN文档中关于 MapVirtualKey()
的内容。
当计算机向键盘发送命令时(例如,在按下Caps Lock键后打开/关闭键盘LED),**PS/2协议** 会切换到完全不同的模式。有关详细信息,请参阅文档文件夹中“Manual PS2 Bus.chm”中的“**主机到设备**通信”章节!
如果您同时使用PS/2键盘和PS/2鼠标,您会注意到当您点击或移动鼠标时,PC会拉低键盘的时钟线。这是正常的,意味着PC正忙。当时钟线处于低电平时,键盘无法发送数据。
关闭图形显示
当您分析一个非常繁忙的RS485或I2C总线时,几秒钟内您将获得数兆字节的捕获数据。然后LogicAnalyzer可能需要生成5000张图片。尽管GIF压缩是速度优化并用快速C++编写的,但这仍需要一段时间。此外,当您打开一个包含5000张图片的页面时,您的浏览器将占用大量内存。
在这种情况下,您可以通过从组合框中选择“**只将解码的字节打印为HTML**”来关闭图形。您将看到类似以下内容
分析的数据是纯HTML,可以复制到剪贴板并在其他地方使用。
不带图形的生成速度超快,但显然您会丢失关于时序和毛刺确切位置的信息。如果您想查看捕获数据特定部分的更多细节,只需将浏览器中的时间码复制到LogicAnalyzer的“**分析开始**”和“**分析结束**”字段,选择创建图像并再次分析数据。这样您就能快速获得感兴趣的部分及其所有细节。
上下文切换
如果您测量 **慢速信号**,例如PS/2(时钟频率为10 kHz)或4800波特异步通信,您将完全没有问题。但是如果您必须测量 **更高频率**,那么在测量(捕获)期间,CPU应该尽可能 **空闲**。
点击“测量速度”按钮,了解您的计算机当前在后台活动中有多忙。此测量将运行一个循环10000次,并测量每个循环运行多长时间。您将看到一个宽度为10000像素的图表,其中X轴上的每个像素代表一个循环周期,Y轴对应每个循环运行的时间
请注意,速度很大程度上取决于您启用了 **多少个端口**!在此示例中,捕获了一个端口。您会看到循环通常需要1.9 µs(= 520 kHz)。在6.3 ms处,您会看到一个 **毛刺**。这里Windows中断了LogicAnalyser 20 µs。在此期间,操作系统已切换到其他应用程序或驱动程序。这称为“**上下文切换**”(参见 维基百科)。
有些程序会产生永久的后台活动,例如聊天程序“Trillian”,它每毫秒都会产生轻微的毛刺。
这个例子说明了,如果您必须测量10 kHz以上的频率,**停止所有正在运行的程序和服务** 是多么重要。您停止的应用程序和服务越多,可用于捕获循环的CPU能力就越多,毛刺也会越少。断开网络和USB设备!即使任务管理器显示CPU 100%空闲,后台活动也始终存在。
请将您用于测量的电脑保持无其他活动。您不应该想到在同一台电脑上测量高速RS232通信。这将由于占用大量CPU的串行端口驱动程序而失败。
毛刺的不可避免来源是主板每15.625毫秒(64赫兹)触发的 **时钟中断**。它用于递增滴答计数器和系统时间。
在上下文切换期间,LogicAnalyser将无法捕获数据。因此,如果您的输入频率很高,您 **可能** 会丢失输入数据……但这只发生在在此期间输入线至少两次改变电平的情况下!上下文切换通常 **不会非常频繁** 发生。如果您关闭了尽可能多的后台活动,您会看到很少的毛刺。如果在上下文切换期间输入线不改变其状态,您就很幸运,不会丢失任何输入数据。
你现在可能会问:“那么我永远不能确定我看到的数据没有损坏吗?”
是的。您将始终知道这一点,因为LogicAnalyser **会自动检测上下文切换**。
如果发生了上下文切换,图中的背景颜色会显示为深红色。
如果这段时间很长,您可以假设您 **可能** 丢失了数据。如果这段时间很短,您就知道这不重要。
在这个例子中,上下文切换肯定不重要,因为它极其短暂。可能在红色间隔期间,一个驱动程序处理了PS/2击键。
单个上下文切换总是显示在超过5个栅格单位时,否则会被忽略。
数据可能丢失是否对您来说是个问题,取决于您打算用 LogicAnalyser 做什么。例如,如果您想分析红外遥控器发出的信号,您只需重复测量直到获得一个干净的图表。对于可以重复的短测量,上下文切换根本不重要。
在使用 LogicAnalyzer 时,您会很快注意到上下文切换出现得如此之少,以至于落在信号中间的概率非常低。
请不要忘记 LogicAnalyser 是一个 **免费** 软件,并且使用 **免费** 硬件!
如果您需要更可靠的高速工具,您将不得不为此付费!
使用FIFO / DMA
并行端口有一个16位FIFO缓冲区,用于缓冲8条数据端口线。使用此FIFO是可行的。但缺点是复杂的握手和FIFO工作所需的外部时钟。并行端口也支持DMA。但编程复杂,也需要硬件握手。(参见ZIP文件中的“Documentation”文件夹。)
我发现所有这些都太麻烦了,所以没有实现。LogicAnalyser完全满足我的需求,并且能够满足我的所有要求。如果您需要更多功能,请自行实现并把代码发给我!
调试
虽然不打算将原始捕获数据写入磁盘,但在向串行分析仪添加新协议时,您可以使用两个编译器开关:使用 SAVE_CAPTURE_TO_FILE
编译并点击捕获以创建BIN文件,然后使用 LOAD_CAPTURE_FROM_FILE
编译并点击捕获,然后分析以分析BIN文件中的数据。这样您可以修改代码直到它工作,而无需在每次构建后重新捕获。