C#上位机系列教程零基础入门-收发处理(上)

[复制链接]
查看67564 | 回复0 | 2024-12-24 14:37:30 | 显示全部楼层 |阅读模式
欢迎来到C#上位机教程的零基础入门!本教程将帮助你从零开始学习C#编程,并使用C#构建上位机应用程序,本教程将以经典的串口工具开发为Demo,讲述一个项目如何去实现,本节内容不涉及到串口工具的具体功能实现,主要讲述下面几个概念:

1、异步线程更新UI:this.Invoke()

在上一章,我们重点就介绍了DataReceived事件,DataReceived在辅助线程中执行,数据要更新到UI的线程时,这个操作就要跨线程了,在 C# 中,this.Invoke() 是一个用于在 Windows 窗体应用程序中跨线程访问控件的方法。由于 Windows 窗体应用程序是单线程模型,即 UI 线程负责处理用户界面的绘制和事件处理,如果在其他线程中访问 UI 控件,可能会引发线程间操作无效的异常。为了解决这个问题,可以使用 this.Invoke() 方法来在 UI 线程上执行指定的委托或方法。该方法的语法如下:this.Invoke(delegate)其中,delegate 是一个委托或方法,它将在 UI 线程上同步执行。下面是一个示例,演示了如何在其他线程中更新 UI 控件的文本:
using System;using System.Threading;using System.Windows.Forms;namespaceWindowsFormsApp{publicpartialclassMainForm : Form    {publicMainForm()        {            InitializeComponent();        }
privatevoidUpdateTextBox(string text)        {if (textBox.InvokeRequired)            {// 在 UI 线程上同步执行委托this.Invoke(new Action<string>(UpdateTextBox), text);            }else            {                textBox.Text = text;            }        }
privatevoidbutton_Click(object sender, EventArgs e)        {// 在新线程中更新文本框            Thread thread = new Thread(() =>            {// 模拟耗时操作                Thread.Sleep(2000);
// 更新文本框                UpdateTextBox("Hello, World!");            });            thread.Start();        }    }}
在上述示例中,当用户点击按钮时,会创建一个新线程,在新线程中模拟一个耗时操作,并通过 UpdateTextBox() 方法更新文本框的内容。由于在新线程中无法直接访问 UI 控件,因此使用 this.Invoke() 方法将更新操作委托到 UI 线程上执行,以避免线程间操作无效的异常。请注意,this.Invoke() 方法会阻塞当前线程,直到委托或方法在 UI 线程上执行完成。因此,在使用 this.Invoke() 方法时,需要注意避免在 UI 线程上执行耗时操作,以免导致界面卡顿或无响应。

2、数据接收缓存区和和数据发送缓存区的建立

首先为什么要建立缓存区


    数据的临时存储:数据接收和发送缓存区用于临时存储从串口接收到的数据和待发送的数据。串口通信中,数据的传输速率可能会受到各种因素的影响,如串口的波特率、数据包的大小等。建立缓存区可以确保数据在传输过程中不会丢失,同时也可以方便地对数据进行处理和管理。

    数据的处理和解析:通过建立数据接收缓存区,可以将从串口接收到的数据逐个字节地存储在缓存区中,以便进行后续的处理和解析。例如,可以根据特定的数据格式或协议对接收到的数据进行解析,提取出有用的信息。

    数据的拼接和分割:有时候,从串口接收到的数据可能会被分成多个数据包进行传输。通过建立数据接收缓存区,可以将接收到的数据包逐个存储在缓存区中,然后根据特定的分隔符或数据包长度进行拼接,以还原完整的数据。

    数据的发送控制:建立数据发送缓存区可以对待发送的数据进行控制和管理。例如,可以将待发送的数据存储在缓存区中,然后根据需要进行分批发送,或者根据特定的发送策略进行发送,以提高数据传输的效率和可靠性。

我们将通过实例List<byte>的方式建立数据缓存区,使用List,<byte>建立数据缓存区的好处有:



    动态大小:List<byte> 是一个动态数组,可以根据需要自动调整大小。在串口通信中,接收和发送的数据长度可能不固定,使用动态缓存区可以灵活地处理不同长度的数据。

    方便操作:List<byte> 提供了丰富的方法和属性,方便对缓存区进行操作。例如,可以使用 Add() 方法向缓存区添加新的字节,使用 RemoveRange() 方法删除指定范围的字节,使用索引访问特定位置的字节等。

    可读性和易用性:使用 List<byte> 可以更直观地表示数据的接收和发送过程。通过向缓存区添加或删除字节,可以清晰地反映数据的流动和处理过程,提高代码的可读性和易用性。

    兼容性:List<byte> 是 .NET Framework 中常用的数据结构,广泛应用于各种场景。使用 List<byte> 作为缓存区可以提高代码的兼容性和可维护性,便于与其他模块或库进行集成。

    下面使用简单的例子来演示了如何使用 List<byte> 建立数据接收和发送缓存区:
    using System;using System.Collections.Generic;using System.IO.Ports;
    namespaceSerialPortTool{classProgram    {static List<byte> receiveBuffer = new List<byte>();static List<byte> sendBuffer = new List<byte>();
    staticvoidMain(string[] args)        {            SerialPort serialPort = new SerialPort("COM1", 9600);            serialPort.DataReceived += SerialPort_DataReceived;
    // 添加数据到发送缓存区            sendBuffer.AddRange(newbyte[] { 0x01, 0x02, 0x03 });
    // 从发送缓存区发送数据            SendData(serialPort);
    // 接收数据并处理            ReceiveData();
                Console.ReadLine();        }
    staticvoidSerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)        {            SerialPort serialPort = (SerialPort)sender;
    // 读取接收缓冲区中的数据int bytesToRead = serialPort.BytesToRead;byte[] buffer = newbyte[bytesToRead];            serialPort.Read(buffer, 0, bytesToRead);
    // 添加数据到接收缓存区            receiveBuffer.AddRange(buffer);
    // 处理接收缓存区中的数据            ProcessReceivedData();        }
    staticvoidSendData(SerialPort serialPort)        {// 发送发送缓存区中的数据byte[] buffer = sendBuffer.ToArray();            serialPort.Write(buffer, 0, buffer.Length);        }
    staticvoidReceiveData()        {// 处理接收缓存区中的数据            ProcessReceivedData();        }
    staticvoidProcessReceivedData()        {// 处理接收缓存区中的数据foreach (byte data in receiveBuffer)            {// 处理每个字节的数据                Console.WriteLine(data);            }
    // 清空接收缓存区            receiveBuffer.Clear();        }    }}

在上述示例中,我们使用List<byte> 分别建立了数据接收缓存区 receiveBuffer 和数据发送缓存区 sendBuffer。在串口数据接收事件中,将接收到的数据添加到接收缓存区中,并调用 ProcessReceivedData() 方法处理接收到的数据。在发送数据时,将发送缓存区中的数据发送出去。通过使用 List<byte> 缓存区,可以灵活地处理串口通信中的数据接收和发送。

3、字符编码格式

在 C# 中处理串口工具中的字符编码格式,可以使用 Encoding 类提供的方法来进行编码和解码操作。Encoding 类位于 System.Text 命名空间中,提供了各种编码和解码方式,如 UTF-8、ASCII、Unicode 等。

下面是一些常见的字符编码处理操作:


    将字符串编码为字节数组:

    byte[] bytes = newbyte[] { 72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33 };string str = Encoding.UTF8.GetString(bytes);


    将字节数组解码为字符串:
byte[] bytes = newbyte[] { 72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33 };string str = Encoding.UTF8.GetString(bytes);
关于字符编码请参考:https://www.cnblogs.com/yank/p/3529395.html

在本示例中将使用“GB2312”编码格式,我们将用到 下面这个方法接收数据
receDataTB.Text = Encoding.GetEncoding("gb2312").GetString(  receDataTB.Text = Encoding.GetEncoding("gb2312").GetString(receiveBuffer.ToArray()).Replace("\0", "\\0"););
本章涉及的内容较多,需要对基础知识有一定的了解,从本章起,我们将放慢脚步,逐步补充基础知识。以上内容重要知识点有:




您需要登录后才可以回帖 登录 | 注册哦

本版积分规则