用于在嵌入式 CPU 和 PC 之间进行通信的协议

发布于 2024-07-09 21:59:59 字数 186 浏览 7 评论 0原文

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

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

I am building a small device with its own CPU (AVR Mega8) that is supposed to connect to a PC. Assuming that the physical connection and passing of bytes has been accomplished, what would be the best protocol to use on top of those bytes? The computer needs to be able to set certain voltages on the device, and read back certain other voltages.

At the moment, I am thinking a completely host-driven synchronous protocol: computer send requests, the embedded CPU answers. Any other ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

櫻之舞 2024-07-16 21:59:59

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

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

Modbus might be what you are looking for. It was designed for exactly the type of problem you have. There is lots of code/tools out there and adherence to a standard could mean easy reuse later. It also support human readable ASCII so it is still easy to understand/test.

See FreeModBus for windows and embedded source.

⒈起吃苦の倖褔 2024-07-16 21:59:59

关于客户端-服务器架构和同步协议有很多话要说。 首先是简单性和稳健性。 如果速度不是问题,您可以考虑使用紧凑的、人类可读的协议来帮助调试。 我正在按照调制解调器 AT 命令的思路进行思考:“唤醒”序列,后跟设置/获取命令,最后是终止符。

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

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

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

实际电压进行回复总是一个好主意,而不是简单地回显命令,因为它可以节省后续的读取操作。

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

There's a lot to be said for client-server architecture and synchronous protocols. Simplicity and robustness, to start. If speed isn't an issue, you might consider a compact, human-readable protocol to help with debugging. I'm thinking along the lines of modem AT commands: a "wakeup" sequence followed by a set/get command, followed by a terminator.

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

Each side might time out if it doesn't see the closing bracket, and they'd re-synchronize on the next open bracket, which cannot appear within the message itself.

Depending on speed and reliability requirements, you might encode the commands into one or two bytes and add a checksum.

It's always a good idea to reply with the actual voltage, rather than simply echoing the command, as it saves a subsequent read operation.

Also helpful to define error messages, in case you need to debug.

满天都是小星星 2024-07-16 21:59:59

我的投票是支持人类可读的。

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

My vote is for the human readable.

But if you go binary, try to put a header byte at the beginning to mark the beginning of a packet. I've always had bad luck with serial protocols getting out of sync. The header byte allows the embedded system to re-sync with the PC. Also, add a checksum at the end.

陈年往事 2024-07-16 21:59:59

我已经用简单的二进制格式完成了类似的事情

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

I've done stuff like this with a simple binary format

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;
}

The sync bytes are less critical in a synchronous protocol than in an asynchronous one, but they still help, especially when the embedded system is first powering up, and you don't know if the first byte it gets is the middle of a message or not.

The type should be an enum that tells how to intepret the packet. Size could be inferred from type, but if you send it explicitly, then the reciever can handle unknown types without choking. You can use 'total packet size', or 'bytes to follow'; the latter can make the reciever code a little cleaner.

The CRC at the end adds more assurance that you have valid data. Sometimes I've seen the CRC in the header, which makes declaring structures easier, but putting it at the end lets you avoid an extra pass over the data when sending the message.

The sender and reciever should both have timeouts starting after the first byte of a packet is recieved, in case a byte is dropped. The PC side also needs a timeout to handle the case when the embedded system is not connected and there is no response at all.

If you are sure that both platforms use IEEE-754 floats (PC's do) and have the same endianness, then you can use floats as the data type. Otherwise it's safer to use integers, either raw A/D bits, or a preset scale (i.e. 1 bit = .001V gives a +/-32.267 V range)

迷爱 2024-07-16 21:59:59

Adam Liss 提出了很多精彩的观点。 简单性和稳健性应该是重点。 人类可读的 ASCII 传输在调试时有很大帮助。 很好的建议。

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

Adam Liss makes a lot of great points. Simplicity and robustness should be the focus. Human readable ASCII transfers help a LOT while debugging. Great suggestions.

They may be overkill for your needs, but HDLC and/or PPP add in the concept of a data link layer, and all the benefits (and costs) that come with a data link layer. Link management, framing, checksums, sequence numbers, re-transmissions, etc... all help ensure robust communications, but add complexity, processing and code size, and may not be necessary for your particular application.

℉絮湮 2024-07-16 21:59:59

USB 总线 将满足您的所有要求。 它可能是非常简单的 USB 设备,只有控制管道来向您的设备发送请求,或者您可以添加一个中断管道,以允许您通知主机有关设备中的更改。
有许多简单的 USB 控制器可供使用,例如 赛普拉斯微芯片

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

USB bus will answer all your requirements. It might be very simple usb device with only control pipe to send request to your device or you can add an interrupt pipe that will allow you to notify host about changes in your device.
There is a number of simple usb controllers that can be used, for example Cypress or Microchip.

Protocol on top of the transfer is really about your requirements. From your description it seems that simple synchronous protocol is definitely enough. What make you wander and look for additional approach? Share your doubts and we will try to help :).

喜爱皱眉﹌ 2024-07-16 21:59:59

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

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

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

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

我通常会做主机驱动的同步工作,除非我有充分的理由不这样做。 该技术可以轻松地从简单的点对点 RS232 扩展到多点 RS422/485 - 通常是一种额外的好处。

If I wasn't expecting to need to do efficient binary transfers, I'd go for the terminal-style interface already suggested.

If I do want to do a binary packet format, I tend to use something loosely based on the PPP byte-asnc HDLC format, which is extremely simple and easy to send receive, basically:

Packets start and end with 0x7e
You escape a char by prefixing it with 0x7d and toggling bit 5 (i.e. xor with 0x20)
So 0x7e becomes 0x7d 0x5e
and 0x7d becomes 0x7d 0x5d

Every time you see an 0x7e then if you've got any data stored, you can process it.

I usually do host-driven synchronous stuff unless I have a very good reason to do otherwise. It's a technique which extends from simple point-point RS232 to multidrop RS422/485 without hassle - often a bonus.

灰色世界里的红玫瑰 2024-07-16 21:59:59

正如您可能已经从所有不直接指导您制定协议的回复中确定的那样,滚动您自己的方法是您的最佳选择。

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

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

一些一般性的考虑因素——

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

正如 Adam 指出的那样,构建命令和可选的校验和可以轻松地包裹在您的命令中。 (使用小命令,简单的 XOR/ADD 校验和快速且轻松)

我建议在重置时向主机发出启动公告,并提供固件版本 - 例如,“您好;固件版本 1.00z” - 会告诉主机目标刚刚启动以及正在运行的内容。

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

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

例如,您的微控制器可以简单地输出字符串;

  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,浏览器准备就绪 -

:)

As you may have already determined from all the responses not directly directing you to a protocol, that a roll your own approach to be your best choice.

So, this got me thinking and well, here are a few of my thoughts --

Given that this chip has 6 ADC channels, most likely you are using Rs-232 serial comm (a guess from your question), and of course the limited code space, defining a simple command structure will help, as Adam points out -- You may wish to keep the input processing to a minimum at the chip, so binary sounds attractive but the trade off is in ease of development AND servicing (you may have to trouble shoot a dead input 6 months from now) -- hyperterminal is a powerful debug tool -- so, that got me thinking of how to implement a simple command structure with good reliability.

A few general considerations --

keep commands the same size -- makes decoding easier.

Framing the commands and optional check sum, as Adam points out can be easily wrapped around your commands. (with small commands, a simple XOR/ADD checksum is quick and painless)

I would recommend a start up announcement to the host with the firmware version at reset - e.g., "HELLO; Firmware Version 1.00z" -- would tell the host that the target just started and what's running.

If you are primarily monitoring, you may wish to consider a "free run" mode where the target would simply cycle through the analog and digital readings -- of course, this doesn't have to be continuous, it can be spaced at 1, 5, 10 seconds, or just on command. Your micro is always listening so sending an updated value is an independent task.

Terminating each output line with a CR (or other character) makes synchronization at the host straight forward.

for example your micro could simply output the strings;

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

Also, commands could be really simple --

? - Read all values -- there's not that many of them, so get them all.

X=12.34 - To set a value, the first byte is the port, then the voltage and I would recommend keeping the "=" and the "." as framing to ensure a valid packet if you forgo the checksum.

Another possibility, if your outputs are within a set range, you could prescale them. For example, if the output doesn't have to be exact, you could send something like

5=0 
6=9
2=5  

which would set port 5 off, port 6 to full on, and port 2 to half value -- With this approach, ascii and binary data are just about on the same footing in regards to computing/decoding resources at the micro. Or for more precision, make the output 2 bytes, e.g., 2=54 -- OR, add an xref table and the values don't even have to be linear where the data byte is an index into a look-up table ...

As I like to say; simple is usually better, unless it's not.

Hope this helps a bit.


Had another thought while re-reading; adding a "*" command could request the data wrapped with html tags and now your host app could simply redirect the output from your micro to a browser and wala, browser ready --

:)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文