串口通信开发

2023-11-07

一开始做串口通信开发时,觉得并不难,无非就是发送,然后等一会,再接收就完事了。其实里面的水很深,特别是在各种设备都有的情况下。我们在整个开发过程中,遇到了以下的几个主要问题:

1、设备出现严重的延迟。

2、接收过程出现数据粘包或截断。

3、多设备共用一个串口。

4、使用RTU的情况,接受到数据传给所有程序处理。

5、采数和反控不能相互影响。

6、多线程并发采数。

 

一、设备出现严重的延迟

正常的设备,在100ms之内就会返回,但某些设备,因为硬件原因,响应指令的速度非常慢,有可能达到10s。

对于这种情况,我们需要延长等待的时间。但直接延长,对于响应快的设备,就不公平。我们可以采用以下的方法:

int count = 0;
while (count < 20)
{
    Thread.Sleep(WRITE_READ_INTERVAL);
    //接收数据
    //如果数据合法,跳出循环
}

具体代码我们结合第二个问题给出。

二、接收过程出现数据粘包或截断

调用一次接收函数,收到的数据不一定是完整的,特别是字节数比较多的情况。我们需要把几次接收到的内容,拼成一个包,再判断这个包是否合法的。根据第一、第二个问题,我们给出以下代码解决:

int count = 0;//循环次数
byte[] total = new byte[1024];//总接收到的内容
int pointer = 0;//新内容的起始指针
while (count < 20)//循环20次,一次100ms,也就是最长等待2s
{
    Thread.Sleep(WRITE_READ_INTERVAL);
    byte[] temp = ReceiveByte(control);//接收数据,存放在一个临时变量中
    if (temp != null && temp.Length > 0)
    {
        Array.Copy(temp, 0, total, pointer, temp.Length);//合并数据
        pointer += temp.Length;

        if (ModbusHelper.CheckRecvValid(total, pointer))//判断数据是否合法,一旦合法,说明一条完整的数据已经接收完成
        {
            break;
        }
    }

    count++;
}
if (pointer < 5)
{
    return null;
}
byte[] recv = new byte[pointer];
Array.Copy(total, recv, pointer);

return recv;

上述方法是专门针对Modbus协议的,如果是其他协议的设备,还需要修改判断合法的条件。

三、多设备共用一个串口

一开始,我们给每一台设备分配一个串口,这台设备负责串口的打开、传数和关闭等操作。但多设备共同串口的情况下,这种方法就行不通了。串口不再属于某一台设备。基于这种情况,我们把串口从设备类里抽离出来,转而使用一个串口管理类去操作串口。

串口管理类的示例代码如下:

private static Dictionary<string, ComPortHelper> ComPortDict = new Dictionary<string, ComPortHelper>();//串口字典

public static bool IsInit(string PortName)//串口是否就绪
{
    if (ComPortDict.ContainsKey(PortName))
    {
        return ComPortDict[PortName].IsInit();
    }
    return false;
}

public static void Send(string PortName, byte[] cmd)//发送数据
{
    long nowTime = DateTime.Now.Ticks;

    if (ComPortDict.ContainsKey(PortName))
    {
        ComPortDict[PortName].Send(cmd);
    }
    else
    {
        //初始化之后再进行发送
    }
}

其基本思路很简单,就是使用一个字典记录串口,设备在每次操作串口时,在字典里寻找,如果能找到,执行操作,如果找不到,就先初始化串口。

 

共用串口需要注意两个问题:

(1)从机地址必须不一样。

(2)对于某些设备,上一个设备接收完,需要等一会再发送,才能接收成功。还有一种办法是用其他设备把这些共用串口的设备隔开,这样他们有休息的时间,也可以接收成功。

四、使用RTU的情况,接受到数据传给所有程序处理

正常情况下,工控机与设备的通信流程是这样的:

设备1发送数据

设备1接收数据,处理

设备2发送数据

设备2接收数据,处理

……

但在使用RTU的时候,这种方法就不适用了。如果添加了几个平台,由于接收数据是异步的,程序1可能接收到平台2的数据,而且,程序1接收之后,程序2不会再接收到数据,那数据就丢失了。

对于这种情况,我们使用复制数据的方法。如果是同一个串口接收到的数据,复制给其他共用串口的设备。

if (ComPortDict.ContainsKey(PortName))
{
    byte[] recv = ComPortDict[PortName].Receive();//接收数据,保存到临时变量

    if (recv != null && recv.Length != 0)//数据不为空
    {
        ComDataDict[PortName] = recv;//把数据保存起来
        ComCounterDict[PortName].count = 0;//重置计数器
        return recv;
    }
    else//数据为空
    {
        ComCounterDict[PortName].count++;//计数器加1
        if (ComCounterDict[PortName].count < ComCounterDict[PortName].total)//如果计数超过了共用这个串口的设备,说明这一次的通信确实失败了
        {
            return ComDataDict[PortName];
        }
        else
        {
            ComDataDict[PortName] = recv;//使用上一次保存的数据
            ComCounterDict[PortName].count = 0;
            return recv;
        }
    }
}
return null;

这里我们需要注意:

(1)通信是有可能失败的,不能一直使用保存起来的数据。需要使用一个计数器,在所有共用串口的设备都取完数后,就重置这个计数器。

(2)可以看出,这种方法在处理第三个问题的时候,是有冲突的。所以我们需要分开处理,第三个问题和第四个问题使用不同的处理方法。

五、采数和反控不能相互影响

采集数据是在一个线程里面循环完成的。用户随时会插入一个反控操作。执行反控时,发送和接收必须跟采集区分开,一个工作完成了才能做另外一个工作。我们采用以下方法:

采集和反控都放在一个类里面,然后一个工作正在执行的时候,使用Monitor,让另外一个工作进行等待。

六、多线程并发采数

在处理第六个问题的时候,我们推翻了第五个问题的方法。我们发现,Monitor会锁住所有线程,而不是一个线程内容的锁定。我们把每台设备的采集和反控放到不同的类里面,每个类独立占据一个线程进行运行。如果没有Monitor,线程运行正常,各个线程互不干扰。但Monitor的加入,破坏了这种秩序。一个线程使用了Monitor.Enter,其他线程都不动了,即使他们锁的对象并不一样。

对于这个问题,我们选择抛弃Monitor,而使用另外一种方法来区分采数和反控。方法流程图如下图所示:

这里需要指出的是,这种方法对于多设备共用串口的情况并不适用。所以在多设备共用串口时,我们没办法使用多线程采数。

 

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

串口通信开发 的相关文章

  • 内存溢出(out of memory)和内存泄露(memory leak)的区别和检测工具方法

    内存溢出 out of memory 是指程序在申请内存时 没有足够的内存空间供其使用 出现out of memory 比如申请了一个integer 但给它存了long才能存下的数 那就是内存溢出 内存泄露 memory leak 是指程序
  • 如何快速使用上C++11

    最近的几个项目都是用C 11写的 越用越觉得强大 很多的新特征也希望Java能整合进去就好了 打算改天有空也写个C 11的使用心得 无奈C 太博大精深 还有好几个很赞的功能还没掌握 现在试着在本机Ubuntu上使用C 11 找了好几种方法
  • 喜讯

    近日 中国信息通信研究院 以下简称 中国信通院 主办的首届 SecGo云和软件安全大会 成功举办 会上重磅揭晓了 安全守卫者计划 零信任 优秀案例征集活动结果 深圳市智安网络有限公司与大庆油田信息技术公司联合申报的零信任项目 凭借为企业提供
  • row_number() over partition by 分组聚合

    row number over partition by 分组聚合 分组聚合 就是先分组再排序 可以的话顺手标个排名 如果不想分组也可以排名 如果不想分组同时再去重排名也可以 ROW NUMBER OVER PARTITION BY col
  • Vue获得指定id的html,vue.js怎么删除指定id的数据

    本文环境 windows7 vue2 9 6 该方法适用于所有品牌的电脑 vue js删除指定id数据的方法 注意click需要传入当前的id值deletes function id this http delete http jsonpl
  • 认识sass

    一 认识sass SASS Syntactically Awesome Stylesheet 是一个CSS预处理器 有助于减少CSS的重复 节省时间 它是更稳定和强大的CSS扩展语言 描述文档的样式干净和结构 扩展了 CSS3 增加了规则
  • Java面向对象基础

    面向对象 学习内容 l 面向对象思想 l 类与对象及其使用 l 对象的内存图 l 成员变量和局部变量的区别 l 匿名对象 l 封装 private l this关键字 l 构造方法 l static关键字 l 继承 l 多态 l 抽象类 l
  • AVR单片机最小系统 基本硬件线路与分析

    AVR单片机最小系统 基本硬件线路与分析 AVR仿真器 AVR编程器 二合一 AVR JTAG与ISP 二合一V2 5 经典推荐 298 00元 富士通 MB90092 DEMO OSD视频字符叠加开发板 380 00元 国产 AVR JT
  • Wireshark使用技巧

    前言 Wireshark是一款图形界面的网络嗅探器 支持多种平台 是网络流量分析的利器 它的创始人是Gerald Combs 前身是Ethereal 作为开源项目经过众多开发者的完善它已经成为使用量最大的安全工具之一 最近刚把 Wiresh
  • 翰文进度计划软件横道图不显示文字_斑马进度计划2019,编制进度计划仅需8步!请收藏...

    斑马进度计划2019新功能简介 大突破 从此双代号网络计划支持父子结构啦 计划分分钟逐级拆解细化 前锋线也能自下而上反馈进度 进行多级管控里程碑预警啦 还有大家最心心念的TPM 全面计划管理 设计 招采 施工以及各专业全面联动计算 从此 项
  • 使用Crash工具分析 Linux dump文件

    前言 Linux 内核 以下简称内核 是一个不与特定进程相关的功能集合 内核的代码很难轻易的在调试器中执行和跟踪 开发者认为 内核如果发生了错误 就不应该继续运 行 因此内核发生错误时 它的行为通常被设定为系统崩溃 机器重启 基于动态存储器
  • 软件测试行业就业前景到底怎么样?

    软件测试就业前景非常好 目前IT行业对于软件测试方面的人才需求是非常大的 软件产品的质量对于一个软件来说是攸关生死的 各企业越来越重视软件产品质量 而软件测试的工作就是让软件质量越来越好 还有就是软件测试的工资待遇是非常好的 和其它职业相比
  • 《数据库系统概论》课程学习(26)——习题集(第1-14章)含答案

    数据库系统概论习题集 第一章 绪论 一 选择题 1 DBS是采用了数据库技术的计算机系统 DBS是一个集合体 包含数据库 计算机硬件 软件和 A 系统分析员 B 程序员 C 数据库管理员 D 操作员 2 数据库 DB 数据库系统 DBS 和
  • ssm框架整合(项目步骤)

    目录 一 前言 二 SSM框架 2 1 SSM整合到底整合什么 2 2 为什么要整合到一起 2 3 由谁来整合 2 4 ResponseBody注解的作用是什么 2 5 JSON 三 各框架应用场景 3 1 SpringMVC框架 3 2
  • 建立单链表并交换表中任意两个元素

    功能 建立单链表并交换表中任意两个元素 time 2017年3月12日15 07 25 include
  • 2021年 IEEE VIS 科学可视化与体渲染论文整理与分析

    因为最近工作的关系 需要研究一下IEEE VIS中2017年以后的与我之前主要方向 体渲染 医学可视化 有关的论文 我把这些年全部的论文进行了筛选和梳理 总共筛选出57篇论文 打算写一个文章来记录这些内容 这个栏目是2021年的九篇论文的介
  • MNIST数据库介绍及转换

    MNIST数据库介绍 MNIST是一个手写数字数据库 它有60000个训练样本集和10000个测试样本集 它是NIST数据库的一个子集 MNIST数据库官方网址为 http yann lecun com exdb mnist 也可以在win
  • springMVC ResponseBody 返回汉字乱码解决方案

    本文查考借鉴 http blog yimik com archives 899 js里通过ajax调用springmvc 后台返回的中文字符串乱码 通过搜索找解决方 大都让配置StringHttpMessageConverter这个bean

随机推荐

  • 本地JAR打镜像,并启动

    1 准备好jar 和Dockerfile文件 2 使用命令打镜像 docker build t wstest 3 查看镜像 4 由于服务是两个端口 使用以下命令 5 优化怎么随着docker的开启而启动 docker run restart
  • Maltrail恶意流量检测系统

    Maltrail恶意流量检测系统 项目介绍 项目GitHub地址 项目架构 项目数据集 运行方式 订阅源扩展 数据采集模块提取 项目介绍 maltrail是一款轻量级的恶意流量检测系统 其工作原理是通过采集网络中各个开源黑样本 包括IP 域
  • python操作sql

    from pymysql import connect def main 创建connection连接 conn connect host localhost port 3306 user root password 123456 data
  • 深入浅出UML类图(一)

    在UML 2 0的13种图形中 类图是使用频率最高的UML图之一 Martin Fowler在其著作 UML Distilled A Brief Guide to the Standard Object Modeling Language
  • Redis中key-value实现

    实现字典的方法有很多种 最简单的就是使用链表或数组 但是这种方式只适用于元素个数不多的情况下 要兼顾高效和简单性 可以使用哈希表 如果追求更为稳定的性能特征 并且希望高效地实现排序操作的话 则可以使用更为复杂的平衡树 在众多可能的实现中 R
  • JAVA8新特性--集合遍历之forEach

    java中的集合有两种形式Collection
  • 5.2 activiti任务监听器TaskListener

    1 任务监听器定义 任务监听器用于在特定的任务相关事件发生时 执行自定义的Java逻辑或表达式 2 监听器监听的事件 String EVENTNAME CREATE create 创建 当任务已经创建 并且所有任务参数都已经设置时触发 St
  • Basic Level 1090 危险品装箱 (25分)

    题目 集装箱运输货物时 我们必须特别小心 不能把不相容的货物装在一只箱子里 比如氧化剂绝对不能跟易燃液体同箱 否则很容易造成爆炸 本题给定一张不相容物品的清单 需要你检查每一张集装箱货品清单 判断它们是否能装在同一只箱子里 输入格式 输入第
  • Linux期末复习总结

    目录 Linux 系统的结构 shell Linux用户类型及其用户主目录 shell提示符 输入输出重定向 标准输入 输出设备 Linux文件的类型 Linux 的目录结构 基本操作命令 链接命令 ln 链接的特点 tar命令举例 vi
  • 求一批整数中出现最多的各位数字

    描述 给定一批整数 分析每个整数的每一位数字 求出现次数最多的各 位数字 例如给定3个整数1234 2345 3456 其中出现最多次数的数字是3和4 均出现了3次 输入 输入在第1行中给出正整数N 1000 在第二行中给出N个不超过整型范
  • QT QLinearGradient (颜色渐变---应用控件背景渐变)

    示例 frame gt setStyleSheet background color qlineargradient spread pad x1 0 y1 0 x2 1 y2 0 stop 0 030303 stop 0 5 030303
  • 多线程及多进程的选择

    我的理解是进程是指在系统中正在运行的一个应用程序 程序一旦运行就是进程 或者更专业化来说 进程是指程序执行时的一个实例 线程是进程的一个实体 进程 资源分配的最小单位 线程 程序执行的最小单位 线程进程的区别体现在几个方面 0 因为进程拥有
  • 【手撕代码系列】JS手写实现bind方法

    公众号 Code程序人生 分享前端所见所闻 在JavaScript中 bind方法允许我们绑定函数的上下文并返回一个新的函数 在这篇文章中 我们将手写实现bind方法 以更好地了解它的内部工作原理 bind方法的语法 bind方法的语法如下
  • 整数转化为罗马数字

    1 问题描述 罗马数字包含以下七种字符 I V X L C D 和 M 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000 例如 罗马数字 2 写做 II 即为两个并列的 1 12 写做 XII 即为 X
  • 多线程互斥锁 pthread_mutex 的使用及初始化问题

    一 互斥锁pthread mutex的使用 1 初始化锁 有两种方法初始化互斥锁 静态方式和动态方式 静态方式 pthread mutex t mutex lock PTHREAD MUTEX INITIALIZER 在LinuxThrea
  • 软件测试流程/需求分析之软件需求概述

    今天重点从四个方面来和大家对需求阶段需求分析做一个讲解 阅读测试文档 参与需求评审 测试需求分析 提取功能点 去做这些对应的事情之前 我们要先了解到底什么是需求 需求评审我们要从哪些点去评审 我们要去提取功能点要怎么样去进行提取 回忆测试流
  • 使用ffmpeg将mkv,rmvb转换成mp4

    cmd输入ffmpeg version 检查ffmpeg安装版本 1 进入mkv rmvb所在的文件夹 2 转换mkv ffmpeg i inputname mkv c v copy c a copy outputname mp4 转换rm
  • 几种优化算法(求最优解)

    几种优化算法 先简单备注下 今后接触到再看 参考资料 http blog sina com cn s blog 6a1bf1310101hhta html
  • SpringBean管理

    一 什么是SpringBean 在Spring中将管理对象称为 Bean Bean是由一个SpringIOC容器实例化 组装和管理的对象 也就是说 Bean并不是由我们程序员编写的 而是在程序运行过程中 由Spring通过反射机制生成的 S
  • 串口通信开发

    一开始做串口通信开发时 觉得并不难 无非就是发送 然后等一会 再接收就完事了 其实里面的水很深 特别是在各种设备都有的情况下 我们在整个开发过程中 遇到了以下的几个主要问题 1 设备出现严重的延迟 2 接收过程出现数据粘包或截断 3 多设备