【问题标题】:c# SerialPort: how to send "0 byte" transfers (AKA ZLP: zero length packet)?c# SerialPort:如何发送“0 字节”传输(AKA ZLP:零长度数据包)?
【发布时间】:2016-08-10 17:50:46
【问题描述】:

我对 SerialPort 进行了一些尝试(令人沮丧),最终达到了我完全不知道为什么这不起作用的地步。我正在尝试向 USB CDC 设备发送十六进制命令,我这样做的方式是通过它公开的 COM 端口接口。我可以与设备握手,当我说 HI 时,它会回复 HI,但随后我向它发送另一个命令,该命令后面必须跟一个零字节数据包,否则设备将完全停止响应。请记住,这个零字节数据包中绝对没有任何内容,这意味着它没有 \0 或 0x00 或 0 甚至是 null(SerialPort 在 null 上抛出异常)。

现在,我能够规避这种情况的一种方法是使用 libusbdotnet。我直接访问 CDC 设备而不是 COM 接口,正确设置端点并发送类似的十六进制命令。我可以使用以下 c# 代码使用此方法成功发送“0 字节”数据包:

string zlpstring = "";
byte[] zlpbyte = Encoding.Default.GetBytes(zlpstring);
....snip
ecWrite = writer.SubmitAsyncTransfer(zlpbyte, 0, zlpbyte.Length, 100, out usbWriteTransfer);

zlpbyte 是缓冲区,0 是偏移量,zlpbyte.Length 是数据包长度,以字节为单位,100 是超时时间,out usbWriteTransfer 是传输上下文。

当我在 COM 端口上使用相同的方法时:

string zlpstring = "";
byte[] zlpbyte = Encoding.Default.GetBytes(zlpstring);
_serialPort.Write(zlpbyte, 0, zlpbyte.Length);

USB 记录器报告绝对没有发送任何内容。就好像 COM 端口忽略了零字节传输。在提到“你不能这样做”之前,有各种程序可以在不进行任何驱动程序操作的情况下将零字节数据包发送到该设备的 COM 端口。这就是我想要的,这就是为什么我试图放弃 libusbdotnet 并直接使用 COM 端口。

编辑:

经过更多的玩弄和不同的 USB 记录器,我发现发送的不是零字节,而是这个:

IRP_MJ_DEVICE_CONTROL (IOCTL_SERIAL_WAIT_ON_MASK)

我认为这可能是问题所在。如果发送的是 0 字节,那么我认为它会显示为:

IRP_MJ_WRITE > UP > STATUS_SUCCESS >(空白)>(空白)

我的程序正在发回 01 00 00 00 的响应,但是在记录另一个成功的程序时它正在设置等待掩码:

IRP_MJ_DEVICE_CONTROL (IOCTL_SERIAL_SET_WAIT_MASK) DOWN STATUS_SUCCESS 01 00 00 00

如果我的假设是正确的,那么这个问题可能已经变成了如何设置串行端口/COM 端口的等待掩码?在 c# SerialPort 类中绝对没有这方面的内容......这就是为什么我现在可以明白为什么这么多文章称它为“缺乏”的原因。我还浏览了 c++:https://msdn.microsoft.com/en-us/library/aa363214(v=vs.85).aspx 这似乎也没有涵盖等待掩码。使用 USB 过滤器 libusb 开始每分钟看起来都更令人愉悦...(尽管我会永远质疑自己为什么在那里发送零字节有效,但它不能通过 SerialPort)。

第二次编辑:

我是个白痴。这绝对是制造商可能没有想到任何人会触摸也不知道如何设置的设置:

#define EV_RXFLAG 0x0001
SetCommMask(hSerial, EV_RXFLAG);

然后我通过 USB 日志看到了这一点:

IRP_MJ_DEVICE_CONTROL (IOCTL_SERIAL_SET_WAIT_MASK) DOWN STATUS_SUCCESS 01 00 00 00

宾果游戏。 RXFLAG 最初设置为 0x0002。我还没有找到在 C# 中改变它的方法。所以我现在不得不处理一些 C++ 代码。它完全有效,并像它应该发送的那样发送“零字节”,而我实际上没有从代码中发送它。我假设这个设置是我的设备和它在 Flash 模式下与之交互的任何其他东西之间的“握手”方法。希望这可以帮助那些 COM/串行设备拒绝/丢弃零字节数据包但同时需要 ZLP 的其他人......多么愚蠢?!

【问题讨论】:

  • 为什么使用默认编码?默认使用 ASCII 过滤不可打印的字符,如 \0。尝试 UTF8 编码。
  • 创建 byte[] zlpbyte = new byte[0];并发送 zlpbyte?
  • 感谢jdweng 和Mong Zhu 的回复。如果我将编码更改为 UTF8,它会将字符视为完全相同(这令人困惑......)。使用 Encoding.UTF8.GetBytes(zlpstring) 编译后,绝对没有任何变化。 “byte[] zlpbyte = new byte[0];”也会发生同样的事情就像其他任何不具有 1 或更多字节长度的东西......端口只是丢弃它并且它永远不会出现在 USB 记录器中。

标签: c#


【解决方案1】:

您是否尝试在数据末尾连接一个额外的新行或回车或两者?

我会说在字节数组的末尾添加一个 0xA(新行)或 0xD(回车)或 0xA 和 0xD,看看你是否得到了什么。

byte[] zlpbyte = new byte[1] {0};

_serialPort.Write(zlpbyte, 0, 1);

[更新] 根据我们的讨论,您似乎正试图控制串行端口的控制信号。我之前没有尝试过,但我可以看到可以将控制信号(如果我正确理解源)设置为某些状态。

尝试设置握手属性

public enum Handshake
  {
    None,
    XOnXOff,
    RequestToSend,
    RequestToSendXOnXOff,
  } 

我不确定它究竟如何影响 IOCTL 设置,但我相信它应该能够以某种方式影响它

【讨论】:

  • 感谢 CJC 的回复。我在 UTF8 和 ASCII 编码中都尝试了这两种方法。 0xA 返回 10 个字节,0xD 返回 13 个字节。所有这些字节都返回 00。我正在尝试发送一个 0 字节的数据包,数据包中绝对没有任何内容(我猜是 null?)。我知道这看起来很奇怪,但这是设备确定文件段何时完成上传的方式。
  • 从您的代码中,zlpstring = "" 不是空值。它是一个空格 ascii,相当于 0x20。如果您尝试将空值写入设备。你为什么不做这样的事情。查看我的更新答案
  • 我认为您对发送 0 字节的解释不清楚。我有一种感觉,你的意思是,你想发送一个数据值为 0 或 null 的信息字节。对?无论如何,我在上面更新了答案。请让我知道这是否是您想要做的,如果您喜欢我的回答,请给我点赞并打勾!谢谢
  • 您好 CJC,如果解释不清楚,我很抱歉,因为零字节传输的想法似乎是我永远不必考虑或崩溃的事情。我一直认为零字节数据包具有空标识符,或者是 0 或 0x00。我没想到它根本没有任何东西,这是 COM 端口似乎想要的,以便意识到我已经完成发送数据并期望从它那里得到响应(“好的,他发送了一个 0 字节数据包给我们,让我们分析一下他在0字节数据包之前发送给我们的数据,然后给他回一个响应“)。
  • 我想我想发送一个值为 null 的字节......但字节大小必须为零。正如上段所述,这是一件非常奇怪的事情。我已经向设备发送了一个值为 0x00 的字节,但它会将其识别为带有数据的 1 字节数据包:0x00(您更新的示例就是一个完美的示例)。我需要一个包含数据的 0 字节数据包:(如果可能,不存在?)。但是,串行端口会丢弃我发送的任何类型的 0 字节长度数据包。除非数据包大小为 1 字节或更大,否则它不会显示在 COM 端口的日志中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-28
  • 1970-01-01
  • 2020-03-11
  • 1970-01-01
  • 2019-11-26
  • 1970-01-01
相关资源
最近更新 更多