开源 C# 快速开发(七)通讯--串口

文章的目的为了记录使用C# 开发学习的经历。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。

相关链接:

开源 C# 快速开发(一)基础知识

开源 C# 快速开发(二)基础控件

开源 C# 快速开发(三)复杂控件

开源 C# 快速开发(四)自定义控件--波形图

开源 C# 快速开发(五)自定义控件--仪表盘

开源 C# 快速开发(六)自定义控件--圆环

开源 C# 快速开发(七)通讯--串口

推荐链接:

开源 C# .net mvc 开发(一)WEB搭建_c#部署web程序-CSDN博客

开源 C# .net mvc 开发(二)网站快速搭建_c#网站开发-CSDN博客

开源 C# .net mvc 开发(三)WEB内外网访问-CSDN博客

开源 C# .net mvc 开发(四)工程结构、页面提交以及显示-CSDN博客

开源 C# .net mvc 开发(五)常用代码快速开发_c# mvc开发-CSDN博客

开源 C# .net mvc 开发(六)发送邮件、定时以及CMD编程-CSDN博客

开源 C# .net mvc 开发(七)动态图片、动态表格和json数据生成-CSDN博客

开源 C# .net mvc 开发(八)IIS Express轻量化Web服务器的配置和使用-CSDN博客

开源 C# .net mvc 开发(九)websocket--服务器与客户端的实时通信-CSDN博客

本章节主要内容是:用C#编写的串口通信工具程序,基于Windows Forms和串口控件实现。

目录:

1.源码分析

2.所有源码

3.效果演示

一、源码分析

  1. 构造函数 Form1()

功能:程序启动时的初始化工作,建立自动刷新串口列表的机制。

复制代码
public Form1()
{
    InitializeComponent();          // 初始化窗体控件
    InitializeSerialPort();        // 初始化串口事件处理
    RefreshPortList();             // 刷新可用串口列表
    
    // 创建定时器,每秒刷新一次串口列表
    System.Windows.Forms.Timer timerRefreshPorts = new System.Windows.Forms.Timer();
    timerRefreshPorts.Interval = 1000;  // 设置间隔为1000毫秒
    timerRefreshPorts.Tick += (s, e) => RefreshPortList();  // Lambda表达式处理Tick事件
    timerRefreshPorts.Start();     // 启动定时器
}
  1. 串口初始化 InitializeSerialPort()

功能:绑定串口的数据接收和错误接收事件处理方法。

复制代码
private void InitializeSerialPort()
{
    // 注册数据接收事件处理程序
    serialPort.DataReceived += new SerialDataReceivedEventHandler(SerialPort_DataReceived);
    // 注册错误接收事件处理程序
    serialPort.ErrorReceived += new SerialErrorReceivedEventHandler(SerialPort_ErrorReceived);
}
  1. 刷新串口列表 RefreshPortList()

功能:动态刷新可用串口列表,保持用户选择状态。

复制代码
private void RefreshPortList()
{
    // 保存当前选择的串口
    string currentSelection = comboBoxPort.SelectedItem?.ToString();

    // 清空并重新填充串口列表
    comboBoxPort.Items.Clear();
    string[] ports = SerialPort.GetPortNames();  // 获取系统可用串口
    comboBoxPort.Items.AddRange(ports);          // 添加到下拉框

    // 恢复之前的选择(如果仍然存在)
    if (!string.IsNullOrEmpty(currentSelection) && comboBoxPort.Items.Contains(currentSelection))
    {
        comboBoxPort.SelectedItem = currentSelection;
    }
    // 否则选择第一个可用串口
    else if (comboBoxPort.Items.Count > 0)
    {
        comboBoxPort.SelectedIndex = 0;
    }
}
  1. 打开/关闭串口按钮 buttonOpenClose_Click()

功能:切换串口的打开/关闭状态。

复制代码
private void buttonOpenClose_Click(object sender, EventArgs e)
{
    // 根据串口当前状态决定操作
    if (serialPort.IsOpen)
    {
        CloseSerialPort();    // 如果已打开,则关闭
    }
    else
    {
        OpenSerialPort();     // 如果已关闭,则打开
    }
}
  1. 打开串口 OpenSerialPort()

功能:根据用户选择的参数配置并打开串口。

复制代码
private void OpenSerialPort()
{
    // 检查是否选择了串口
    if (comboBoxPort.SelectedItem == null)
    {
        MessageBox.Show("请选择串口!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
    }

    try
    {
        // 配置串口参数
        serialPort.PortName = comboBoxPort.SelectedItem.ToString();
        serialPort.BaudRate = int.Parse(comboBoxBaudRate.SelectedItem.ToString());
        serialPort.DataBits = int.Parse(comboBoxDataBits.SelectedItem.ToString());

        // 配置校验位(5种模式)
        switch (comboBoxParity.SelectedItem.ToString())
        {
            case "None": serialPort.Parity = Parity.None; break;
            case "Odd": serialPort.Parity = Parity.Odd; break;
            case "Even": serialPort.Parity = Parity.Even; break;
            case "Mark": serialPort.Parity = Parity.Mark; break;
            case "Space": serialPort.Parity = Parity.Space; break;
        }

        // 配置停止位(3种模式)
        switch (comboBoxStopBits.SelectedItem.ToString())
        {
            case "1": serialPort.StopBits = StopBits.One; break;
            case "1.5": serialPort.StopBits = StopBits.OnePointFive; break;
            case "2": serialPort.StopBits = StopBits.Two; break;
        }

        serialPort.Open();  // 打开串口
        
        // 更新UI状态
        buttonOpenClose.Text = "关闭串口";
        comboBoxPort.Enabled = false;
        comboBoxBaudRate.Enabled = false;
        comboBoxDataBits.Enabled = false;
        comboBoxParity.Enabled = false;
        comboBoxStopBits.Enabled = false;
    }
    catch (Exception ex)
    {
        MessageBox.Show($"打开串口失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}
  1. 关闭串口 CloseSerialPort()

功能:安全关闭串口并恢复界面控件的可用状态。

复制代码
private void CloseSerialPort()
{
    try
    {
        // 安全关闭串口
        if (serialPort.IsOpen)
        {
            serialPort.Close();
        }
        
        // 恢复UI状态
        buttonOpenClose.Text = "打开串口";
        comboBoxPort.Enabled = true;
        comboBoxBaudRate.Enabled = true;
        comboBoxDataBits.Enabled = true;
        comboBoxParity.Enabled = true;
        comboBoxStopBits.Enabled = true;

        // 停止自动发送功能
        checkBoxAutoSend.Checked = false;
    }
    catch (Exception ex)
    {
        MessageBox.Show($"关闭串口失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}
  1. 数据接收处理 SerialPort_DataReceived()

功能:在后台线程中接收串口数据,通过线程安全的方式更新UI。

复制代码
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    try
    {
        // 读取可用数据
        int bytesToRead = serialPort.BytesToRead;
        byte[] buffer = new byte[bytesToRead];
        serialPort.Read(buffer, 0, bytesToRead);

        // 通过Invoke在UI线程中更新显示
        this.Invoke(new Action(() => DisplayReceivedData(buffer)));
    }
    catch (Exception ex)
    {
        // 错误信息也要在UI线程中显示
        this.Invoke(new Action(() =>
            MessageBox.Show($"接收数据错误:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error)));
    }
}
  1. 数据显示 DisplayReceivedData()

功能:将接收到的字节数据按指定格式显示在文本框中。

复制代码
private void DisplayReceivedData(byte[] data)
{
    // 根据复选框选择显示格式
    if (checkBoxHexReceive.Checked)
    {
        // 十六进制显示:将字节数组转换为"XX XX XX"格式
        string hexString = BitConverter.ToString(data).Replace("-", " ");
        textBoxReceive.AppendText(hexString + " ");
    }
    else
    {
        // ASCII显示:将字节数组转换为字符串
        string asciiString = Encoding.ASCII.GetString(data);
        textBoxReceive.AppendText(asciiString);
    }

    // 自动滚动到最新内容
    textBoxReceive.SelectionStart = textBoxReceive.Text.Length;
    textBoxReceive.ScrollToCaret();
}
  1. 错误接收处理 SerialPort_ErrorReceived()

功能:处理串口通信错误,在接收框中显示错误信息。

复制代码
private void SerialPort_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
{
    // 在UI线程中显示错误信息
    this.Invoke(new Action(() =>
        textBoxReceive.AppendText($"[错误:{e.EventType}]\r\n")));
}
  1. 数据发送 SendData()

功能:处理数据发送逻辑,支持两种格式。

复制代码
private void SendData()
{
    // 前置检查
    if (!serialPort.IsOpen)
    {
        MessageBox.Show("请先打开串口!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
        return;
    }

    if (string.IsNullOrEmpty(textBoxSend.Text))
    {
        MessageBox.Show("发送内容不能为空!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
        return;
    }

    try
    {
        byte[] sendData;

        // 根据复选框选择发送格式
        if (checkBoxHexSend.Checked)
        {
            // 十六进制发送
            sendData = HexStringToByteArray(textBoxSend.Text);
        }
        else
        {
            // ASCII发送
            sendData = Encoding.ASCII.GetBytes(textBoxSend.Text);
        }

        serialPort.Write(sendData, 0, sendData.Length);  // 发送数据
    }
    catch (Exception ex)
    {
        MessageBox.Show($"发送数据失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}
  1. 十六进制转换 HexStringToByteArray()

功能:将十六进制字符串转换为字节数组,支持灵活的输入格式。

复制代码
private byte[] HexStringToByteArray(string hex)
{
    // 清理字符串:移除所有空格、横线、换行符等
    hex = hex.Replace(" ", "").Replace("-", "").Replace("\r", "").Replace("\n", "").Replace("\t", "");

    // 验证长度
    if (hex.Length % 2 != 0)
    {
        throw new ArgumentException("十六进制字符串长度必须为偶数");
    }

    // 每两个字符转换成一个字节
    byte[] bytes = new byte[hex.Length / 2];
    for (int i = 0; i < hex.Length; i += 2)
    {
        string hexByte = hex.Substring(i, 2);           // 提取两个字符
        bytes[i / 2] = Convert.ToByte(hexByte, 16);    // 转换为字节
    }
    return bytes;
}
  1. 自动发送控制 checkBoxAutoSend_CheckedChanged()

功能:管理自动发送功能的启动和停止。

复制代码
private void checkBoxAutoSend_CheckedChanged(object sender, EventArgs e)
{
    if (checkBoxAutoSend.Checked)
    {
        // 启用自动发送前的检查
        if (!serialPort.IsOpen)        {
            MessageBox.Show("请先打开串口!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            checkBoxAutoSend.Checked = false;
            return;
        }

        // 配置并启动定时器
        timerAutoSend.Interval = (int)numericUpDownInterval.Value;
        timerAutoSend.Tick += (s, args) => SendData();  // 每次Tick事件触发发送
        timerAutoSend.Start();
    }
    else
    {
        // 停止自动发送
        timerAutoSend.Stop();
    }

    // 控制间隔设置控件的可用状态
    numericUpDownInterval.Enabled = !checkBoxAutoSend.Checked;
}
  1. 窗体关闭处理 OnFormClosing()

功能:重写窗体关闭事件,确保资源正确释放。

复制代码
protected override void OnFormClosing(FormClosingEventArgs e)
{
    // 确保程序退出前关闭串口
    if (serialPort.IsOpen)
    {
        serialPort.Close();
    }
    base.OnFormClosing(e);  // 调用基类方法
}

二、所有源码

Form1.designer.cs文件

复制代码
using System;
using System.Drawing;
using System.IO.Ports;
using System.Windows.Forms;

namespace _7_uart
{
    partial class Form1
    {
        /*
        /// <summary>
        /// 必需的设计器变量。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// 清理所有正在使用的资源。
        /// </summary>
        /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows 窗体设计器生成的代码

        /// <summary>
        /// 设计器支持所需的方法 - 不要修改
        /// 使用代码编辑器修改此方法的内容。
        /// </summary>
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(800, 450);
            this.Text = "Form1";
        }

        #endregion
        */
        private System.ComponentModel.IContainer components = null;
        private ComboBox comboBoxPort;
        private ComboBox comboBoxBaudRate;
        private ComboBox comboBoxDataBits;
        private ComboBox comboBoxParity;
        private ComboBox comboBoxStopBits;
        private Button buttonOpenClose;
        private TextBox textBoxSend;
        private TextBox textBoxReceive;
        private Button buttonSend;
        private CheckBox checkBoxHexSend;
        private CheckBox checkBoxHexReceive;
        private CheckBox checkBoxAutoSend;
        private NumericUpDown numericUpDownInterval;
        private Label label1, label2, label3, label4, label5, label6, label7;
        private SerialPort serialPort;
        private System.Windows.Forms.Timer timerAutoSend;

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
                if (serialPort != null && serialPort.IsOpen)
                    serialPort.Close();
            }
            base.Dispose(disposing);
        }

        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.comboBoxPort = new System.Windows.Forms.ComboBox();
            this.comboBoxBaudRate = new System.Windows.Forms.ComboBox();
            this.comboBoxDataBits = new System.Windows.Forms.ComboBox();
            this.comboBoxParity = new System.Windows.Forms.ComboBox();
            this.comboBoxStopBits = new System.Windows.Forms.ComboBox();
            this.buttonOpenClose = new System.Windows.Forms.Button();
            this.textBoxSend = new System.Windows.Forms.TextBox();
            this.textBoxReceive = new System.Windows.Forms.TextBox();
            this.buttonSend = new System.Windows.Forms.Button();
            this.checkBoxHexSend = new System.Windows.Forms.CheckBox();
            this.checkBoxHexReceive = new System.Windows.Forms.CheckBox();
            this.checkBoxAutoSend = new System.Windows.Forms.CheckBox();
            this.numericUpDownInterval = new System.Windows.Forms.NumericUpDown();
            this.label1 = new System.Windows.Forms.Label();
            this.label2 = new System.Windows.Forms.Label();
            this.label3 = new System.Windows.Forms.Label();
            this.label4 = new System.Windows.Forms.Label();
            this.label5 = new System.Windows.Forms.Label();
            this.label6 = new System.Windows.Forms.Label();
            this.label7 = new System.Windows.Forms.Label();
            this.serialPort = new System.IO.Ports.SerialPort(this.components);
            this.timerAutoSend = new System.Windows.Forms.Timer(this.components);
            ((System.ComponentModel.ISupportInitialize)(this.numericUpDownInterval)).BeginInit();
            this.SuspendLayout();
            // 
            // comboBoxPort
            // 
            this.comboBoxPort.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.comboBoxPort.Location = new System.Drawing.Point(80, 20);
            this.comboBoxPort.Name = "comboBoxPort";
            this.comboBoxPort.Size = new System.Drawing.Size(100, 23);
            this.comboBoxPort.TabIndex = 0;
            // 
            // comboBoxBaudRate
            // 
            this.comboBoxBaudRate.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.comboBoxBaudRate.Items.AddRange(new object[] {
            "9600",
            "19200",
            "38400",
            "57600",
            "115200"});
            this.comboBoxBaudRate.Location = new System.Drawing.Point(80, 60);
            this.comboBoxBaudRate.Name = "comboBoxBaudRate";
            this.comboBoxBaudRate.Size = new System.Drawing.Size(100, 23);
            this.comboBoxBaudRate.TabIndex = 1;
            // 
            // comboBoxDataBits
            // 
            this.comboBoxDataBits.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.comboBoxDataBits.Items.AddRange(new object[] {
            "5",
            "6",
            "7",
            "8"});
            this.comboBoxDataBits.Location = new System.Drawing.Point(280, 20);
            this.comboBoxDataBits.Name = "comboBoxDataBits";
            this.comboBoxDataBits.Size = new System.Drawing.Size(100, 23);
            this.comboBoxDataBits.TabIndex = 2;
            // 
            // comboBoxParity
            // 
            this.comboBoxParity.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.comboBoxParity.Items.AddRange(new object[] {
            "None",
            "Odd",
            "Even",
            "Mark",
            "Space"});
            this.comboBoxParity.Location = new System.Drawing.Point(280, 60);
            this.comboBoxParity.Name = "comboBoxParity";
            this.comboBoxParity.Size = new System.Drawing.Size(100, 23);
            this.comboBoxParity.TabIndex = 3;
            // 
            // comboBoxStopBits
            // 
            this.comboBoxStopBits.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.comboBoxStopBits.Items.AddRange(new object[] {
            "1",
            "1.5",
            "2"});
            this.comboBoxStopBits.Location = new System.Drawing.Point(480, 20);
            this.comboBoxStopBits.Name = "comboBoxStopBits";
            this.comboBoxStopBits.Size = new System.Drawing.Size(100, 23);
            this.comboBoxStopBits.TabIndex = 4;
            // 
            // buttonOpenClose
            // 
            this.buttonOpenClose.Location = new System.Drawing.Point(480, 60);
            this.buttonOpenClose.Name = "buttonOpenClose";
            this.buttonOpenClose.Size = new System.Drawing.Size(100, 30);
            this.buttonOpenClose.TabIndex = 5;
            this.buttonOpenClose.Text = "打开串口";
            this.buttonOpenClose.Click += new System.EventHandler(this.buttonOpenClose_Click);
            // 
            // textBoxSend
            // 
            this.textBoxSend.Location = new System.Drawing.Point(20, 150);
            this.textBoxSend.Multiline = true;
            this.textBoxSend.Name = "textBoxSend";
            this.textBoxSend.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
            this.textBoxSend.Size = new System.Drawing.Size(560, 100);
            this.textBoxSend.TabIndex = 6;
            // 
            // textBoxReceive
            // 
            this.textBoxReceive.Location = new System.Drawing.Point(20, 313);
            this.textBoxReceive.Multiline = true;
            this.textBoxReceive.Name = "textBoxReceive";
            this.textBoxReceive.ReadOnly = true;
            this.textBoxReceive.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
            this.textBoxReceive.Size = new System.Drawing.Size(560, 187);
            this.textBoxReceive.TabIndex = 7;
            // 
            // buttonSend
            // 
            this.buttonSend.Location = new System.Drawing.Point(480, 260);
            this.buttonSend.Name = "buttonSend";
            this.buttonSend.Size = new System.Drawing.Size(100, 30);
            this.buttonSend.TabIndex = 8;
            this.buttonSend.Text = "发送";
            this.buttonSend.Click += new System.EventHandler(this.buttonSend_Click);
            // 
            // checkBoxHexSend
            // 
            this.checkBoxHexSend.Location = new System.Drawing.Point(20, 260);
            this.checkBoxHexSend.Name = "checkBoxHexSend";
            this.checkBoxHexSend.Size = new System.Drawing.Size(100, 20);
            this.checkBoxHexSend.TabIndex = 9;
            this.checkBoxHexSend.Text = "十六进制发送";
            // 
            // checkBoxHexReceive
            // 
            this.checkBoxHexReceive.Location = new System.Drawing.Point(120, 260);
            this.checkBoxHexReceive.Name = "checkBoxHexReceive";
            this.checkBoxHexReceive.Size = new System.Drawing.Size(100, 20);
            this.checkBoxHexReceive.TabIndex = 10;
            this.checkBoxHexReceive.Text = "十六进制接收";
            // 
            // checkBoxAutoSend
            // 
            this.checkBoxAutoSend.Location = new System.Drawing.Point(220, 260);
            this.checkBoxAutoSend.Name = "checkBoxAutoSend";
            this.checkBoxAutoSend.Size = new System.Drawing.Size(80, 20);
            this.checkBoxAutoSend.TabIndex = 11;
            this.checkBoxAutoSend.Text = "自动发送";
            this.checkBoxAutoSend.CheckedChanged += new System.EventHandler(this.checkBoxAutoSend_CheckedChanged);
            // 
            // numericUpDownInterval
            // 
            this.numericUpDownInterval.Location = new System.Drawing.Point(310, 260);
            this.numericUpDownInterval.Maximum = new decimal(new int[] {
            10000,
            0,
            0,
            0});
            this.numericUpDownInterval.Minimum = new decimal(new int[] {
            100,
            0,
            0,
            0});
            this.numericUpDownInterval.Name = "numericUpDownInterval";
            this.numericUpDownInterval.Size = new System.Drawing.Size(60, 25);
            this.numericUpDownInterval.TabIndex = 12;
            this.numericUpDownInterval.Value = new decimal(new int[] {
            1000,
            0,
            0,
            0});
            // 
            // label1
            // 
            this.label1.Location = new System.Drawing.Point(20, 20);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(50, 20);
            this.label1.TabIndex = 13;
            this.label1.Text = "串口:";
            // 
            // label2
            // 
            this.label2.Location = new System.Drawing.Point(20, 60);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(50, 20);
            this.label2.TabIndex = 14;
            this.label2.Text = "波特率:";
            // 
            // label3
            // 
            this.label3.Location = new System.Drawing.Point(220, 20);
            this.label3.Name = "label3";
            this.label3.Size = new System.Drawing.Size(50, 20);
            this.label3.TabIndex = 15;
            this.label3.Text = "数据位:";
            // 
            // label4
            // 
            this.label4.Location = new System.Drawing.Point(220, 60);
            this.label4.Name = "label4";
            this.label4.Size = new System.Drawing.Size(50, 20);
            this.label4.TabIndex = 16;
            this.label4.Text = "校验位:";
            // 
            // label5
            // 
            this.label5.Location = new System.Drawing.Point(420, 20);
            this.label5.Name = "label5";
            this.label5.Size = new System.Drawing.Size(50, 20);
            this.label5.TabIndex = 17;
            this.label5.Text = "停止位:";
            // 
            // label6
            // 
            this.label6.Location = new System.Drawing.Point(20, 120);
            this.label6.Name = "label6";
            this.label6.Size = new System.Drawing.Size(50, 20);
            this.label6.TabIndex = 18;
            this.label6.Text = "发送:";
            // 
            // label7
            // 
            this.label7.Location = new System.Drawing.Point(20, 288);
            this.label7.Name = "label7";
            this.label7.Size = new System.Drawing.Size(50, 20);
            this.label7.TabIndex = 19;
            this.label7.Text = "接收:";
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(600, 520);
            this.Controls.Add(this.comboBoxPort);
            this.Controls.Add(this.comboBoxBaudRate);
            this.Controls.Add(this.comboBoxDataBits);
            this.Controls.Add(this.comboBoxParity);
            this.Controls.Add(this.comboBoxStopBits);
            this.Controls.Add(this.buttonOpenClose);
            this.Controls.Add(this.textBoxSend);
            this.Controls.Add(this.textBoxReceive);
            this.Controls.Add(this.buttonSend);
            this.Controls.Add(this.checkBoxHexSend);
            this.Controls.Add(this.checkBoxHexReceive);
            this.Controls.Add(this.checkBoxAutoSend);
            this.Controls.Add(this.numericUpDownInterval);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.label3);
            this.Controls.Add(this.label4);
            this.Controls.Add(this.label5);
            this.Controls.Add(this.label6);
            this.Controls.Add(this.label7);
            this.Name = "Form1";
            this.Text = "串口调试工具";
            ((System.ComponentModel.ISupportInitialize)(this.numericUpDownInterval)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();

        }
    }
}

From1.cs文件

复制代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System;
using System.IO.Ports;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace _7_uart
{
    public partial class Form1 : Form
    {
        /*
        public Form1()
        {
            InitializeComponent();
        }
        */
        public Form1()
        {
            InitializeComponent();
            InitializeSerialPort();
            RefreshPortList();

            // 定时刷新可用串口
            System.Windows.Forms.Timer timerRefreshPorts = new System.Windows.Forms.Timer();
            timerRefreshPorts.Interval = 1000;
            timerRefreshPorts.Tick += (s, e) => RefreshPortList();
            timerRefreshPorts.Start();
        }

        private void InitializeSerialPort()
        {
            serialPort.DataReceived += new SerialDataReceivedEventHandler(SerialPort_DataReceived);
            serialPort.ErrorReceived += new SerialErrorReceivedEventHandler(SerialPort_ErrorReceived);
        }

        private void RefreshPortList()
        {
            string currentSelection = comboBoxPort.SelectedItem?.ToString();

            comboBoxPort.Items.Clear();
            string[] ports = SerialPort.GetPortNames();
            comboBoxPort.Items.AddRange(ports);

            if (!string.IsNullOrEmpty(currentSelection) && comboBoxPort.Items.Contains(currentSelection))
            {
                comboBoxPort.SelectedItem = currentSelection;
            }
            else if (comboBoxPort.Items.Count > 0)
            {
                comboBoxPort.SelectedIndex = 0;
            }
        }

        private void buttonOpenClose_Click(object sender, EventArgs e)
        {
            if (serialPort.IsOpen)
            {
                CloseSerialPort();
            }
            else
            {
                OpenSerialPort();
            }
        }

        private void OpenSerialPort()
        {
            if (comboBoxPort.SelectedItem == null)
            {
                MessageBox.Show("请选择串口!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            try
            {
                serialPort.PortName = comboBoxPort.SelectedItem.ToString();
                serialPort.BaudRate = int.Parse(comboBoxBaudRate.SelectedItem.ToString());
                serialPort.DataBits = int.Parse(comboBoxDataBits.SelectedItem.ToString());

                // 设置校验位
                switch (comboBoxParity.SelectedItem.ToString())
                {
                    case "None": serialPort.Parity = Parity.None; break;
                    case "Odd": serialPort.Parity = Parity.Odd; break;
                    case "Even": serialPort.Parity = Parity.Even; break;
                    case "Mark": serialPort.Parity = Parity.Mark; break;
                    case "Space": serialPort.Parity = Parity.Space; break;
                }

                // 设置停止位
                switch (comboBoxStopBits.SelectedItem.ToString())
                {
                    case "1": serialPort.StopBits = StopBits.One; break;
                    case "1.5": serialPort.StopBits = StopBits.OnePointFive; break;
                    case "2": serialPort.StopBits = StopBits.Two; break;
                }

                serialPort.Open();
                buttonOpenClose.Text = "关闭串口";
                comboBoxPort.Enabled = false;
                comboBoxBaudRate.Enabled = false;
                comboBoxDataBits.Enabled = false;
                comboBoxParity.Enabled = false;
                comboBoxStopBits.Enabled = false;
            }
            catch (Exception ex)
            {
                MessageBox.Show($"打开串口失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void CloseSerialPort()
        {
            try
            {
                if (serialPort.IsOpen)
                {
                    serialPort.Close();
                }
                buttonOpenClose.Text = "打开串口";
                comboBoxPort.Enabled = true;
                comboBoxBaudRate.Enabled = true;
                comboBoxDataBits.Enabled = true;
                comboBoxParity.Enabled = true;
                comboBoxStopBits.Enabled = true;

                // 停止自动发送
                checkBoxAutoSend.Checked = false;
            }
            catch (Exception ex)
            {
                MessageBox.Show($"关闭串口失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {
                int bytesToRead = serialPort.BytesToRead;
                byte[] buffer = new byte[bytesToRead];
                serialPort.Read(buffer, 0, bytesToRead);

                // 在UI线程中更新接收文本框
                this.Invoke(new Action(() => DisplayReceivedData(buffer)));
            }
            catch (Exception ex)
            {
                this.Invoke(new Action(() =>
                    MessageBox.Show($"接收数据错误:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error)));
            }
        }

        private void DisplayReceivedData(byte[] data)
        {
            if (checkBoxHexReceive.Checked)
            {
                // 十六进制显示
                string hexString = BitConverter.ToString(data).Replace("-", " ");
                textBoxReceive.AppendText(hexString + " ");
            }
            else
            {
                // ASCII显示
                string asciiString = Encoding.ASCII.GetString(data);
                textBoxReceive.AppendText(asciiString);
            }

            // 自动滚动到最新内容
            textBoxReceive.SelectionStart = textBoxReceive.Text.Length;
            textBoxReceive.ScrollToCaret();
        }

        private void SerialPort_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
        {
            this.Invoke(new Action(() =>
                textBoxReceive.AppendText($"[错误:{e.EventType}]\r\n")));
        }

        private void buttonSend_Click(object sender, EventArgs e)
        {
            SendData();
        }

        private void SendData()
        {
            if (!serialPort.IsOpen)
            {
                MessageBox.Show("请先打开串口!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            if (string.IsNullOrEmpty(textBoxSend.Text))
            {
                MessageBox.Show("发送内容不能为空!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            try
            {
                byte[] sendData;

                if (checkBoxHexSend.Checked)
                {
                    // 十六进制发送
                    sendData = HexStringToByteArray(textBoxSend.Text);
                }
                else
                {
                    // ASCII发送
                    sendData = Encoding.ASCII.GetBytes(textBoxSend.Text);
                }

                serialPort.Write(sendData, 0, sendData.Length);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"发送数据失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private byte[] HexStringToByteArray(string hex)
        {
            hex = hex.Replace(" ", "").Replace("-", "").Replace("\r", "").Replace("\n", "").Replace("\t", "");

            if (hex.Length % 2 != 0)
            {
                throw new ArgumentException("十六进制字符串长度必须为偶数");
            }

            byte[] bytes = new byte[hex.Length / 2];
            for (int i = 0; i < hex.Length; i += 2)
            {
                string hexByte = hex.Substring(i, 2);
                bytes[i / 2] = Convert.ToByte(hexByte, 16);
            }
            return bytes;
        }

        private void checkBoxAutoSend_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBoxAutoSend.Checked)
            {
                if (!serialPort.IsOpen)
                {
                    MessageBox.Show("请先打开串口!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    checkBoxAutoSend.Checked = false;
                    return;
                }

                timerAutoSend.Interval = (int)numericUpDownInterval.Value;
                timerAutoSend.Tick += (s, args) => SendData();
                timerAutoSend.Start();
            }
            else
            {
                timerAutoSend.Stop();
            }

            numericUpDownInterval.Enabled = !checkBoxAutoSend.Checked;
        }

        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            if (serialPort.IsOpen)
            {
                serialPort.Close();
            }
            base.OnFormClosing(e);
        }
    }
}

三、效果演示

相关推荐
HelloGitHub3 小时前
《HelloGitHub》第 114 期
开源·github
伞啊伞4 小时前
开源的容器化平台:Docker
docker·容器·开源
爱吃小胖橘4 小时前
Unity-动画IK控制
3d·unity·c#·游戏引擎
徐小夕@趣谈前端12 小时前
如何实现多人协同文档编辑器
javascript·vue.js·设计模式·前端框架·开源·编辑器·github
胡耀超12 小时前
PaddleLabel百度飞桨Al Studio图像标注平台安装和使用指南(包冲突 using the ‘flask‘ extra、眼底医疗分割数据集演示)
人工智能·百度·开源·paddlepaddle·图像识别·图像标注·paddlelabel
时光追逐者12 小时前
一个基于 .NET 开源、简易、轻量级的进销存管理系统
开源·c#·.net·.net core·经销存管理系统
SuperHeroWu713 小时前
【鸿蒙开源技术共建】用@luvi/lv-markdown-in在HarmonyOS上打造高性能Markdown编辑体验
华为·开源·harmonyos
William_cl14 小时前
【连载7】 C# MVC 跨框架异常处理对比:.NET Framework 与 .NET Core 实现差异
c#·mvc·.net
ajassi200014 小时前
开源 C# 快速开发(五)自定义控件--仪表盘
开发语言·开源·c#