简单的串行点对点通信协议
我需要两个设备(一台PC和一个微控制器)之间的简单通信协议。 PC必须发送一些命令和参数给微型计算机。 微必须传输一个字节数组(来自传感器的数据)。
数据必须是噪声保护的 (除了奇偶校验,我想我还需要一些其他的数据校正方法)。
有没有标准的解决scheme来做到这一点? (我只需要一个想法,而不是完整的解决scheme)。
PS任何build议表示赞赏。 PPS对不起,任何语法错误,我希望你明白。
编辑1.我还没有决定是主/从协议还是双方都可以启动通讯。 个人电脑必须知道什么时候微软已经完成了一项工作,可以发送数据 如果数据准备就绪,它可以连续轮询微型数据,或者微型数据可以在工作完成时发送数据。 我不知道哪个更好,更简单。
编辑2. 硬件和物理层协议。 由于PC中使用的RS-232 C串行标准,我将使用asynchronous通信 。 我将只使用RxD,TxD和GND信号。 我不能使用额外的电线,因为微控制器AFAIK不支持它们。 顺便说一句,我正在使用AVR ATmega128芯片。
所以我将使用固定波特率,8位数据,2个停止位而不进行奇偶校验(或用?)。
数据链路协议 。 这就是我的问题主要关心的问题。 感谢您提供HDLC , PPP和Modbus协议。 我会研究它。
我会用HDLC 。 过去我已经好运了。 我会指出串行只是使用asynchronous帧 ,忘记所有其他控制的东西,因为它可能是矫枉过正。
除了使用HDLC来组帧。 我格式化我的数据包,如下所示。 这是如何使用802.11传递选项
U8 cmd; U8 len; u8 payload[len];
每个命令包的总大小是len + 2
然后你定义类似的命令
#define TRIGGER_SENSOR 0x01 #define SENSOR_RESPONSE 0x02
另一个好处是你可以添加新的命令,如果你正确地devise你的parsing器忽略未定义的命令,那么你将有一些向后兼容性。
所以把它放在一起,数据包将如下所示。
// total packet length minus flags len+4 U8 sflag; //0x7e start of packet end of packet flag from HDLC U8 cmd; //tells the other side what to do. U8 len; // payload length U8 payload[len]; // could be zero len U16 crc; U8 eflag; //end of frame flag
然后系统将监视串行stream的标志0x7e,当它在那里时,你检查长度,看是否是pklen> = 4和pklen = len + 4,并且crc是有效的。 注意不要仅仅依靠crc来处理小数据包,否则你会得到大量的误报,也会检查长度。 如果长度或crc不匹配,只需重置长度和crc,并开始解码新的帧。 如果匹配,则将数据包复制到新的缓冲区,并将其传递给您的命令处理function。 收到标志时,总是重置长度和crc。
对于你的命令处理函数,抓住cmd和len,然后用一个开关来处理每种types的命令。 我还要求某些事件发送响应,以便系统像事件驱动的远程过程调用那样工作。
因此,例如传感器设备可以具有定时器或响应命令来读取。 然后它将格式化一个数据包并将其发送到PC,PC将响应它收到数据包。 如果没有,则传感器设备可以在超时时重新发送。
另外,当你正在进行networking传输时,你应该把它devise成像OSI模型一样的networking栈,因为Foredecker点不会忘记物理层的东西 。 我的HDLC是数据链路层 , RPC和命令处理是应用层 。
RS232协议是棘手的。 使用HDLC的build议是好的,但不是整个解决scheme。 还有其他的事情你需要决定:
- 如何确定两个设备之间的波特率? Autobuad? 预定义,或设置明确?
- 你会在软件或硬件或两者都做stream量控制吗? 请注意,如果您使用硬件stream量控制,则必须确保电缆构build正确。
- 谈到电缆,这是RS233的巨大痛苦。 根据设备的不同,您可能需要使用直通电缆,交叉电缆或变体。
- 使用基于软件的stream量控制机制可以是有效的,因为它允许使用最简单的电缆 – 只有三个有线(TX,RX和普通)。
- 你select一个7或8位字?
- 硬件奇偶校验或软件错误检查。
我build议你去8个数据位,没有硬件奇偶校验,1个停止位,并使用基于软件的stream量控制。 如果您的硬件支持,您应该使用自动波特率。 如果没有,那么软件中的自动波特难以做到。
在这里有一些很好的答案,这里有一些有用的指针:
即使您的数据包没有时间分隔,同步字节也是减less您需要尝试构build数据包的位置的重要途径。 您的设备通常不得不处理一堆垃圾数据(即打开时的飞行中数据包的末端或硬件碰撞的结果)。 如果没有同步字节,您将不得不尝试从您收到的每个字节中创build一个数据包。 同步字节意味着只有1/255字节的随机噪声可能是数据包的第一个字节。 当你想窥探你的协议时,也很奇怪。
当你通过某种types的侦听工具查看数据包时,在你的数据包上有一个地址,甚至有一点说主/从或PC /设备是有用的。 您可以通过为PC使用与DEVICE不同的同步字节来完成此操作。 而且,这将意味着一个设备不会对自己的回应作出回应。
您可能想要查看错误更正(如汉明 )。 将8位数据打包成12位保护字节。 这12位中的任何一位都可以在途中翻转,并检索原始的8位。 用于数据存储(在CD上使用)或设备不能轻松重新发送(卫星链接,单向射频)。
数据包号码使生活更轻松。 一个发送的数据包带有一个数字,响应携带相同的数字,一个标志表示“响应”。 这意味着,发送方很容易检测到没有到达的数据包(同步被破坏),而在慢速链路的全双工模式下,可以在收到第一个响应之前发送两个命令。 这也使得协议分析更容易(第三方可以理解哪些分组是在不知道底层协议的情况下被接收的)
有一个单一的主人是一个很棒的简化。 也就是说,在全双工的环境中,这根本不重要。 只要你总是这样做,除非你试图节省电力,或者你正在做一些事件驱动在设备端(input状态改变,样品准备好)。
我的build议是modbus。 与具有传感器和参数的设备(例如PLC)进行通信是一种高效且简单的标准协议。 你可以在http://www.modbus.org上获得规范。; 它自1979年以来一直在stream行,并且越来越受欢迎,您将不会遇到find示例和库的问题。
几个月前我读了这个问题,问题完全一样,并没有真正发现任何有效的微小的8位微型内存。 所以受到CAN和LIN的启发,我build立了一些工作。 我把它叫做MIN(微控制器互连networking),我已经把它上传到GitHub这里:
https://github.com/min-protocol/min
这里有两个实现:一个embedded式C,一个用于PC。 再加上一个小小的“hello world”testing程序,PC发送命令,固件点亮一个LED。 我在博客上写了一个关于如何在Arduino板子上运行的方法:
https://kentindell.wordpress.com/2015/02/18/micrcontroller-interconnect-network-min-version-1-0/
MIN很简单。 我修复了0层表示(8个数据位,1个停止位,无奇偶校验),但保持波特率打开。 每个帧以三个0xAA字节开始,二进制是1010101010,如果一端要dynamic地适应另一端,则是一个很好的脉冲串来进行自动波特率检测。 帧的有效负载为0-15字节,具有16位Fletcher的校验和以及控制字节和8位标识符(以告知应用程序有效载荷数据包含的内容)。
该协议使用字符填充,以便0xAA 0xAA 0xAA始终指示开始帧。 这意味着,如果一个设备脱离重置,它总是与下一帧的开始同步(MIN的devise目标决不会传递不完整或不正确的帧)。 这也意味着不需要特定的字节间和帧间时序限制。 协议的全部细节在GitHub repo wiki中。
MIN有未来的改进空间。 我留下了一些钩子在那里阻止消息传递(控制字节的4位被保留)和更高层次的能力协商(标识符0xFF被保留),所以有足够的空间来增加对常用function的支持。
这是一个替代协议:
u8 Sync // A constant value which always marks the start of a packet u16 Length // Number of bytes in payload u8 Data[Length] // The payload u16 Crc // CRC
使用RS232 / UART,因为PC(串口)和处理器(UART)已经能够以最小的麻烦来处理(只需要一个MAX232芯片或类似的电平转换器)。
而使用RS232 / UART,如果不相关,则不必担心主/从。 stream量控制可根据需要提供。
build议的个人电脑软件:或者自己写,或者Docklight进行简单的监控和控制(评估版是免费的)。
对于更大的错误检查,最简单的是奇偶检查,或者如果你需要更强大的function,可能是卷积编码 。
无论如何,无论你做什么: 保持简单!
编辑:使用RS232与PC比以前更容易,因为你现在可以得到USB到RS232 / TTL转换器。 一端插入电脑的USB插口,显示为正常的串口; 另一个出来5 V或3.3 V信号,可以直接连接到您的处理器,无需电平转换要求。
我们使用了FDTI芯片中的TTL-232R-3V3 ,这种应用非常适合。
我唯一的build议是,如果你需要防噪音,你可能想使用全双工RS-422/485。 您可以在AVR端使用与此类似的IC,然后在PC端使用RS-232→RS-422转换器(如485PTBR) 。 如果您可以find或制作屏蔽电缆(两根屏蔽双绞线),那么您将获得更多的保护。 而所有这些对于微型和个人电脑都是不可见的 – 没有软件的变化。
不pipe你做什么,都要确保你使用的是全双工系统,并确保IC上的读/写使能线有效。
关于平价检查(因为它在这里出现了几次):
他们大多是无用的。 如果您担心单个位可能会发生错误更改,那么很有可能第二位也会发生更改,您会从奇偶校验中获得误报。
使用一个像CRC16一样的轻量级查找表 – 它可以计算为每个字节被接收,基本上只是一个异或。 史蒂夫梅尔尼科夫的build议是伟大的小微型。
我还build议传输人类可读的数据,而不是原始的二进制文件(如果性能不是你的首要任务)。 这将使debugging和日志文件更愉快。
你没有详细说明微控制器是如何工作的,但是从微型计算机发出的所有东西都是对PC命令的直接响应。 如果这样做似乎可以使用某种主/从协议(这通常是最简单的解决scheme)。 如果双方都可以发起通信,则需要更一般的数据链路层协议。 HDLC是一个经典的协议。 虽然完整的协议可能是你的需求矫枉过正,你可以例如至less使用相同的帧格式。 你也可以看看PPP是否有一些有用的部分。
也许这个问题可以是完全愚蠢的,但有人考虑使用X / Y / Z MODEM协议之一?
使用上述协议之一的主要好处是在各种编程环境中有很好的即用型实现。
您可以看看Telemetry
和在Pytelemetry
相关的桌面实现
主要特点
这是一个基于PubSub的协议 ,但与MQTT不同,它是一个点对点协议, 没有经纪人 。
作为任何pubsub协议,您可以从一个topic
一端发布 ,并在另一端通知该主题。
在embedded方面,发布到一个主题就像这样简单:
publish("someTopic","someMessage")
对于数字:
publish_f32("foo",1.23e-4) publish_u32("bar",56789)
这种发送variables的方式似乎有限,但下一个里程碑意图通过这样做来为主题的parsing添加额外的含义:
// Add an indexing meaning to the topic publish("foo:1",45) // foo with index = 1 publish("foo:2",56) // foo with index = 2 // Add a grouping meaning to the topic publish("bar/foo",67) // foo is under group 'bar' // Combine publish("bar/foo:45",54)
如果你需要发送数组,复杂的数据结构等等,这很好。
此外,PubSub模式由于其灵活性而非常好。 您可以构build主/从应用程序,设备到设备等。
C库
只要你有一个体面的UART库,C库就可以很容易地添加到任何新设备上。
您只需实例化一个名为TM_transport
(由Telemetry
定义)的数据结构,并将4个函数指针分配为readable
writeable
。
// your device's uart library function signatures (usually you already have them) int32_t read(void * buf, uint32_t sizeToRead); int32_t readable(); int32_t write(void * buf, uint32_t sizeToWrite); int32_t writeable();
要使用遥测,您只需添加以下代码
// At the beginning of main function, this is the ONLY code you have to add to support a new device with telemetry TM_transport transport; transport.read = read; transport.write = write; transport.readable = readable; transport.writeable = writeable; // Init telemetry with the transport structure init_telemetry(&transport); // and you're good to start publishing publish_i32("foobar",...
Python库
在桌面上,有一个实现协议的pytelemetry
模块。
如果你知道python,下面的代码连接到一个串行端口,在主题foo
上发布一次,在3秒内打印所有接收到的主题,然后终止。
import runner import pytelemetry.pytelemetry as tm import pytelemetry.transports.serialtransport as transports import time transport = transports.SerialTransport() telemetry = tm.pytelemetry(transport) app = runner.Runner(transport,telemetry) def printer(topic, data): print(topic," : ", data) options = dict() options['port'] = "COM20" options['baudrate'] = 9600 app.connect(options) telemetry.subscribe(None, printer) telemetry.publish('bar',1354,'int32') time.sleep(3) app.terminate()
如果你不知道python,你可以使用命令行界面
Pytelemetry CLI
命令行可以启动
pytlm
然后,您可以connect
ls
(列表)收到的主题, print
收到的主题数据,发表主题上的发表( pub
),或打开主题的plot
以实时显示收到的数据
SLIP和UDP。 认真。
所有的PC和类似的设备说话。
TCP Lean有一本很好的书和例子
Jeremy Bentham已经偷偷地find了一个正在工作的TCP / IP的PIC。 AVR和PIC一样好吗?
我build议使用UDP,这相当简单。