我正在构建一个带有自己的CPU(AVR Mega8)的小型设备,它应该连接到PC。假设已完成物理连接和字节传递,那么在这些字节之上使用的最佳协议是什么?计算机需要能够在设备上设置某些电压,并回读某些其他电压。
目前,我正在考虑一个完全由主机驱动的同步协议:计算机发送请求,嵌入式CPU应答。还有其他想法吗?
答案 0 :(得分: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
如果没有看到结束括号,每一方可能会超时,并且他们会在下一个开放式括号中重新同步,这不会出现在消息本身中。
根据速度和可靠性要求,您可以将命令编码为一个或两个字节并添加校验和。
使用实际电压回复总是一个好主意,而不是简单地回显命令,因为它会保存后续的读取操作。
如果需要调试,还可以定义错误消息。
答案 1 :(得分:6)
Modbus可能就是你要找的东西。它专为您所遇到的问题而设计。有很多代码/工具,遵守标准可能意味着以后可以轻松重用。它还支持人类可读的ASCII,因此它仍然易于理解/测试。
有关Windows和嵌入源的信息,请参阅FreeModBus。
答案 2 :(得分:5)
我的投票是为了人类可读。
但是如果你使用二进制文件,请尝试在开头添加一个标头字节来标记数据包的开头。串口协议不同步,我总是运气不好。标题字节允许嵌入式系统与PC重新同步。另外,最后添加一个校验和。
答案 3 :(得分: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范围)更安全
答案 4 :(得分:3)
Adam Liss提出了许多重要观点。应该关注简单性和稳健性。人类可读的ASCII传输在调试时帮助很多。很棒的建议。
它们可能对您的需求有些过分,但HDLC和/或PPP增加了数据链路层的概念,以及数据链路层带来的所有好处(和成本)。链路管理,成帧,校验和,序列号,重传等......都有助于确保稳健的通信,但增加了复杂性,处理和代码大小,对于您的特定应用可能不是必需的。
答案 5 :(得分:1)
USB bus将满足您的所有要求。它可能是非常简单的usb设备,只有控制管道向您的设备发送请求,或者您可以添加一个中断管道,允许您通知主机有关设备的更改。 可以使用许多简单的USB控制器,例如Cypress或Microchip。
转移顶部的协议实际上是关于您的要求。从您的描述看来,简单的同步协议似乎就足够了。是什么让你徘徊并寻找其他方法?分享您的疑虑,我们将尽力帮助:)。
答案 6 :(得分:1)
如果我不期望需要进行有效的二进制传输,我会选择已建议的终端式界面。
如果我想做二进制数据包格式,我倾向于使用基于PPP字节asnc HDLC格式的松散的东西,这非常简单且易于发送接收,基本上:
数据包以0x7e开头和结尾 通过在前缀为0x7d并切换第5位(即xor为0x20)来转义char 所以0x7e变为0x7d 0x5e 并且0x7d变为0x7d 0x5d
每当你看到0x7e时,如果你有任何数据存储,你可以处理它。
我通常会做主机驱动的同步内容,除非我有充分的理由不这样做。这是一种技术,从简单的点对点RS232扩展到多点RS422 / 485,没有麻烦 - 通常是奖金。
答案 7 :(得分:1)
由于您可能已经从所有答案中确定并未直接指导您使用协议,因此您可以选择自己的方法作为最佳选择。
所以,这让我思考得很好,这里有一些我的想法 -
鉴于该芯片有6个ADC通道,很可能你正在使用Rs-232串行通信(从你的问题猜测),当然有限的代码空间,定义一个简单的命令结构将有所帮助,正如Adam所指出的那样 - 您可能希望将输入处理保持在芯片的最小值,因此二进制听起来很有吸引力,但折衷是易于开发和维护(您可能不得不在6个月后发现死输入的麻烦) - 超级终端是一个强大的调试工具 - 所以,这让我想到如何实现一个具有良好可靠性的简单命令结构。
一些一般性考虑因素 -
保持命令大小相同 - 使解码更容易。
框架命令和可选的校验和,正如Adam指出的那样,可以很容易地围绕命令。 (使用小命令,简单的XOR / ADD校验和快速且无痛)
我建议在重置时使用固件版本向主机发布启动通知 - 例如,“HELLO;固件版本1.00z” - 会告诉主机目标刚刚启动并且正在运行什么。
如果您主要是监控,您可能希望考虑一种“自由运行”模式,其中目标只是循环通过模拟和数字读数 - 当然,这不必是连续的,它可以是间隔的在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标签的数据,现在您的主机应用程序可以简单地将您的微型输出重定向到浏览器和wala,浏览器就绪 -
:)