当前位置: 代码网 > it编程>编程语言>Asp.net > C# SerialPort类实现串口通信的实战指南

C# SerialPort类实现串口通信的实战指南

2026年01月18日 Asp.net 我要评论
前言在工业自动化、物联网和嵌入式系统中,串口通信仍然扮演着不可替代的角色。尽管网络通信技术发展迅速,但在一些对稳定性、实时性要求较高的场景中,串口通信依然具有广泛的应用基础。c# 语言通过 syste

前言

在工业自动化、物联网和嵌入式系统中,串口通信仍然扮演着不可替代的角色。尽管网络通信技术发展迅速,但在一些对稳定性、实时性要求较高的场景中,串口通信依然具有广泛的应用基础。

c# 语言通过 system.io.ports 命名空间中的 serialport 类,为开发者提供了便捷的串口编程接口。本文将从基础使用出发,深入讲解 serialport 类的属性、事件处理、异常捕获及 winform 中的实际应用,帮助开发开发稳定、安全的串口通信程序。

一、serialport 类

1、serialport 类的基本属性与构造函数

c# 提供了 serialport 类用于串口通信,它支持多种构造函数。一个完整的构造函数如下:

public serialport(
    string portname,
    int baudrate,
    parity parity,
    int databits,
    stopbits stopbits
)

例如,设置串口为 com1、波特率 9600、无奇偶校验、数据位 8 和停止位 1 的代码如下:

serialport serialport = new serialport("com1", 9600, parity.none, 8, stopbits.one);

2、属性说明

属性名称描述常用取值
portname串口号,如 "com1"、"com2"具体的系统端口号
baudrate数据传输速率9600、115200 等
parity奇偶校验none、even、odd
databits每个数据帧的位数8
stopbits数据帧结束标志one、two
handshake数据传输时的流控制措施none、xonxoff、requesttosend
readtimeout读取数据的超时时间毫秒数,如 500
writetimeout写入数据的超时时间毫秒数,如 500
ctsholdingtrue 表示对方设备已准备好接收数据true、false,需硬件 cts 支持
cdholdingtrue 表示检测到载波信号true、false,需硬件 cd 支持

二、串口事件:数据接收与 ui 更新

在串口通信中,数据接收是核心环节。serialport 类通过 datareceived 事件实现异步接收。在 winform 应用中,事件处理函数运行在后台线程,不能直接更新 ui 控件。为此,应使用 begininvoke 方法进行异步更新,避免阻塞主线程。

public delegate void updateuidelegate(byte[] data);

private void comm_datareceived(object sender, serialdatareceivedeventargs e)
{
    byte[] receiveddata = new byte[8];
    try
    {
        serialport.read(receiveddata, 0, 6);
        this.begininvoke(new updateuidelegate(updateui), receiveddata);
    }
    catch (timeoutexception ex)
    {
        messagebox.show("超时:" + ex.message);
    }
}

private void updateui(byte[] data)
{
    string receivedstr = system.text.encoding.default.getstring(data);
    this.textboxdata.text = receivedstr;
}

三、串口的打开、关闭与参数配置

1、打开串口

打开串口前应确保参数配置完成,并进行异常捕获:

try
{
    serialport.open();
}
catch (unauthorizedaccessexception ex)
{
    messagebox.show("权限不足或串口正在使用:" + ex.message);
}
catch (ioexception ex)
{
    messagebox.show("i/o错误:" + ex.message);
}

2、关闭串口与安全退出

关闭串口时,若存在未完成的线程操作,可能导致死锁。

建议使用标志变量控制流程:

private bool isreceiving = false;
private bool istryingtoclose = false;

public void safecloseserialport()
{
    istryingtoclose = true;
    while (isreceiving)
    {
        system.windows.forms.application.doevents();
    }
    serialport.close();
}

3、参数配置示例

serialport serialport = new serialport();
serialport.portname = "com5";
serialport.baudrate = 115200;
serialport.parity = parity.none;
serialport.databits = 8;
serialport.stopbits = stopbits.one;
serialport.handshake = handshake.none;
serialport.readtimeout = 500;
serialport.writetimeout = 500;

四、常见异常处理策略

1、端口占用与权限问题

try
{
    serialport.open();
}
catch (unauthorizedaccessexception ex)
{
    messagebox.show("串口访问权限不足:" + ex.message);
}
catch (ioexception ex)
{
    messagebox.show("串口可能不存在或被占用:" + ex.message);
}

2、超时异常处理

try
{
    byte[] buffer = new byte[serialport.bytestoread];
    int bytesread = serialport.read(buffer, 0, buffer.length);
}
catch (timeoutexception ex)
{
    messagebox.show("数据读取超时:" + ex.message);
}

3、未打开串口的操作

if (!serialport.isopen)
{
    messagebox.show("串口尚未打开,请先调用 open() 方法。");
    return;
}

4、ctsholding 与 cdholding 支持

public static bool ishardwareflowcontrolsupported(serialport port)
{
    try
    {
        bool originalrts = port.rtsenable;
        bool originaldtr = port.dtrenable;

        port.rtsenable = false;
        thread.sleep(10);
        bool ctslow = port.ctsholding;

        port.rtsenable = true;
        thread.sleep(10);
        bool ctshigh = port.ctsholding;

        port.rtsenable = originalrts;
        port.dtrenable = originaldtr;

        return ctslow != ctshigh;
    }
    catch
    {
        return false;
    }
}

五、winform 环境下串口通信的实现示例

以下是一个完整的 winform 示例界面和核心代码结构:

using system.io.ports;
using timer = system.windows.forms.timer;

namespace appserialportexplained
{
    public partial class form1 : form
    {
        private serialport serialport = new serialport();
        private timer statustimer = new timer();

        public form1()
        {
            initializecomponent();
            refreshportlist();
            comboboxbaud.items.addrange(new object[] { "1200", "2400", "4800", "9600", "19200", "38400", "57600", "115200" });
            comboboxbaud.selectedindex = 3;
            comboboxparity.items.addrange(new object[] { "none", "even", "odd", "mark", "space" });
            comboboxparity.selectedindex = 0;
            comboboxdatabits.items.addrange(new object[] { "5", "6", "7", "8" });
            comboboxdatabits.selectedindex = 3;
            comboboxstopbits.items.addrange(new object[] { "one", "two", "onepointfive" });
            comboboxstopbits.selectedindex = 0;
            comboboxhandshake.items.addrange(new object[] { "none", "xonxoff", "requesttosend", "requesttosendxonxoff" });
            comboboxhandshake.selectedindex = 0;
            numericupdownreadtimeout.value = 500;
            numericupdownwritetimeout.value = 500;
            serialport.datareceived += serialport_datareceived;
            serialport.errorreceived += serialport_errorreceived;
            serialport.pinchanged += serialport_pinchanged;
            initializetimer();
        }

        private void initializetimer()
        {
            statustimer.interval = 100;
            statustimer.tick += statustimer_tick;
        }

        private void refreshportlist()
        {
            string selectedport = comboboxport.text;
            comboboxport.items.clear();
            comboboxport.items.addrange(serialport.getportnames());
            if (comboboxport.items.count > 0)
            {
                if (comboboxport.items.contains(selectedport))
                    comboboxport.text = selectedport;
                else
                    comboboxport.selectedindex = 0;
            }
        }

        private void buttonrefresh_click(object sender, eventargs e)
        {
            refreshportlist();
        }

        private void buttonopen_click(object sender, eventargs e)
        {
            if (!serialport.isopen)
            {
                try
                {
                    serialport.portname = comboboxport.text;
                    serialport.baudrate = int.parse(comboboxbaud.text);
                    switch (comboboxparity.text)
                    {
                        case "none": serialport.parity = parity.none; break;
                        case "even": serialport.parity = parity.even; break;
                        case "odd": serialport.parity = parity.odd; break;
                        case "mark": serialport.parity = parity.mark; break;
                        case "space": serialport.parity = parity.space; break;
                    }
                    serialport.databits = int.parse(comboboxdatabits.text);
                    switch (comboboxstopbits.text)
                    {
                        case "one": serialport.stopbits = stopbits.one; break;
                        case "two": serialport.stopbits = stopbits.two; break;
                        case "onepointfive": serialport.stopbits = stopbits.onepointfive; break;
                    }
                    serialport.handshake = (handshake)enum.parse(typeof(handshake), comboboxhandshake.text.replace("xonxoff", "xonxoff"));
                    serialport.readtimeout = (int)numericupdownreadtimeout.value;
                    serialport.writetimeout = (int)numericupdownwritetimeout.value;
                    serialport.open();
                }
                catch (exception ex)
                {
                    messagebox.show("打开串口失败:" + ex.message);
                }
            }
        }
    }
}

效果预览

六、常见问题与解决方案

在实际开发过程中,使用 serialport 类时会遇到许多常见问题,下面列举并详细介绍解决方案:

死锁问题与 ui 更新阻塞

在调用 serialport.close() 时,如果数据接收线程仍在运行,采用 invoke 调用 ui 更新方法会导致同步等待,最终引起死锁问题。解决这一问题的方法是改为使用 begininvoke 进行异步调用,以避免线程阻塞。

串口线程安全性问题

在多线程环境下,数据接收线程与 ui 主线程可能同时访问共享资源,若不加保护,容易引起数据竞争问题。通常的解决办法是采用标志控制(如 isreceivingistryingtoclose)以及使用 application.doevents() 循环确保所有后台线程结束后再关闭串口。

异常捕获不足

许多开发在编写串口通信代码时,往往忽略了对各种异常(超时、i/o 错误、未打开串口等)的充分捕获。应在关键操作(如 open、read、write)处使用 try-catch 结构,将异常信息反馈给用户,并记录日志以便后续分析。

串口数据粘包或格式不正确

在数据连续传输的场景中,串口可能会因为数据粘包的问题导致解析错误。为解决这一问题,建议在数据传输协议中明确数据边界,如采用特定的分隔符,或者在数据头部增加包长度信息,然后在接收时进行数据拆包解析。

总结

通过对 serialport 类的详细解析,本文展示了如何在 winform 环境下正确设置串口参数、打开关闭串口以及处理常见的异常情况。合理的异常捕获、线程安全机制以及 ui 数据更新策略,不仅提高了应用的稳定性,也为编写高质量串口通信程序提供了有效的技术支持。

在工业自动化、嵌入式设备通信等领域,串口通信依然是不可替代的技术手段。随着国产软硬件生态的不断完善,开发在串口通信方面的实践经验也日益丰富。面对未来不断变化的硬件通信需求,开发应继续关注异常自愈和智能数据解析技术的进步,为行业应用提供更全面、可靠的解决方案。

以上就是c# serialport类实现串口通信的实战指南的详细内容,更多关于c# serialport串口通信的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com