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

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

有帮助吗?

解决方案

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

有关Windows和嵌入式源的信息,请参见 FreeModBus

其他提示

客户端 - 服务器架构和同步协议有很多要说的。简单和强大,开始。如果速度不是问题,您可以考虑使用紧凑的,人类可读的协议来帮助调试。我正在考虑调制解调器AT命令的行:a!!!wakeup <!> quot;序列后跟一个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

如果没有看到结束括号,每一方可能会超时,并且他们会在下一个开放式括号中重新同步,这不会出现在消息本身中。

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

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

如果需要调试,还可以定义错误消息。

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

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

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

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范围)更安全

Adam Liss提出了许多重要观点。应该关注简单性和稳健性。人类可读的ASCII传输在调试时帮助很多。很棒的建议。

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

USB总线将满足您的所有要求。它可能是非常简单的usb设备,只有控制管道向您的设备发送请求,或者您可以添加一个中断管道,允许您通知主机有关设备的更改。 可以使用许多简单的USB控制器,例如 nodeId = 2124 <!> param = en022613 < !> page = wwwFullSpeedUSB“rel =”nofollow noreferrer“> Microchip

转移顶部的协议实际上是关于您的要求。从您的描述看来,简单的同步协议似乎就足够了。是什么让你徘徊并寻找其他方法?分享您的疑虑,我们将尽力帮助:)。

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

如果我想做二进制数据包格式,我倾向于使用基于PPP字节asnc HDLC格式的松散的东西,这非常简单且易于发送接收,基本上:

数据包以0x7e开头和结尾 通过在前缀为0x7d并切换第5位(即xor为0x20)来转义char 所以0x7e变为0x7d 0x5e 并且0x7d变为0x7d 0x5d

每当你看到0x7e时,如果你有任何数据存储,你可以处理它。

我通常会做主机驱动的同步内容,除非我有充分的理由不这样做。这是一种技术,从简单的点对点RS232扩展到多点RS422 / 485,没有麻烦 - 通常是奖金。

由于您可能已经从所有答案中确定并未直接指导您使用协议,因此您可以选择自己的方法作为最佳选择。

所以,这让我思考得很好,这里有一些我的想法 -

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

一些一般性考虑因素 -

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

框架命令和可选的校验和,正如Adam指出的那样,可以很容易地围绕命令。 (使用小命令,简单的XOR / ADD校验和快速且无痛)

我建议在重置时使用固件版本向主机发布启动通知 - 例如,<!>“HELLO;固件版本1.00z <!> quot; - 告诉主人目标刚刚开始,目的是什么。

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

使用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,浏览器就绪 -

:)

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top