首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

【摘译+整理】System.IO.Ports.SerialPort使用注意

编程知识
2024年07月26日 07:57

远古的一篇博客,内容散落于博文和评论 https://sparxeng.com/blog/software/must-use-net-system-io-ports-serialport

C# 和 .NET Framework 提供了一种快速的应用程序开发,非常适合需要随着硬件设计的发展跟踪不断变化的需求的早期开发。在大多数方面都很理想。但.NET 附带的 System.IO.Ports.SerialPort 类是一个明显的例外。委婉地说,它是由计算机科学家设计的,远远超出了他们的核心能力领域。他们既不了解串行通信的特征,也不了解常见的用例。在发布之前,它也不可能在任何真实场景中进行测试,而不会发现漏洞,这些缺陷会使 System.IO.Ports.SerialPort (以下简称IOPSP)的可靠通信成为真正的噩梦。(StackOverflow 上的大量证据证明了这一点)。

更令人惊讶的是,当底层kernel32.dll API 非常好时,还会发生这种级别的问题(我在使用 .NET 之前使用过 WinAPI)。.NET 工程师不仅没有设计出合理的接口,还选择无视非常成熟的 WinAPI 设计,也没有从二十年的内核团队串行移植经验中吸取教训。

下面列出其可靠和不可靠的成员列表:

  • event DataReceived (100%冗余,也完全不可靠)
  • BytesToRead 属性 (完全不可靠)
  • ReadReadExistingReadLine 函数(处理错误完全错误,并且是同步的)
  • PinChanged event (顺序完全不能保证)

可以安全使用的列表:

  • 属性: BaudRate 、 DataBits 、 Parity 、 StopBits ,但仅在打开端口之前。并且仅适用于标准波特率。
  • Handshake属性
  • 构造函数、 PortName属性、 Open函数 IsOpen函数、 GetPortNames函数

还有一个没人使用的成员,因为 MSDN 没有给出任何示例,但对你绝对是必不可少的

  • BaseStream

唯一正常工作的串行端口读取方法是通过 BaseStream 访问。

以下示例是接收数据的错误方式:

port.DataReceived += port_DataReceived;

// (later, in DataReceived event)
try {
    byte[] buffer = new byte[port.BytesToRead];
    port.Read(buffer, 0, buffer.Length);
    raiseAppSerialDataEvent(buffer);
}
catch (IOException exc) {
    handleAppSerialError(exc);
}

下面是正确的方法,它与基础 Win32 API 的使用方式相匹配:

byte[] buffer = new byte[blockLimit];
Action kickoffRead = null;
kickoffRead = delegate {
    port.BaseStream.BeginRead(buffer, 0, buffer.Length, delegate (IAsyncResult ar) {
        try {
            int actualLength = port.BaseStream.EndRead(ar);
            byte[] received = new byte[actualLength];
            Buffer.BlockCopy(buffer, 0, received, 0, actualLength);
            raiseAppSerialDataEvent(received);
        }
        catch (IOException exc) {
            handleAppSerialError(exc);
        }
        kickoffRead();
    }, null);
};
kickoffRead();

从 .NET 4.5 开始,可以改为调用 ReadAsync BaseStream 对象,该对象在内部调用 BeginRead 和 EndRead 。

或者就直接调用 Win32 API的方式。

第一个例子的问题在于:

第一个也是最严重的是 DataReceived 在线程池线程上触发,并且可以再次触发,而无需等待上一个事件处理程序返回。因此,它会导致你进入竞争条件,当你去读取缓冲区时,比 BytesToRead 承诺的要少,因为事件处理程序的另一个实例同时读取它们。应用程序程序员可以通过显式同步来克服这个问题。

但同步不会解决实现本身中存在的竞争条件。BytesToRead 调用 ClearCommError 以获取缓冲区级别,并丢弃其他所有内容。但是 ClearCommError 是对串口寄存器错误标志位的原子交换——你只会看到一次,然后就会清除他们。框架中正在查看这些标志的其他代码以触发 PinChangedErrorReceived 事件由于 BytesToRead(查看并清除寄存器) 会忽略它们,因此事件会丢失。事实上,ErrorReceived 事件的 MSDN 页面显示“由于操作系统决定是否引发此事件,因此不会报告所有奇偶校验错误。这是一个彻头彻尾的谎言 — 事件丢失发生在 BytesToReadBytesToWrite 的 getter 函数中。

译者注:这段原因写在作者和游客的讨论中,感兴趣的可以看看原文。如果写过STM32就能理解作者的意思(STM32的有些寄存器在读后就会清除,这样就会导致丢失了一些事件)。作者表示如果你对串口通信的要求很高,(高性能/低时延/错误等)你就应该使用Win32 API或者 p/invoke 或 C++/CLI。(作者列了写商业库说也能用,感兴趣的在原文评论区自行找一下)。如果你对串口通信的要求不高,其实DataReceived的方式其实也能用。

From:https://www.cnblogs.com/zhangchen-trunk/p/18324550
本文地址: http://www.shuzixingkong.net/article/440
0评论
提交 加载更多评论
其他文章 《HelloGitHub》第 100 期
HelloGitHub 每月 28 号(遇到周末就会提前发,不想占用大家宝贵的周末时间^_^),分享 GitHub 上有趣、入门级的开源项目,让你更好地体验开源的魅力。
《HelloGitHub》第 100 期 《HelloGitHub》第 100 期 《HelloGitHub》第 100 期
七天.NET 8操作SQLite入门到实战 - (3)第七天Blazor学生管理页面编写和接口对接
前言 本章节的主要内容是完善Blazor学生管理页面的编写和接口对接。 七天.NET 8 操作 SQLite 入门到实战详细教程 第一天 SQLite 简介 第二天 在 Windows 上配置 SQLite 环境 第三天 SQLite 快速入门 第四天 EasySQLite 前后端项目框架搭建 第五
七天.NET 8操作SQLite入门到实战 - (3)第七天Blazor学生管理页面编写和接口对接 七天.NET 8操作SQLite入门到实战 - (3)第七天Blazor学生管理页面编写和接口对接 七天.NET 8操作SQLite入门到实战 - (3)第七天Blazor学生管理页面编写和接口对接
手把手教你本地运行Meta最新大模型:Llama3.1,可是它说自己是ChatGPT?
就在昨晚,Meta发布了可以与OpenAI掰手腕的最新开源大模型:Llama 3.1。 该模型共有三个版本: 8B 70B 405B 对于这次发布,Meta已经在超过150个涵盖广泛语言范围的基准数据集上评估了性能。此外,Meta还进行了广泛的人工评估,在现实场景中将Llama 3.1与竞争模型进行
手把手教你本地运行Meta最新大模型:Llama3.1,可是它说自己是ChatGPT? 手把手教你本地运行Meta最新大模型:Llama3.1,可是它说自己是ChatGPT? 手把手教你本地运行Meta最新大模型:Llama3.1,可是它说自己是ChatGPT?
Java代码实现七夕魔方照片墙
本文详细介绍了Java代码实现七夕魔方照片墙的方法,本文提供一个简化的Java后端示例,用于生成一个模拟的“照片墙”数据模型,并给出一个基本的前端HTML页面来展示这些数据;本文还提供了创建一个简单的REST API来提供照片数据,并构建一个前端页面来动态展示这些数据。
Java代码实现七夕魔方照片墙
《最新出炉》系列入门篇-Python+Playwright自动化测试-53- 处理面包屑(详细教程)
1.简介 面包屑(Breadcrumb),又称面包屑导航(BreadcrumbNavigation)这个概念来自童话故事“汉赛尔和格莱特”,当汉赛尔和格莱特穿过森林时,不小心迷路了,但是他们发现沿途走过的地方都撒下了面包屑,让这些面包屑来帮助他们找到回家的路。所以,面包屑导航的作用是告诉访问者他们在
《最新出炉》系列入门篇-Python+Playwright自动化测试-53- 处理面包屑(详细教程) 《最新出炉》系列入门篇-Python+Playwright自动化测试-53- 处理面包屑(详细教程) 《最新出炉》系列入门篇-Python+Playwright自动化测试-53- 处理面包屑(详细教程)
仓颉编程语言技术指南:嵌套函数、Lambda 表达式、闭包
本文分享自华为云社区《【华为鸿蒙开发技术】仓颉编程语言技术指南【嵌套函数、Lambda 表达式、闭包】》,作者:柠檬味拥抱。 仓颉编程语言(Cangjie)是一种面向全场景应用开发的通用编程语言,旨在兼顾开发效率和运行性能,并提供良好的编程体验。本文将深入探讨仓颉语言的主要特点和功能,包括其简明高效
人脸识别项目打包成exe的过程遇到的问题
我最近重新拾起了计算机视觉,借助Python的opencv还有face_recognition库写了个简单的图像识别demo,额外定制了一些内容,原本想打包成exe然后发给朋友,不过在这当中遇到了许多小问题,都解决了,记录一下踩过的坑。 1、Pyinstaller打包过程当中出现warning,跟d
人脸识别项目打包成exe的过程遇到的问题
iOS开发基础143-性能优化
我们可以先构建一个详细的大纲,然后在每个部分详细阐述。下面是一个针对iOS性能优化的详细大纲: 一. App启动时间优化 A. 启动分类 冷启动 热启动 B. 冷启动优化 减少启动时的动态库加载 尽可能减少动态库的数量,采用静态库或者合并一部分动态库。 优化启动时的代码执行 延迟不必要的初始化操作,