SerialPort类的用法与示例

2023-10-26

SerialPort类的用法与示例


从Microsoft .Net 2.0版本以后,就默认提供了 System.IO.Ports.SerialPort类,用户可以非常简单地编写少量代码就完成串口的信息收发程序。本文将介绍如何在PC端用C# .Net 来开发串口应用程序。

1. 串口硬件信号定义

DB9 Connector 信号定义

Image 1

针脚 信号 定义 作用
1 DCD 载波检测 Received Line Signal Detector(Data Carrier Detect)
2 RXD 接收数据 Received Data
3 TXD 发送数据 Transmit Data
4 DTR 数据终端准备好 Data Terminal Ready
5 SGND 信号地 Signal Ground
6 DSR 数据准备好 Data Set Ready
7 RTS 请求发送 Request To Send
8 CTS 清除发送 Clear To Send
9 RI 振铃提示 Ring Indicator

2. 串口端口号搜索

一个最简单的办法:

string[] portList = System.IO.Ports.SerialPort.GetPortNames();  // 静态方法

还有一种通过调用API的方法来获取实现,可以获取详细的完整串口名称,对于USB-to-COM虚拟串口来说特别适用。它可以获取到与设备管理器中一样的名字,例如“Prolific USB-to-Serial Comm Port(COM34)”, 而上面的方法只能获取到“COM34”。

3. 串口属性参数设置

SerialPort类所包含的属性详见下表:

名称 说明
BaseStream 获取 Stream 对象的基础 SerialPort 对象。
BaudRate 获取或设置串行波特率。
BreakState 获取或设置中断信号状态。
BytesToRead 获取接收缓冲区中数据的字节数。
BytesToWrite 获取发送缓冲区中数据的字节数。
CanRaiseEvents 获取一个值,该值指示组件是否可以引发一个事件。(继承自 Component。)
CDHolding 获取端口的载波检测行的状态。
Container 获取 IContainer ,其中包含 Component。(继承自 Component。)
CtsHolding 获取“可以发送”行的状态。
DataBits 获取或设置每个字节的标准数据位长度。
DesignMode 获取一个值,该值指示是否 Component 当前处于设计模式。(继承自 Component。)
DiscardNull 获取或设置一个值,该值指示 null 字节在端口和接收缓冲区之间传输时是否被忽略。
DsrHolding 获取数据设置就绪 (DSR) 信号的状态。
DtrEnable 获取或设置一个值,该值在串行通信过程中启用数据终端就绪 (DTR) 信号。
Encoding 获取或设置传输前后文本转换的字节编码。
Events 获取的事件处理程序附加到此列表 Component。(继承自 Component。)
Handshake 使用 Handshake 中的值获取或设置串行端口数据传输的握手协议。
IsOpen 获取一个值,该值指示 SerialPort 对象的打开或关闭状态。
NewLine 获取或设置用于解释 ReadLine 和 WriteLine 方法调用结束的值。
Parity 获取或设置奇偶校验检查协议。
ParityReplace 获取或设置一个字节,该字节在发生奇偶校验错误时替换数据流中的无效字节。
PortName 获取或设置通信端口,包括但不限于所有可用的 COM 端口。
ReadBufferSize 获取或设置 SerialPort 输入缓冲区的大小。
ReadTimeout 获取或设置读取操作未完成时发生超时之前的毫秒数。
ReceivedBytesThreshold 获取或设置 DataReceived 事件发生前内部输入缓冲区中的字节数。
RtsEnable 获取或设置一个值,该值指示在串行通信中是否启用请求发送 (RTS) 信号。
Site 获取或设置 ISite 的 Component。(继承自 Component。)
StopBits 获取或设置每个字节的标准停止位数。
WriteBufferSize 获取或设置串行端口输出缓冲区的大小。
WriteTimeout 获取或设置写入操作未完成时发生超时之前的毫秒数。

简单初始化串口参数的示例程序:

SerialPort mySerialPort = new SerialPort("COM2"); // 构造参数指定串口名
//serialPort.PortName = "COM2";
mySerialPort.BaudRate = 9600;
mySerialPort.Parity=Parity.None;
mySerialPort.StopBits = StopBits.One;
mySerialPort.DataBits = 8;

mySerialPort.Open();  // 打开串口

// DataReceived是串口类的一个事件,通过+=运算符订阅事件,如果串口接收到数据就触发事件,调用DataReceive_Method事件处理方法
mySerialPort.DataReceived += new SerialDataReceivedEvenHandler(DataReceive_Method);
 
// 事件处理方法
public static void DataReceive_Method(object sender, SerialDataReceivedEventArgs e)
{
    //sender是事件触发对象,通过它可以获取到mySerialPort
    SerialPort mySerialPort = (SerialPort)sender;
}

mySerialPort.Close();  // 关闭串口

4. 串口发送信息

SerialPort类定义了多种方法用于串口发送信息。

Write(Byte[], Int32, Int32)使用缓冲区中的数据将指定数量的字节写入串行端口

注意一字节(Byte)是8个比特(bit),而一个十六进制数是4个比特,两个十六进制数就是8个比特,即一个字节。所以要想发送十六进制数就得用写入字节的函数。

byte[] t = new byte[2];
t[0] = 0xAA;
t[1] = 0xBB;
串口对象.Write(t,0,2);

==Write(Char[], Int32, Int32)==使用缓冲区中的数据将指定数量的字符写入串行端口
==Write(String)==将指定的字符串写入串行端口
==WriteLine(String)==将指定的字符串和NewLine值写入输出缓冲区

下面是一个简单的例子说明如何通过串口发送字符串和字节数据:

private static void SendSampleData()
{
    SerialPort port = new SerialPort(
        "COM1", 9600, Parity.None, 8, StopBits.One);

    port.Open();

    port.Write("Hello World");

    port.Write(new byte[] { 0x0A, 0xE2, 0xFF }, 0, 3);

    port.Close();
}

下面是如何发送一个文本文件的例子:

private static void SendTextFile(SerialPort port, string FileName)
{
    port.Write(File.OpenText(FileName).ReadToEnd());
}

下面是如何发送一个二进制文件的例子:

private static void SendBinaryFile(SerialPort port, string FileName)
{
    using (FileStream fs = File.OpenRead(FileName))
        port.Write((new BinaryReader(fs)).ReadBytes((int)fs.Length), 0, (int)fs.Length);
}

5. 串口接收信息

SerialPort类定义了多种方法用于串口接收信息。

Read(Byte[], Int32, Int32) 从SerialPort输入缓冲区读取一些字节,并将那些字节写入字节数组中指定的偏移量处
Read(Byte[], Int32, Int32)从SerialPort输入缓冲区读取一些字符,并将那些字符写入字符数组中指定的偏移量处
ReadByte() 从SerialPort输入缓冲区中同步读取一个字节
ReadChar() 从SerialPort输入缓冲区中同步读取一个字符
ReadExisting() 在编码的基础上,读取SerialPort对象的流和输入缓冲区中所有立即可用的字节
ReadLine()一直读取到输入缓冲区中的NewLine值
ReadTo(String)一直读取到输入缓冲区中的指定value的字符串

通常一个比较常见的用法就是将串口里面立即能用的字符或数据读取然后打印在textbox等控件中显示。

private SerialPort port = new SerialPort("COM1",
                                         9600, Parity.None, 8, StopBits.One);

port.DataReceived += new
    SerialDataReceivedEventHandler(port_DataReceived);

port.Open();

private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    Console.WriteLine(port.ReadExisting());
}

另外还有一种应用场合是需要缓存一段串口接收数据,然后在缓存数据中查找有用信息,这时可以采用下面例子所用的办法。

SerialPort com = new SerialPort(SerialPort.GetPortNames()[0],
                                9600, Parity.None, 8, StopBits.One);

List<byte> bBuffer = new List<byte>();
string sBuffer = String.Empty;

com.DataReceived += new SerialDataReceivedEventHandler(com_DataReceived);

com.Open();

void com_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    while (com.BytesToRead > 0)
        bBuffer.Add((byte)com.ReadByte());
    ProcessBuffer(bBuffer);

    sBuffer += com.ReadExisting();
    ProcessBuffer(sBuffer);
}

private void ProcessBuffer(string sBuffer)
{}

private void ProcessBuffer(List<byte> bBuffer)
{}

串口工具类

public class SerialPortUtils
{
    public static SerialPort serialPort = null;   // 定义一个静态的串口对象

    /// <summary>
    /// 搜索串口
    /// </summary>
    /// <returns></returns>
    public static string[] GetPortNames() 
    {
        return SerialPort.GetPortNames();
    }

    /// <summary>
    /// 打开或者关闭串口
    /// </summary>
    /// <param name="comName"></param>
    /// <param name="baud"></param>
    /// <returns></returns>
    public static SerialPort OpenClosePort(string comName, int baud)
    {
        //串口未打开
        if (serialPort == null || !serialPort.IsOpen)
        {
            serialPort = new SerialPort();
            //串口名称
            serialPort.PortName = comName;
            //波特率
            serialPort.BaudRate = baud;
            //数据位
            serialPort.DataBits = 8;
            //停止位
            serialPort.StopBits = StopBits.One;
            //校验位
            serialPort.Parity = Parity.None;
            //打开串口
            serialPort.Open();
            //串口数据接收事件实现
            serialPort.DataReceived += new SerialDataReceivedEventHandler(ReceiveData);

            return serialPort;
        }
        //串口已经打开
        else
        {
            serialPort.Close();
            return serialPort;
        }
    }

    /// <summary>
    /// 接收数据
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public static void ReceiveData(object sender, SerialDataReceivedEventArgs e)
    {
        SerialPort _SerialPort = (SerialPort)sender;

        int _bytesToRead = _SerialPort.BytesToRead;
        byte[] recvData = new byte[_bytesToRead];

        _SerialPort.Read(recvData, 0, _bytesToRead);

        //向控制台打印数据
        Debug.WriteLine("收到数据:" + recvData);
    }

    /// <summary>
    /// 发送数据
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    public static bool SendData(byte[] data)
    {
        if (serialPort != null && serialPort.IsOpen)
        {
            serialPort.Write(data, 0, data.Length);
            Debug.WriteLine("发送数据:" + data);
            return true;
        }
        else
        {
            return false;
        }
    }
}

串口使用注意事项

参考:https://blog.csdn.net/wuyazhe/article/details/5606276

Open()打开串口的时候会创建一个监听线程ListenThread,DataReceived事件就是在这个线程进行处理的,与此同时还有一个主线程即UIThread,这个时候要注意线程同步问题。

在DataReceived事件处理函数中,一般是调用Read()方法读取串口缓存区数据,读取完后进行容错处理,之后解析数据,解析完成后更新UI。在监听线程中是无法更新主线程的UI的,所以必须调用Invoke方法回到主线程更新UI。但是由于Invoke是同步函数,在UI未完成更新之前,监听线程一直停在DataReceived事件处理函数中。此时主线程如果调用Close()函数关闭串口,由于监听线程的资源还没来得及释放,造成死锁。

解决方法就是用BeginInvoke异步调用去更新UI

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

SerialPort类的用法与示例 的相关文章

随机推荐

  • Qt中对TCP粘包的处理

    当时用TCP协议传输数据时 经常出现粘包的现象 当服务器向客户端发送数据之后 客户端还没有接收数据的时候 这段时间数据在什么地方 1 服务器 服务器已经发出数据了 2 网线 数据应该在内存 怎么会在网线里面 又没有内存 3 客户端 是的 这
  • GoFrame框架入门教程一(下载框架)

    本地环境 1 Windows 2 go版本 1 17 3 编辑器 GoLand 2021 3 2 新建项目 Environment 处填写 GOPROXY https goproxy cn 使用 go mod文件管理项目包 module g
  • sqli-labs————less 22

    Less 22 简单测试 username admin password aaa 查看一下源代码如下
  • linux系统离线安装miniconda3 及创建python环境

    在linux系统中安装python开发环境 一般采取安装miniconda的方法 不建议安装anaconda miniconda是一个anaconda的轻量级 默认只有python跟conda 有时候出于安全性考虑 服务器不允许连接外网 因
  • STM32一键下载电路设计原理

    先放原理图 补充 图中的BOOT0通过10K的电阻接到地 再解释为什么这么设计 STM32启动方式 BOOT0和 BOOT1用于设置 STM32的启动方式 见下表 BOOT0 1 BOOT1 0 串口下载模式 BOOT0 0 BOOT1 X
  • 聚类(K-means)实现手写数字识别

    其他实现手写数字识别的方法 1 KNN实现手写数字识别 2 卷积神经网络 CNN 实现手写数字识别 3 全连接神经网络实现手写数字识别 4 聚类 K means 实现手写数字识别 2 实验数据是老师收集了所有人的手写数字图片 且经过处理将图
  • 使用checkpoint遇到的问题

    使用checkpoint时 警告使用checkpoint时 警告UserWarning None of the inputs have requires grad True 原因 使用checkpoint 不能放在第一个位置 或者说放在第一
  • 如何采用conda配置python虚拟环境

    文章目录 一 创建python虚拟环境 二 配置刚创建的虚拟环境 三 将虚拟环境配置到相应项目 一 创建python虚拟环境 首先选中要配置环境的文件 如下 在此处输入cmd按回车 此处我创建一个环境名为hands3dtext 环境版本为3
  • 机器人毕业设计题目推荐/康复机器人、(三、四、五、六度机器人)、焊接机器人、履带式搜救机器人、管道机器人、关节机器人、码垛机器人、焊接机器人、爬壁机器人、扫地机器人、喷涂机器人、搬运机器人……

    机器人毕业设计题目共有2000多套 部分列表如下 上肢康复机器人结构设计 全套 本科毕业设计 论文 CAD图纸 开题报告 任务书 三自由度机械手 工业机器人 说明书 CAD图纸 三自由度焊接机器人设计 毕业设计说明书 论文 12份CAD图纸
  • Windows下安装Qt5.12.8(1)

    一 下载 Qt5 12 8 Index of archive qt 5 12 5 12 8 下载其他版本将链接对应数字替换即可 windows下载 exe linux下载 run mac下载 dmg 二 安装 1 双击下载后的可执行程序 点
  • 虚拟机支持本地nvme ssd

    提起存储都是血泪史 不知道丢了多少数据 脑子首先想到的就是 你说啥 洗脑神曲 我就像那个大妈一样 千万个问号 hdd是啥 ssd又是啥 mbr是啥 gpt又是啥 primary partion是啥 logical partion又是啥 sa
  • java日志管理-学习(二)

    log4j Log4j是Apache的一个开源项目 通过使用Log4j 我们可以控制日志信息输送的目的地是控制台 文件 GUI组件 甚至是套接口服务器 NT的事件记录器 UNIX Syslog守护进程等 我们也可以控制每一条日志的输出格式
  • unity3d pivot与center local与global

    local是指的自身的坐标 global指的是世界坐标 假如一个物体没有父物体 即这个物体不是某个物体的子物体 或者父物体的坐标为 0 0 0 这时候local和global的坐标是一样的 local坐标是相对与父物体的坐标 假如有父物体
  • Latex公式编号: 多行公式多编号,多行公式单编号

    目录 多行公式多编号 多行公式单编号 编号居中 多行公式无编号 多行公式多编号 有的行没有编号 一行公式分多行写 情况 case 划分 大括号单编号 大括号多编号 提示 begin align 与 begin equation 不能同时使用
  • 瞧瞧苹果OS X如何干掉Linux

    原文地址 http www csdn net article 2012 08 28 2809270 osx killed linux 摘要 如果你去过Facebook或者其它一些创业类科技公司 你会发现随处可见的Mac 无论是CEO还是开发
  • 【STM32学习】搭建一个简单的 keil5 工程

    一 安装 pack 支持包 pack是支持包文件 当你的板子连接到电脑时 keil5 怎么知道你的板子是哪个型号的 这就需要用到 pack 文件了 Keil 官方下载pack文件的地址 download device pack 我这里使用的
  • Mybatis的mapper对象注入到Spring容器中的过程

    整合的核心组件 MapperScan MapperScannerRegistrar ImportBeanDefinitionRegistrar ClassPathBeanDefinitionScanner FactoryBean 一 在Sp
  • 支配世界的几个重要算法

    算法应当具有以下三大重要特征才被视为拥有实际效果 应该是有限的 算法应该在有限的时间内用有限的步骤解决掉其旨在解决的问题 也就是说算法必须在有限的时间内可以完成 要不然就没有现实意义 应该具有明确的指令 算法中的每个步骤必须经过精确定义 同
  • QT编译安装QtMqtt子模块,Linux平台

    QT安装QtMqtt子模块 include文件夹和src文件夹 lib文件夹 mkspecs文件夹 错误修改 总结 系统 Windows10 环境 QT5 12 9 源码下载和源码的编译请参考 QT编译安装QtMqtt子模块 WIN平台 网
  • SerialPort类的用法与示例

    SerialPort类的用法与示例 文章目录 SerialPort类的用法与示例 1 串口硬件信号定义 2 串口端口号搜索 3 串口属性参数设置 4 串口发送信息 5 串口接收信息 串口工具类 串口使用注意事项 从Microsoft Net