2023年最后一天,来一波C#中的串口通讯SerialPort基本操作

开篇碎碎念

今天要要写的是 C# 中可以用来实现串口通信的 SerialPort 类。这个 demo 在很久之前就写了,当时是需要调试串口间发送和接收的消息,后面用到了 sscom 工具,心血来潮模仿这个工具写了一个pdd版的串口调试工具。

串口通信在各领域都有广泛的应用,下面我将使用 System.IO.Ports 命名空间提供的 SerialPort 类来简单演示如何在 C# 中使用串口通信。

基本认识

在使用 SerialPort 类进行串口通信时,可以配置的属性有:

  • PortName: 串口名称,如"COM1"
  • BaudRate:波特率,表示每秒发送的位数,常见的波特率有9600、115200等
  • Parity:奇偶校验位。可以选择 None(无校验)、Even(偶校验)和Odd(奇校验)
  • DataBits:数据位的位数,常见值为8
  • StopBits:停止位的位数,常见值由None(无停止位)、One(1位停止位)、Two(2位停止位)
  • readTimeout:读取操作的超时时间
  • writeTimeout:写入操作的超时时间

常用方法:

  • public static string[] GetPortNames()

    获取当前计算机上可用的串口名称数组

  • public void Open()

    打开串口以建立连接

  • public int Read(byte[] buffer, int offset, int count)

    从串口读取指定数量的字节数据,并存储到缓冲区中

  • public int ReadChar()

    从串口读取一个字符

  • BytesToRead()

    获取当前串口接收缓冲区中可用的字节数

  • public int Read(char[] buffer, int offset, int count)

    从串口读取指定数量的字符,并存储到指定的缓冲区中

  • BytesToWrite()

    获取当前串口发送缓冲区中待发送的bytes

  • public void Write(string text)

    向串口写入字符串

  • public void Write(char[] buffer, int offset, int count)

    向串口写入指定数量的字符数据

  • public void WriteLine(string text)

    向串口写入字符串并在末尾添加换行符

  • public void DiscardInBuffer()

    清空串口接收缓冲区中的数据

  • public void DiscardOutBuffer()

    清空串口发送缓冲区中的数据

  • public void Close()

    关闭串口连接

正餐开始

UI界面

新建窗体应用,创建 SerialPort 控件,并在工具箱中拉出button等相关控件,创建如下 WinForm 应用界面

串口扫描

先写一个活动串口扫描,实现自动扫描COM口,并设置 comboBox 初始值。由于本机上只有一个串口COM3,这里我使用虚拟串口工具Virtual Serial Port Driver 创建了4个串口。

ini 复制代码
private void getAvailablePort()
{
    try
    {
        string[] serialPorts = SerialPort.GetPortNames();
        if(serialPorts.Length == 0)
        {
            throw (new System.IO.IOException("this device doesn't have any port!"));
        }
        if(serialPorts != null && serialPorts.Length != 0)
        {
            Array.Sort<String>(serialPorts);
            foreach(String port in serialPorts)
            {
                if(port != null && port.Length != 0)
                {
                    comboBox1.Items.Add(port);
                }
                Console.WriteLine(port);
            }
            comboBox1.SelectedIndex = 0;
        }
    }
    catch(System.IO.IOException e)
    {
        MessageBox.Show(e.Message);
    }
}
​
private void portInit()
{
    getAvailablePort();
    //设置初始显示的值
    comboBox2.SelectedIndex = 6;
    comboBox3.SelectedIndex = 2;
    comboBox4.SelectedIndex = 0;
    comboBox5.SelectedIndex = 0;
}

打开串口

扫描到COM口后,选择一个打开,下面选择COM1,并写好波特率、数据位等对应的配置,进行配置,最后调用Open()方法打开串口

数据发送和接收

接下来写发送按钮click事件处理程序,获取到发送框输入的数据并存储在str中,再使用String.Split方法将输入字符串按空格拆分为十六进制数,并将所有的十六进制数对存储在一个List中。后面在转换过程中,使用了byte.Parse方法,用来接收一个字符串和一个指定的十六进制格式的参数来解析十六进制数对。

接收框获取数据这里,由于想减少UI刷新,便先将接收到的数据先存储在一个缓冲区中,并在合适时机使用BeginInvoke方法将UI刷新放到UI线程执行。

csharp 复制代码
private List<string> GetHexPairs(string str)
{
    List<string> hexPairs = new List<string>();
    string[] strArray = str.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
    foreach(string hexPair in strArray)
    {
        hexPairs.Add(hexPair);
    }
    return hexPairs;
}
private byte ConvertHexPairToByte(string hexPair)
{
    return Convert.ToByte(hexPair, 16);
}
private void button1_Click(object sender, EventArgs e)
{
    string str = textBox7.Text.Trim();
    if (serialPort1.IsOpen)
    {
        try
        {
            foreach(var hexPair in GetHexPairs(str))
            {
                byte data = ConvertHexPairToByte(hexPair);
                serialPort1.Write(new byte[] { data }, 0, 1);
            }
        }
        catch (FormatException)
        {
            MessageBox.Show("输入的16进制数数据格式错误!");
        }
        catch (Exception ex)
        {
            MessageBox.Show($"串口发送失败,系统将关闭当前串口错误!\n {ex.Message}");
            serialPort1.Close();
        }
    }
}
​
private StringBuilder receivedDataBuffer = new StringBuilder();
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
    int bufferSize = serialPort1.BytesToRead;
    byte[] buffer = new byte[bufferSize];
    serialPort1.Read(buffer, 0, bufferSize);
​
    foreach(byte data in buffer)
    {
        string hex = data.ToString("X2"); 
        // 以空格分割并添加到缓冲区,避免频繁UI更新
        receivedDataBuffer.Append(hex).Append(' ');
    }
    // 将UI更新操作放入UI线程执行
    BeginInvoke(new Action(() =>
    {
        textBox6.AppendText(receivedDataBuffer.ToString());
        textBox6.AppendText(Environment.NewLine);
    }));
    serialPort1.DiscardInBuffer(); 
}

最终效果如下:

最后

本示例只是简单介绍了 C# 中 SerialPort 串口控件的扫描、打开、数据发送和接收。对这方面有兴趣的伙伴可以参考一下,对于文中出现的不足或可优化之处,欢迎大家评论吐槽!最后,祝大家新年快乐,工作顺心,同事有趣~

相关推荐
devlei7 小时前
从源码泄露看AI Agent未来:深度对比Claude Code原生实现与OpenClaw开源方案
android·前端·后端
努力的小郑8 小时前
Canal 不难,难的是用好:从接入到治理
后端·mysql·性能优化
Victor3569 小时前
MongoDB(87)如何使用GridFS?
后端
Victor3569 小时前
MongoDB(88)如何进行数据迁移?
后端
小红的布丁9 小时前
单线程 Redis 的高性能之道
redis·后端
GetcharZp9 小时前
Go 语言只能写后端?这款 2D 游戏引擎刷新你的认知!
后端
宁瑶琴11 小时前
COBOL语言的云计算
开发语言·后端·golang
普通网友11 小时前
阿里云国际版服务器,真的是学生党的性价比之选吗?
后端·python·阿里云·flask·云计算
IT_陈寒12 小时前
Vue的这个响应式问题,坑了我整整两小时
前端·人工智能·后端
Soofjan13 小时前
Go 内存回收-GC 源码1-触发与阶段
后端