18

我正在构建一个带有自己的 CPU (AVR Mega8) 的小型设备,它应该连接到 PC。假设已经完成了物理连接和字节传递,那么在这些字节之上使用的最佳协议是什么?计算机需要能够在设备上设置某些电压,并回读某些其他电压。

目前,我正在考虑一个完全由主机驱动的同步协议:计算机发送请求,嵌入式 CPU 应答。还有其他想法吗?

4

8 回答 8

6

对于客户端-服务器架构和同步协议,有很多话要说。简单和健壮,开始。如果速度不是问题,您可以考虑使用紧凑的、人类可读的协议来帮助调试。我正在考虑调制解调器 AT 命令的思路:“唤醒”序列后跟 set/get 命令,然后是终止符。

Host -->  [V02?]      // Request voltage #2
AVR  -->  [V02=2.34]  // Reply with voltage #2
Host -->  [V06=3.12]  // Set voltage #6
AVR  -->  [V06=3.15]  // Reply with voltage #6

如果看不到右括号,每一方都可能会超时,并且它们会在下一个左括号上重新同步,而后者不能出现在消息本身中。

根据速度和可靠性要求,您可以将命令编码为一或两个字节并添加校验和。

回复实际电压总是一个好主意,而不是简单地回显命令,因为它保存了后续的读取操作。

也有助于定义错误消息,以防您需要调试。

于 2008-11-22T04:17:35.650 回答
6

Modbus可能是您正在寻找的。它专为您遇到的问题类型而设计。那里有很多代码/工具,遵守标准可能意味着以后可以轻松重用。它还支持人类可读的 ASCII,因此仍然很容易理解/测试。

有关 windows 和嵌入式源代码,请参阅FreeModBus

于 2008-11-26T08:07:36.747 回答
5

我的投票是为了人类可读的。

但是如果你是二进制的,试着在开头放一个头字节来标记一个数据包的开始。我总是运气不好,串行协议不同步。标头字节允许嵌入式系统与 PC 重新同步。另外,在最后添加一个校验和。

于 2008-11-25T13:43:03.603 回答
4

我用简单的二进制格式做了这样的事情

struct PacketHdr
{
  char syncByte1;
  char syncByte2;
  char packetType;
  char bytesToFollow;  //-or- totalPacketSize
};

struct VoltageSet
{ 
   struct PacketHdr;
   int16 channelId;
   int16 voltageLevel; 
   uint16 crc;
};

struct VoltageResponse
{
   struct PacketHdr;
   int16 data[N];  //Num channels are fixed
   uint16 crc;
}

同步字节在同步协议中不如在异步协议中重要,但它们仍然有帮助,尤其是当嵌入式系统第一次上电时,您不知道它获得的第一个字节是消息的中间还是消息的中间不是。

类型应该是一个枚举,告诉如何解释数据包。大小可以从类型推断出来,但如果你明确地发送它,那么接收者可以处理未知类型而不会窒息。您可以使用“总数据包大小”或“要遵循的字节数”;后者可以使接收方代码更简洁。

最后的 CRC 增加了您拥有有效数据的更多保证。有时我会在标头中看到 CRC,这使得声明结构更容易,但将其放在最后可以避免在发送消息时额外传递数据。

发送方和接收方都应该在收到数据包的第一个字节后开始超时,以防丢失一个字节。PC端也需要超时来处理嵌入式系统没有连接完全没有响应的情况。

如果您确定两个平台都使用 IEEE-754 浮点数(PC 使用)并且具有相同的字节序,那么您可以使用浮点数作为数据类型。否则,使用整数会更安全,无论是原始 A/D 位还是预设比例(即 1 位 = .001V 给出 +/-32.267 V 范围)

于 2008-11-25T19:41:58.140 回答
3

亚当·利斯提出了很多重要的观点。简单性和鲁棒性应该是重点。人类可读的 ASCII 传输在调试时有很大帮助。很棒的建议。

它们可能对您的需求来说太过分了,但 HDLC 和/或 PPP 增加了数据链路层的概念,以及数据链路层带来的所有好处(和成本)。链路管理、成帧、校验和、序列号、重传等...都有助于确保可靠的通信,但会增加复杂性、处理和代码大小,并且对于您的特定应用程序可能不是必需的。

于 2008-11-23T18:10:44.017 回答
1

USB 总线将满足您的所有要求。它可能是非常简单的 USB 设备,只有控制管道向您的设备发送请求,或者您可以添加一个中断管道,以便通知主机您的设备发生变化。可以使用许多简单的 USB 控制器,例如CypressMicrochip

传输之上的协议确实与您的要求有关。从您的描述看来,简单的同步协议似乎就足够了。是什么让你徘徊并寻找其他方法?分享您的疑问,我们将尽力提供帮助:)。

于 2008-11-22T12:09:55.150 回答
1

如果我不希望需要进行有效的二进制传输,我会选择已经建议的终端样式界面。

如果我确实想做二进制数据包格式,我倾向于使用基于 PPP byte-asnc HDLC 格式的松散格式,它非常简单且易于发送接收,基本上:

数据包以 0x7e 开头和结尾您通过在字符前面加上 0x7d 并切换位 5(即与 0x20 进行异或)来转义字符,因此 0x7e 变为 0x7d 0x5e 并且 0x7d 变为 0x7d 0x5d

每次看到 0x7e 时,如果您存储了任何数据,您就可以对其进行处理。

我通常会做主机驱动的同步工作,除非我有很好的理由不这样做。这是一种从简单的点点 RS232 扩展到多点 RS422/485 的技术,没有任何麻烦——通常是一种奖励。

于 2008-11-23T19:17:55.193 回答
1

正如您可能已经从所有不直接将您定向到协议的响应中确定的那样,滚动您自己的方法是您的最佳选择。

所以,这让我开始思考,好吧,这是我的一些想法——

鉴于该芯片有 6 个 ADC 通道,很可能您正在使用 Rs-232 串行通信(您的问题的猜测),当然,有限的代码空间,定义一个简单的命令结构将有所帮助,正如 Adam 指出的那样——您可能希望在芯片上将输入处理保持在最低限度,因此二进制听起来很有吸引力,但权衡是易于开发和服务(您可能需要在 6 个月后解决死输入问题)——超级终端功能强大调试工具——所以,这让我想到了如何实现一个具有良好可靠性的简单命令结构。

一些一般性的考虑——

保持命令大小相同——使解码更容易。

正如亚当指出的那样,构建命令和可选的校验和可以很容易地围绕您的命令进行包装。(使用小命令,简单的 XOR/ADD 校验和快速而轻松)

我会向主机推荐一个带有重置时固件版本的启动公告——例如,“你好;固件版本 1.00z”——会告诉主机目标刚刚启动以及正在运行的内容。

如果您主要是监控,您可能希望考虑“自由运行”模式,其中目标将简单地循环通过模拟和数字读数 - 当然,这不必是连续的,它可以间隔为 1, 5、10 秒,或者只是根据命令。您的 micro 一直在监听,因此发送更新的值是一项独立的任务。

用 CR(或其他字符)终止每个输出行可以直接在主机上进行同步。

例如,您的 micro 可以简单地输出字符串;

  V0=3.20
  V1=3.21
  V2= ...
  D1=0
  D2=1
  D3=...
  and then start over -- 

此外,命令可能非常简单——

? - 阅读所有值——它们的数量不多,所以全部获取。

X=12.34 - 要设置一个值,第一个字节是端口,然后是电压,我建议保留“=”和“。” 如果您放弃校验和,作为框架以确保有效数据包。

另一种可能性是,如果您的输出在设定范围内,您可以预先调整它们。例如,如果输出不必精确,您可以发送类似

5=0 
6=9
2=5  

这会将端口 5 设置为关闭,端口 6 设置为完全打开,将端口 2 设置为半值——使用这种方法,ascii 和二进制数据在微型计算机的计算/解码资源方面几乎处于相同的基础上。或者为了更精确,使输出 2 个字节,例如 2=54 - 或者,添加一个外部参照表,并且值甚至不必是线性的,其中数据字节是查找表的索引。 .

正如我喜欢说的;简单通常更好,除非它不是。

希望这个对你有帮助。


重读时有了另一个想法;添加“*”命令可以请求使用 html 标签包装的数据,现在您的主机应用程序可以简单地将输出从您的 micro 重定向到浏览器和 wala,浏览器准备就绪 --

:)

于 2008-11-26T01:49:27.040 回答