【Delphi】基于状态机的串口通信
通信协议
串行通信接口(如RS232、RS485等)作为计算机与单片机交互数据的主要接口,广泛用于各类仪器仪表、工业监测及自动控制领域中。
通信协议是需要通信的双方所达成的一种约定,它对包括数据格式、同步方式、传送速度、传送步骤、检纠错方式以及控制字符定义等问题作出统一规定,在双方的通信中必须共同遵守。在实际应用系统中,如果缺少一个严格、合理、规范的串口通信协议,将无法保证数据传输的正确性及通信的可靠性。
因此,需要提出一种基于状态机串口通信协议的设计方法:通过合理地设置数据包格式来保证了数据传输的正确性:引入了状态机方法,简化了协议的实现难度,提高了通信的可靠性,同时使通信过程具有较高的容错能力。
定义数据包格式
串口通信中最小的的信息单元是数据帧。一个数据帧通常包括起始位、数据位、结束位,另外还可以包含用于检测传输错误的“奇偶校验位”,每个数据帧中传输的数据位可以有5、6、7、8或9个。
实际通信过程中,数据的发送是一帧一帧地进行,当被传输的数据超过一帧时(例如浮点型数据),如果没有对数据帧进行必要的打包,发送出去的数据将会很难被数据接收方解释与分析,进而造成数据传输混乱与错误。因此,在一般应用中有必要将数据帧组装成数据包再发送。
- 起始标志表示开始接收一个新的数据包。
- 数据长度命令和附加数据共占的字节数。设置此字段,可方便接收方识别数据包的长度并能够准确地接收数据包。
- 命令用来说明数据包的用途。
- 附加数据 当命令不同时,含义不同。
- 校验是对命令字段与附加数据字段的所有字节数据的异或校验。
- 结束标志表示该数据包结束。
另外,在多机通信中,数据包中还应增加源地址与设备地址等字段。
通信状态机
状态机简介
状态机由事物所处的状态及引发状态变化的外部事件两部分组成。
在软件编程中,事物所处的状态可以描述为某个程序片断或函数,而引发状态变化的处部条件可以理解为条件判断语句,当条件为真时,事物的状态发生变化。事物发生变化前的状态称为现态,变化后的状态称为次态,程序中可以通过不同的数字对不同的状态进行编号。现态到次态的变化可以通过状态变量值的改变来描述。
在协议中需要传输的基本信息单元是数据包,数据包一般包含多个数据帧。实际传输过程中,数据的传输通常是一帧一帧地进行,数据包是被拆分成若干帧数据后再进行传输,数据接受方也是分帧接受一个数据包。
数据接受方在解释与分析数据包时可能存在两个问题:
1.识别并接收完整的数据包
- 对于数据接收方,一个数据包是分若干批到来,在识别包头与包尾时,也就是帧同步问题;
- 具体编程时存在难度,特别对于已接收部分与未接收部分以及数据接收的进度及状态的处理。
2.数据传输时的容错能力
- 数据传输过程中已经出现错误时,系统应该具有摆脱错误状态,恢复到正常状态的能力。
- 例如,当一个数据包只传输完一部分时,因为未知故障,下一个数据包就开始传输,系统应该能识别出传输错误,抛弃前一个出错的数据包,并且能正确接收下一个数据包。
- 实际编程时处理这种问题难度较大,结果很可能会出现将第一个数据包的前一部分与第二个数据包的前一部分拼装成一个新的数据包的情况,这就损失了两个数据包,最严重的结果可能是系统无法从错误中恢复,这就严重降低了系统的安全性与可靠性。
为解决上面的两个问题,在协议中引入了状态机。
在状态机中,状态的变化依赖于外部触发条件,当条件满足时,状态将发生变化。
在协议中将数据包接收的各个阶段定义为不同的状态,将接收一帧新的数据或数据处理的结果作为外部触发条件,从而达到状态改变的目的,最终完成一个数据包的接收与校验。
串口通信状态图
串口通信协议中,发送数据包时一般不需引入状态机,这主要是为提高发送速率和简化编程模型而考虑。
在协议中主要针对数据接收过程建立状态机。
串口通信数据接收过程
- 当未开始接收数据包或发现数据传输出错时,系统进入空闲状态;
- 当数接收到数据包起始标志时,变为收到起始标志状态,如果收到的数据不为起始标志,系统继续保持空闲状态;
- 进入收到起始标志状态后,新接收到的任何数据将被当作数据包中命令与附加数据的总字节数(记为LEN),系统进入收到数据长度状态;
- 继续接收新的数据,直至接到新收到的数据总字节数达到LEN +2,进入检验结束标志状态;
- 这时可以检验结束标志是否为协议定义的标志值,如果是,说明传输正确,否则传输出错,出错后应查找接收缓冲区中本数据包的起始标志后有无其它起始标志,如果没有发现起始标志,系统应进入空闲状态,否则应直接进入接收到起始标志状态,这样可提高系统容错能力,方便系统从错误中恢复。
- 检验结柬标志正确后,进入数据校验状态;
- 校验结果如果正确,数据包接收完成,否则说明传输出错,系统进入空闲状态。
上位机软件编程逻辑
上位机软件中,当接收到数据时,串口控件会触发一个事件,在事件处理代码中应及时将收到的数据存入接收冲区,同时不应该把串口通信协议接收部分的代码放置在此事件中,否则后面到来的数据可能因为前面先到的数据没有及时处理完毕而被冲掉,导致数据丢失。
- 在上位机软件运行时,应该启动一个Windows线程,用于不断检测接收缓冲区是否为空,不为空时则对缓冲中的数据进行处理;
- 线程类创建好后,应具体编写线程类执行函数的处理过程,在其中通过状态指示变量sp实现状态机机制;
- 数据包的接收进度依据于状态指示变量sp。
当数据接收顺利时,sp的变化将会引导完成一个数据包的接收过程。这样处理可以简化编程的模型,使协议易于实现;数据包接收过程中,一旦发现数据传输出错,立即将sp置为0(空闲状态),也就是状态复位,使系统进入准备接收下一个数据包的状态,这样可提高通信过程的可靠性及容错能力。
状态机机制实现
{------------------------------
@功能:状态机机制实现串口通讯
@author:成鹏致远
@net:lcw.cnblogs.com
-------------------------------}
procedure TBufferThread.Execute;
var
s,a :string;
sp,mylen,oddEvenCheck,i :integer;
begin
sp :=; {指示读数据状态}
a :='';
while True do
begin {quelist为接收缓冲区}
if quelist.Count <> then {缓冲区取数}
begin
s :=quelist.Strings[];
quelist.Delete();
a :=a+s;
end;
if a='' then Continue;
{是否空闲状态}
if sp = then
begin
if ord(a[]) =0xFE then
begin
sp :=; {进入到起始标志状态}
end
else {起始标志错误}
begin end;
Delete(a,,);
end
{是否进入收到起始标志状态}
else if sp = then
begin
mylen :=Ord(a[]);
sp :=; {进入长度状态}
Delete(a,,);
end
{是否进入长度状态}
else if sp = then
begin
if Length(a) <=mylen + then Continue;
{数据结束标志:正确}
if ord(a[mylen +])=0xFD then
begin
sp :=; {进入数据校验状态}
end
else {数据结束标志:错误}
begin
sp :=; {重新进入空闲状态}
end;
end
{是否进入数据校验状态}
else if sp = then
begin
for i:= to mylen do
begin
oddEvenCheck :=a[] xor a[i];
end;
if oddEvenCheck =Ord(a[mylen +]) then
begin
sp :=; {校验正确,进入完成状态}
end
else {检验错误,进入空闲状态}
begin
sp :=;
end;
end
else if sp = then
begin
{这里省略处理命令与附加数据代码}
sp :=; {进入空闲状态}
Delete(a,,mylen+);
end;
end;
end;
【Delphi】基于状态机的串口通信的更多相关文章
- 基于FPGA自适应串口通信(Auto Baud Rate)
做的课设,相当于复习了一遍verilog. 实现了 1.接收端固定模式:8N1 BAUD:921600. 2.发送端8N1,任意波特率(不取极端值). 3.数码管显示波特率(16进制). 用了 1.两 ...
- QT5 TK1 串口通信
对TK1中基于QT5的串口通信过程进行总结.按照软件安装及通信实现的顺序. 1.QT5安装 较简洁方式:打开软件中心(类似A形),搜索qtcreator,点击安装即可. 2.串口通信库安装 采用上述方 ...
- 基于FPGA的红外遥控解码与PC串口通信
基于FPGA的红外遥控解码与PC串口通信 zouxy09@qq.com http://blog.csdn.net/zouxy09 这是我的<电子设计EDA>的课程设计作业(呵呵,这个月都拿 ...
- VS2008基于对话框的MFC上位机串口通信(C++实现)简单例程
首先,在 vs2008 环境下创建 MFC 运用程序 设置项目名称为 ComTest(这个地方随意命名,根据个人习惯),点击确定后,点击下一步 出现如下界面 选择"基于对话框"模式 ...
- Delphi 串口通信(1)
利用 Delphi实现串口通信的常用的方法有 3种: 一是利用控件,如 MSCOMM控件和 SPCOMM控件: 二是使用 API函数: 三是调用其他串口通信程序.其中利用 API编写串口通信程序较为复 ...
- [python] 3 、基于串口通信的嵌入式设备上位机自动测试程序框架(简陋框架)
星期一, 20. 八月 2018 01:53上午 - beautifulzzzz 1.前言 做类似zigbee.ble mesh...无线网络节点性能测试的时候,手动操作然后看表象往往很难找出真正的原 ...
- C#中缓存的使用 ajax请求基于restFul的WebApi(post、get、delete、put) 让 .NET 更方便的导入导出 Excel .net core api +swagger(一个简单的入门demo 使用codefirst+mysql) C# 位运算详解 c# 交错数组 c# 数组协变 C# 添加Excel表单控件(Form Controls) C#串口通信程序
C#中缓存的使用 缓存的概念及优缺点在这里就不多做介绍,主要介绍一下使用的方法. 1.在ASP.NET中页面缓存的使用方法简单,只需要在aspx页的顶部加上一句声明即可: <%@ Outp ...
- (转载)用vs2010开发基于VC++的MFC 串口通信一*****两台电脑同一个串口号之间的通信
此文章以visual C++数据採集与串口通信測控应用实战为參考教程 此文章适合VC++串口通信入门 一.页面布局及加入控件 1, 安装好vs2010如图 2, 新建一个基于VC++的MFC项目com ...
- [stm32][ucos] 1、基于ucos操作系统的LED闪烁、串口通信简单例程
* 内容简述: 本例程操作系统采用ucos2.86a版本, 建立了5个任务 任务名 优先级 ...
随机推荐
- List集合去重的一些方法(常规遍历、Set去重、java8 stream去重、重写equals和hashCode方法)
1. 常规元素去重 碰到List去重的问题,除了遍历去重,我们常常想到利用Set集合不允许重复元素的特点,通过List和Set互转,来去掉重复元素. // 遍历后判断赋给另一个list集合,保持原来顺 ...
- 一个死去的网站shige.laiyo.com
2017年4月份的时候,研一下刚刚开始. 爬了这个网站,现在这个网站已经关闭了,这些爬虫代码也就没用了,面向特定网站爬虫本身就是没有意义的. 爬author import requests from ...
- Java实现List数组的几种替代方案
在Java中,禁止定义List<Integer>a[],这种List数组结构. 但是还是可以使用其它一些方式来实现列表数组. 一.使用Node把List包裹起来 public class ...
- java 获取当前日期和特殊日期格式转换
1.获取当前日期: package com.infomorrow.dao; import java.sql.Timestamp; import java.util.Calendar; import ...
- LNMP分离式部署实例[转]
很多人在练习部署LNMP环境的时候,大都数是部署在同一个虚拟机上面的.但是实际工作中,我们一般都是分离部署的. 今天我就用3台虚拟机,部署下LNMP环境.以供参考! 网络拓扑图: 首先准备3台虚拟机: ...
- Win7 U盘安装Ubuntu16.04 双系统详细教程(方法一)
主要分为以下几步: 一. 下载Ubuntu 16.04镜像软件: 二. 制作U盘启动盘使用ultraISO: 三. 安装Ubuntu系统: 四. 用EasyBCD 创建启动系统启动引导: (根据个人情 ...
- Docker(一):Docker入门教程
如今Docker的使用已经非常普遍,特别在一线互联网公司.使用Docker技术可以帮助企业快速水平扩展服务,从而到达弹性部署业务的能力.在云服务概念兴起之后,Docker的使用场景和范围进一步发展,如 ...
- cucumber java从入门到精通(1)初体验
cucumber java从入门到精通(1)初体验 cucumber在ruby环境下表现让人惊叹,作为BDD框架的先驱,cucumber后来被移植到了多平台,有cucumber-js以及我们今天要介绍 ...
- The password supplied with the username Domain\UserName was not correct. Verify that it was entered correctly and try again
起因 今天想进入SharePoint 2013 Central Administration创建一个WebApplication,尽然发生了错误: The password supplied with ...
- (面试)写出下面switch语句的输出结果
(1) public static void main(String[] as) { int a = 0; switch (a) { case 1: System.out.println(&q ...