如何使用C#串口通訊實(shí)現(xiàn)數(shù)據(jù)的發(fā)送和接收
串口通訊(Serial Communication)是一種常見(jiàn)的硬件設(shè)備與計(jì)算機(jī)之間的數(shù)據(jù)傳輸方式,廣泛應(yīng)用于工業(yè)控制、嵌入式系統(tǒng)、傳感器數(shù)據(jù)采集等領(lǐng)域。本文將詳細(xì)介紹如何使用C#實(shí)現(xiàn)基于串口通訊的數(shù)據(jù)發(fā)送和接收,并結(jié)合代碼示例解析其實(shí)現(xiàn)過(guò)程。
1. 概述
串口通訊的核心是System.IO.Ports.SerialPort類(lèi),它封裝了串口操作的底層細(xì)節(jié),提供了簡(jiǎn)單易用的接口。以下是串口通訊的基本流程:
1.初始化串口:設(shè)置串口參數(shù)(如波特率、數(shù)據(jù)位、停止位等)。
2.打開(kāi)串口:通過(guò)SerialPort.Open方法打開(kāi)串口。
3.發(fā)送數(shù)據(jù):通過(guò)SerialPort.Write方法向串口發(fā)送數(shù)據(jù)。
4.接收數(shù)據(jù):通過(guò)SerialPort.DataReceived事件異步接收數(shù)據(jù)。
5.處理數(shù)據(jù):對(duì)接收到的數(shù)據(jù)進(jìn)行解析和處理。
2. 關(guān)鍵技術(shù)點(diǎn)
2.1 SerialPort類(lèi)
SerialPort是C#中用于實(shí)現(xiàn)串口通訊的核心類(lèi),提供了發(fā)送和接收數(shù)據(jù)的方法。
2.2 異步接收數(shù)據(jù)
通過(guò)SerialPort.DataReceived事件異步接收數(shù)據(jù),避免阻塞主線程。
2.3 數(shù)據(jù)解析
接收到的數(shù)據(jù)通常是字節(jié)數(shù)組(byte[]),需要根據(jù)協(xié)議格式進(jìn)行解析。
2.4 事件機(jī)制
通過(guò)事件機(jī)制將接收到的數(shù)據(jù)傳遞給其他模塊處理。
3. 代碼實(shí)現(xiàn)
以下是基于串口通訊實(shí)現(xiàn)數(shù)據(jù)發(fā)送和接收的代碼示例。
3.1 SerialPortHelper類(lèi)
SerialPortHelper類(lèi)負(fù)責(zé)初始化串口、發(fā)送數(shù)據(jù)、接收數(shù)據(jù)以及處理數(shù)據(jù)。
public class SerialPortHelper
{
private SerialPort serialPort; // 串口對(duì)象
private List<byte> _buffer = new List<byte>(); // 數(shù)據(jù)緩沖區(qū)
/// <summary>
/// 自定義串口消息接收事件
/// </summary>
public event ReceiveDataEventHandler ReceiveDataEvent;
public SerialPortHelper()
{
serialPort = new SerialPort();
}
/// <summary>
/// 獲取當(dāng)前計(jì)算機(jī)所有的串行端口名
/// </summary>
public static string[] GetPortArray()
{
return SerialPort.GetPortNames(); // 獲取所有可用串口
}
/// <summary>
/// 設(shè)置串口參數(shù)
/// </summary>
public void SetSerialPort(string portName, int baudrate, Parity parity, int databits, StopBits stopBits)
{
serialPort.PortName = portName; // 端口名
serialPort.BaudRate = baudrate; // 波特率
serialPort.Parity = parity; // 奇偶校驗(yàn)
serialPort.DataBits = databits; // 數(shù)據(jù)位
serialPort.StopBits = stopBits; // 停止位
serialPort.DataReceived += ReceiveDataMethod; // 訂閱數(shù)據(jù)接收事件
}
/// <summary>
/// 打開(kāi)串口
/// </summary>
public void Open()
{
try
{
serialPort.Open(); // 打開(kāi)串口
}
catch (Exception ex)
{
Console.WriteLine("Error opening serial port: " + ex.Message);
}
}
/// <summary>
/// 關(guān)閉串口
/// </summary>
public void Close()
{
serialPort.Close(); // 關(guān)閉串口
}
/// <summary>
/// 發(fā)送數(shù)據(jù)(字節(jié)數(shù)組)
/// </summary>
public void SendDataMethod(byte[] data)
{
if (!serialPort.IsOpen)
{
Open(); // 如果串口未打開(kāi),則先打開(kāi)
}
serialPort.Write(data, 0, data.Length); // 發(fā)送數(shù)據(jù)
}
/// <summary>
/// 發(fā)送數(shù)據(jù)(字符串)
/// </summary>
public void SendDataMethod(string data)
{
if (!serialPort.IsOpen)
{
Open(); // 如果串口未打開(kāi),則先打開(kāi)
}
serialPort.Write(data); // 發(fā)送字符串
}
/// <summary>
/// 數(shù)據(jù)接收事件處理方法
/// </summary>
private void ReceiveDataMethod(object sender, SerialDataReceivedEventArgs e)
{
try
{
byte[] dataChunk = new byte[serialPort.BytesToRead]; // 讀取緩沖區(qū)中的數(shù)據(jù)
serialPort.Read(dataChunk, 0, dataChunk.Length);
_buffer.AddRange(dataChunk); // 將數(shù)據(jù)添加到緩沖區(qū)
while (_buffer.Count >= 3) // 確保緩沖區(qū)中有足夠的數(shù)據(jù)
{
byte thirdByte = _buffer[1]; // 獲取第三個(gè)字節(jié)(功能碼)
if (thirdByte == 0x03) // 功能碼0x03
{
if (_buffer.Count >= 19) // 檢查是否有完整的數(shù)據(jù)包
{
byte[] completePacket = _buffer.GetRange(0, 19).ToArray(); // 提取完整數(shù)據(jù)包
TriggerReceiveDataEvent(completePacket); // 觸發(fā)接收事件
_buffer.RemoveRange(0, 19); // 移除已處理的數(shù)據(jù)
}
else
{
break; // 數(shù)據(jù)不足,等待更多數(shù)據(jù)
}
}
else if (thirdByte == 0x10 || thirdByte == 0x06) // 功能碼0x10或0x06
{
byte[] dataToProcess = _buffer.ToArray(); // 處理整個(gè)緩沖區(qū)
TriggerReceiveDataEvent(dataToProcess); // 觸發(fā)接收事件
_buffer.Clear(); // 清空緩沖區(qū)
}
else
{
_buffer.RemoveRange(0, 3); // 移除無(wú)法識(shí)別的數(shù)據(jù)
}
}
}
catch (Exception ex)
{
Console.WriteLine("Error processing serial port data: " + ex.Message);
}
}
/// <summary>
/// 觸發(fā)接收數(shù)據(jù)事件
/// </summary>
private void TriggerReceiveDataEvent(byte[] data)
{
if (ReceiveDataEvent != null)
{
ReceiveDataEventArg arg = new ReceiveDataEventArg(data); // 創(chuàng)建事件參數(shù)
ReceiveDataEvent.Invoke(this, arg); // 觸發(fā)事件
}
}
}3.2 ReceiveDataEventArg類(lèi)
ReceiveDataEventArg類(lèi)用于封裝接收到的數(shù)據(jù),并作為事件參數(shù)傳遞。
public class ReceiveDataEventArg : EventArgs
{
public byte[] Data { get; set; } // 接收到的數(shù)據(jù)
public ReceiveDataEventArg(byte[] data)
{
Data = data; // 初始化數(shù)據(jù)
}
}3.3 主窗體(MainWindow)
主窗體負(fù)責(zé)初始化SerialPortHelper并處理接收到的數(shù)據(jù)。
public partial class MainWindow : Window
{
private SerialPortHelper serialPortHelper;
public MainWindow()
{
InitializeComponent();
SettingComPorts(); // 初始化串口
}
private void SettingComPorts()
{
string portName = "COM8"; // 串口名稱(chēng)
int baudRate = 115200; // 波特率
Parity parity = Parity.None; // 奇偶校驗(yàn)
int dataBits = 8; // 數(shù)據(jù)位
StopBits stopBits = StopBits.One; // 停止位
serialPortHelper = new SerialPortHelper();
serialPortHelper.SetSerialPort(portName, baudRate, parity, dataBits, stopBits); // 設(shè)置串口參數(shù)
serialPortHelper.Open(); // 打開(kāi)串口
serialPortHelper.ReceiveDataEvent += ReceiveDataEvent; // 訂閱接收數(shù)據(jù)事件
}
/// <summary>
/// 接收數(shù)據(jù)事件處理方法
/// </summary>
private void ReceiveDataEvent(object sender, ReceiveDataEventArg e)
{
byte[] receivedData = e.Data; // 獲取接收到的數(shù)據(jù)
if (receivedData[1] == 0x03) // 功能碼0x03
{
int receiveDataLength = receivedData[2]; // 獲取有效數(shù)據(jù)長(zhǎng)度
byte[] receiveDataBuffer = new byte[receiveDataLength];
Array.Copy(receivedData, 3, receiveDataBuffer, 0, receiveDataLength); // 提取有效數(shù)據(jù)
ushort[] units = messageData.GetUnitsFromByteArray(receiveDataBuffer); // 解析數(shù)據(jù)
if (units != null)
{
List<ChannelData> channelDatas = databaseHelper.GetUUTInfo(units); // 獲取設(shè)備信息
UpdateDeviceInfo(channelDatas); // 更新UI
}
}
}
}4. 數(shù)據(jù)傳遞流程詳解
4.1 初始化串口
在SettingComPorts方法中,設(shè)置串口參數(shù)并打開(kāi)串口。
訂閱ReceiveDataEvent事件以接收數(shù)據(jù)。
4.2 發(fā)送數(shù)據(jù)
通過(guò)SendDataMethod方法向串口發(fā)送數(shù)據(jù)。
4.3 接收數(shù)據(jù)
串口接收到數(shù)據(jù)后,觸發(fā)ReceiveDataMethod方法。
將接收到的數(shù)據(jù)添加到緩沖區(qū),并根據(jù)功能碼解析數(shù)據(jù)。
4.4 處理數(shù)據(jù)
在ReceiveDataEvent方法中,解析接收到的數(shù)據(jù)并更新UI。
5. 關(guān)鍵技術(shù)解析
5.1 異步接收數(shù)據(jù)
通過(guò)SerialPort.DataReceived事件異步接收數(shù)據(jù),避免阻塞主線程。
5.2 數(shù)據(jù)解析
根據(jù)功能碼(如0x03、0x06)解析接收到的字節(jié)數(shù)組,提取有效數(shù)據(jù)。
5.3 事件機(jī)制
通過(guò)事件將接收到的數(shù)據(jù)傳遞給主窗體或其他模塊處理。
6. 總結(jié)
本文詳細(xì)介紹了如何使用C#實(shí)現(xiàn)基于串口通訊的數(shù)據(jù)發(fā)送和接收。通過(guò)SerialPort類(lèi),我們可以輕松實(shí)現(xiàn)串口通訊,并結(jié)合事件機(jī)制實(shí)現(xiàn)數(shù)據(jù)的傳遞和處理。串口通訊適用于硬件設(shè)備與計(jì)算機(jī)之間的數(shù)據(jù)傳輸,是工業(yè)控制和嵌入式系統(tǒng)開(kāi)發(fā)中的重要技術(shù)。
希望這篇文章對(duì)您有所幫助!如果有任何問(wèn)題,歡迎在評(píng)論區(qū)留言討論。
到此這篇關(guān)于如何使用C#串口通訊實(shí)現(xiàn)數(shù)據(jù)的發(fā)送和接收的文章就介紹到這了,更多相關(guān)c#數(shù)據(jù)的發(fā)送和接收內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- C# SerialPort實(shí)現(xiàn)串口通訊的代碼詳解
- C#基于WinForm實(shí)現(xiàn)串口通訊
- C#實(shí)現(xiàn)簡(jiǎn)單串口通訊實(shí)例
- C#基于SerialPort類(lèi)實(shí)現(xiàn)串口通訊詳解
- c# 實(shí)現(xiàn)簡(jiǎn)單的串口通訊
- C#串口通訊概念及簡(jiǎn)單的實(shí)現(xiàn)方法
- C#遠(yuǎn)程發(fā)送和接收數(shù)據(jù)流生成圖片的方法
- C#使用post發(fā)送和接收數(shù)據(jù)的方法
- C#使用Socket發(fā)送和接收TCP數(shù)據(jù)實(shí)例
相關(guān)文章
C#獲取兩個(gè)數(shù)的最大公約數(shù)和最小公倍數(shù)示例
本文介紹了使用C#獲取兩個(gè)數(shù)的最大公約數(shù)和最小公倍數(shù)的示例,大家參考使用吧2014-01-01
C#中Request.Cookies 和 Response.Cookies 的區(qū)別分析
本文通過(guò)實(shí)例代碼向我們展示了C#中Request.Cookies 和 Response.Cookies 的區(qū)別,文章淺顯易懂,這里推薦給大家。2014-11-11
C#實(shí)現(xiàn)簡(jiǎn)單的Login窗口實(shí)例
這篇文章主要介紹了C#實(shí)現(xiàn)簡(jiǎn)單的Login窗口,實(shí)例分析了C#顯示及關(guān)閉登陸Login窗口的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-08-08
C#裝飾器模式(Decorator Pattern)實(shí)例教程
這篇文章主要介紹了C#裝飾器模式(Decorator Pattern),以一個(gè)完整實(shí)例形式講述了C#裝飾器模式的實(shí)現(xiàn)過(guò)程,有助于深入理解C#程序設(shè)計(jì)思想,需要的朋友可以參考下2014-09-09
C#中實(shí)現(xiàn)PriorityQueue優(yōu)先級(jí)隊(duì)列的代碼
這篇文章主要介紹了C#中PriorityQueue優(yōu)先級(jí)隊(duì)列的實(shí)現(xiàn),構(gòu)造初始化這部分主要介紹關(guān)鍵的字段和方法,比較器的初始化以及堆的初始化,需要的朋友可以參考下2021-12-12

