使用 C# WinForm 制作简单的串口调试助手

使用 C# WinForm 制作简单的串口调试助手

很久之前就已经发现了C# WinForm开源的控件界面库Sunny.UI,于是想着做一个Demo来用上Sunny.UI界面库。于是就想着做一个串口调试助手的Demo。

下面我就创建一个工程,并且加载Sunny.UI控件库,我这个项目还加载了另一个库叫做SeeSharpTools.JY.GUI,但是没有使用其中的控件,添加它以备后面使用。

第一步:在VS2022编译器的NuGet管理工具中搜索Sunny.UI,然后下载下来。如下图笔者已经安装了Sunny.UI库:

安装Sunny.UI库后,在项目的工具箱中会由该库。如下图:

第二步:拖动控件布局界面。笔者的界面如上图所示。我们可以参考Sunny.UI库的说明文档修改控件的属性。Sunny.UI说明文档。以按钮为例,该说明书详细说明了每一个属性的作用,如下图:

另外Sunny.UI库控件的属性单独进行了分类,如下图中笔者的按钮控件属性:

界面布局完毕。

第三步:编写代码。

C# 为我们提供了串口类SerialPort,我们可以直接使用。

1.串口搜索按钮部分代码:

c# 复制代码
 private void m_btnSearch_Click(object sender, EventArgs e)
 {
     try
     {
         string[] items = SerialPort.GetPortNames();
         m_ctrlPort.Items.Clear();
         m_ctrlPort.Items.AddRange(items);
         m_ctrlPort.SelectedIndex = m_ctrlPort.Items.Count > 0 ? 0 : -1;

         if (items.Length > 0)
         {
             m_ctrlBaud.SelectedIndex = 10;
             m_ctrlData.SelectedIndex = 0;
             m_ctrlStop.SelectedIndex = 0;
             m_ctrlProof.SelectedIndex = 0;
         }
         else
         {
             MessageBox.Show("当前无串口连接!");
         }
     }
     catch (Exception ex)
     {
         MessageBox.Show("无串口设备!/r/n请检查是否连接设备!/r/n请检查设备驱动!");
     }
 }

说明:

①我们调用SerialPort类的GetPortNames()可以获取当前计算机的串行端口名的数组。

②在C#中的CombBox控件比C++更加简单,可以直接在属性中添加字符串。具体如下图中的波特率:

2.连接按钮部分的代码如下:

c# 复制代码
 private void m_btnConnect_Click(object sender, EventArgs e)
 {
     if (!m_serialPort.IsOpen)
     {
         if (m_ctrlPort.SelectedItem == null)
         {
             MessageBox.Show("请选择正确的串口", "提示");
             return;
         }
         // 设置串口参数
         m_serialPort.PortName = m_ctrlPort.Text.ToString();
         m_serialPort.BaudRate = Convert.ToInt32(m_ctrlBaud.SelectedItem.ToString());
         m_serialPort.DataBits = Convert.ToInt32(m_ctrlData.SelectedItem.ToString());

         // 设置停止位
         if (m_ctrlStop.Text == "One")
         {
             m_serialPort.StopBits = StopBits.One;
         }
         else if (m_ctrlStop.Text == "Two")
         {
             m_serialPort.StopBits = StopBits.Two;
         }
         else if (m_ctrlStop.Text == "OnePointTwo")
         {
             m_serialPort.StopBits = StopBits.OnePointFive;
         }
         else if (m_ctrlStop.Text == "None")
         {
             m_serialPort.StopBits = StopBits.None;
         }

         // 设置奇偶校验位
         if (m_ctrlProof.Text == "Odd")
         {
             m_serialPort.Parity = Parity.Odd;
         }
         else if (m_ctrlProof.Text == "Even")
         {
             m_serialPort.Parity = Parity.Even;
         }
         else if (m_ctrlProof.Text == "None")
         {
             m_serialPort.Parity = Parity.None;
         }

         try
         {
             m_ctrlPort.Enabled = false;
             m_ctrlBaud.Enabled = false;
             m_ctrlData.Enabled = false;
             m_ctrlStop.Enabled = false;
             m_ctrlProof.Enabled = false;
             m_btnSearch.Enabled = false;

             m_serialPort.Open();
             m_btnConnect.Text = "断开";
             m_Led.OnColor = Color.FromArgb(110, 190, 40);

         }
         catch (Exception ex)
         {
             MessageBox.Show("串口打开失败!");
         }
        
         m_serialPort.DataReceived += new SerialDataReceivedEventHandler(SerialDataReceive);
     }
     else if (m_btnConnect.Text == "断开")
     {
         m_ctrlPort.Enabled = true;
         m_ctrlBaud.Enabled = true;
         m_ctrlData.Enabled = true;
         m_ctrlStop.Enabled = true;
         m_ctrlProof.Enabled = true;
         m_btnSearch.Enabled = true;

         m_serialPort.DiscardInBuffer();
         m_serialPort.Close();
         m_btnConnect.Text = "连接";
         m_Led.OnColor = Color.Red;
     }
 }

说明:

①上述代码设置好串口参数后,调用SerialPort类的Open()函数即可连接串口。

DataReceivedSerialPort 类的 DataReceived 事件是在接收到数据时触发的。当串口接收缓冲区中有数据时,就会引发此事件。DataReceived 事件提供了一个 SerialDataReceivedEventArgs 对象,该对象包含有关事件的详细信息。请注意,DataReceived 事件处理程序应该尽可能快地执行,以避免阻塞其他事件的接收。

③定义DataReceived事件触发的串口接收函数和显示函数,代码如下:

c# 复制代码
#region(接收串口数据)
// 串口缓冲区
List<byte> pBuffer = new List<byte>(4096);
// 串口缓冲区最大字节数
int nBufferMaxSize = 4096;

private void SerialDataReceive(object sender, SerialDataReceivedEventArgs e)
{
    if (m_serialPort.IsOpen == false)
    {
        m_serialPort.Close();
        return;
    }

    // 读取缓存的数据长度
    int nByteLen = m_serialPort.BytesToRead;
    byte [] bRecv = new byte[nByteLen];
    // 将读取的数据存入缓存数组中
    m_serialPort.Read(bRecv,0,nByteLen);
    string strText = "接收的字节数:";
    statusStrip1.Items[1].Text = strText + nByteLen.ToString();

    pBuffer.Clear();
    // 将接收的数据存入缓冲区
    pBuffer.AddRange(bRecv);

    byte[] rBuffer = new byte[9192];
    pBuffer.CopyTo(0, rBuffer, 0, pBuffer.Count);
    Task.Run(() =>
    PrintfData(rBuffer, pBuffer.Count, 1));

}
#endregion

#region (打印数据)
void PrintfData(byte[] buffer,int nLength,int nTR)
{
    Int16 nLen;
    StringBuilder sb = new StringBuilder();
    if (nTR == 0)
        sb.Append("发送:");
    else
        sb.Append("接收:");

    string strRecv = "";
    if (uiRB_ASCII_Recv.Checked)
    {
        strRecv = sb.ToString();
        strRecv += Encoding.Default.GetString(buffer);
    }
    else if(uiRB_HEX_Recv.Checked)
    {
        // 转为字节显示
        for (nLen = 0; nLen < nLength; nLen++)
        {
            sb.Append(buffer[nLen].ToString());
            sb.Append(" ");
        }
        strRecv = sb.ToString();
    }
    MethodInvoker mi = new MethodInvoker(()=>
    { 
        if(m_tbReceive.Lines.Count() > 20)
            m_tbReceive.Clear();
        m_tbReceive.AppendText(strRecv + "\r\n");
       
    });
    BeginInvoke(mi);
}
#endregion

说明:

①上述代码使用了异步Task,其中调用Task.Run的函数,Task.Run是在线程池上执行任务。关于Run函数的详细说明可以参考官方文档。Run().

MethodInvoker类的说明:在C#中,MethodInvoker 是一个委托(delegate),它定义了与具有无返回值参数的方法匹配的签名。换句话说,MethodInvoker 是一个可以引用任何不接受参数且不返回值的方法的委托。MethodInvoker通常用于Windows Forms应用程序中,当你需要在非UI线程上更新UI控件时。由于UI控件只能由创建它们的线程(通常是主UI线程)进行操作,所以当你在后台线程中更新UI时,需要使用 MethodInvoker 来确保UI更新操作在正确的线程上执行。

③这两个类对于我来说,接触的也是比较少,只能先放在这里,以后用到的话再来详细查看说明。

3.发送按钮部分的代码如下:

c# 复制代码
 private void m_btnSend_Click(object sender, EventArgs e)
 {
     if (m_serialPort.IsOpen)
     {
         string strData = m_tbSend.Text;
         int nBufferLen = strData.Length;
         byte[] sendData = new Byte[nBufferLen];
         sendData = System.Text.Encoding.Default.GetBytes(strData);//转码
         try
         {
             // 写数据
             m_serialPort.Write(sendData, 0, sendData.Length);
             Task.Run(() => PrintfData(sendData, sendData.Length, 0));
             string strText = "发送的字节数:";
             statusStrip1.Items[0].Text = strText + sendData.Length.ToString();
         }
         catch
         {
             MessageBox.Show("发送失败!");
         }

     }
     else
     {
         MessageBox.Show("串口未打开!");
     }
 }

说明:

①使用SerialPort类的Write函数向串口缓冲区中写入数据。

第四步、代码调试结果。

软件运行后界面如下图,可以实现基本的数据收发:

关于Sunnu.UI控件库的使用就到这里了,有关串口的介绍还有很多细节的地方,如果想了解更多细节,大家可以直接去看别人的开源代码。

说明:另外也可以直接使用工具箱中提供的串口组件:SerialPort。关于SerialPort组件的使用,大家可以参考这篇文章。 源码开源C#桌面应用开发:串口调试助手

本人水平有限,代码有不足之处请谅解。欢迎大家一起交流学习。

源码地址

参考文献:

1.C#开发: 通信篇-串口调试助手

2.C#开发串口调试助手的详细教程

3.Sunny.UI说明文档

相关推荐
杨荧13 分钟前
【JAVA毕业设计】基于Vue和SpringBoot的服装商城系统学科竞赛管理系统
java·开发语言·vue.js·spring boot·spring cloud·java-ee·kafka
白子寰20 分钟前
【C++打怪之路Lv14】- “多态“篇
开发语言·c++
王俊山IT32 分钟前
C++学习笔记----10、模块、头文件及各种主题(一)---- 模块(5)
开发语言·c++·笔记·学习
为将者,自当识天晓地。34 分钟前
c++多线程
java·开发语言
小政爱学习!36 分钟前
封装axios、环境变量、api解耦、解决跨域、全局组件注入
开发语言·前端·javascript
k09331 小时前
sourceTree回滚版本到某次提交
开发语言·前端·javascript
神奇夜光杯1 小时前
Python酷库之旅-第三方库Pandas(202)
开发语言·人工智能·python·excel·pandas·标准库及第三方库·学习与成长
Themberfue1 小时前
Java多线程详解⑤(全程干货!!!)线程安全问题 || 锁 || synchronized
java·开发语言·线程·多线程·synchronized·
plmm烟酒僧1 小时前
Windows下QT调用MinGW编译的OpenCV
开发语言·windows·qt·opencv
测试界的酸菜鱼1 小时前
Python 大数据展示屏实例
大数据·开发语言·python