【Unity】使用 C# SerialPort 進行串口通信

索引

  • 一、SerialPort串口通信
  • 二、使用SerialPort
    • 1.創建SerialPort對象,進行基本配置
    • 2.寫入串口數據
      • ①.寫入串口數據的方法
      • ②.封裝數據
    • 3.讀取串口數據
      • ①.讀取串口數據的方法
      • ②.解析數據
    • 4.讀取串口數據的時機
      • ①.DataReceived事件
      • ②.多線程接收數據
    • 5.粘包問題處理

一、SerialPort串口通信

C#中的SerialPort類是.NET框架提供的一個用于串口通信的強大工具,主要用于實現計算機與外部設備(如傳感器、嵌入式設備、PLC等)之間的數據交換。

二、使用SerialPort

1.創建SerialPort對象,進行基本配置

使用SerialPort時,直接創建一個SerialPort對象即可,其基本配置項包括:

PortName:串口號,如COM1。
BaudRate:波特率,如9600或115200。
DataBits:數據位,通常為7或8。
StopBits:停止位,通常為1、1.5或2。
Parity:奇偶校驗位,如None(無校驗)、Odd(奇校驗)或Even(偶校驗)。

代碼如下:

    /// <summary>/// 串口號/// </summary>[Label("串口號")] public string portName;/// <summary>/// 波特率/// </summary>[Label("波特率")] public int baudRate;/// <summary>/// 數據位/// </summary>[Label("數據位")] public int dataBits;/// <summary>/// 停止位/// </summary>[Label("停止位")] public StopBits stopBits;/// <summary>/// 奇偶校驗位/// </summary>[Label("奇偶校驗位")] public Parity parity;private SerialPort _serialPort;protected override void Awake(){base.Awake();_serialPort = new SerialPort();_serialPort.PortName = portName;_serialPort.BaudRate = baudRate;_serialPort.DataBits = dataBits;_serialPort.StopBits = stopBits;_serialPort.Parity = parity;try{_serialPort.Open();}catch (Exception e){Log.Error(e.Message);}}

2.寫入串口數據

①.寫入串口數據的方法

寫入串口數據的方法非常簡單,如下:

		//將數據封裝為字節數組(可以直接強轉字符串,也可以按16進制處理等)byte[] bytes = EncapsulatePackage(data);_serialPort.Write(bytes, 0, bytes.Length);

②.封裝數據

如何封裝數據取決于數據交換的協議,比如直接強轉字符串:

	    /// <summary>/// 單個數據包結束符(換行符)/// </summary>private const byte EndSign = 10;/// <summary>/// 封裝數據包(這里要求所有字符必須為ASCII碼,也即是一個字符只占一個字節)/// </summary>/// <param name="data">原始數據</param>/// <returns>數據包</returns>private byte[] EncapsulatePackage(string data){byte[] bytes = new byte[data.Length + 1];for (int i = 0; i < data.Length; i++){bytes[i] = (byte)data[i];}//在每個數據包后面補充【結束符】,表明此數據包結束bytes[bytes.Length - 1] = EndSign;return bytes;}

這里以簡單協議進行講解(每個數據包以【換行符】代表結束符)。

3.讀取串口數據

①.讀取串口數據的方法

讀取串口數據的方法非常簡單,如下:

		//建立數據緩沖區(大小為串口中可讀取數據大小:_serialPort.BytesToRead)byte[] bytes = new byte[_serialPort.BytesToRead];_serialPort.Read(bytes, 0, bytes.Length);//解析數據包string data = AnalyzePackage(bytes);

②.解析數據

如何解析數據取決于數據交換的協議,比如直接強轉字符串:

        /// <summary>/// 解析數據包/// </summary>/// <param name="bytes">數據包</param>/// <returns>數據</returns>private string AnalyzePackage(byte[] bytes){return Encoding.Default.GetString(bytes);}

4.讀取串口數據的時機

那么對如上的簡單的寫入、讀取串口數據有了基本的了解之后,接下來便是相對不那么簡單的部分了。

首先,寫入串口數據的時機由我們說了算,何時調用便何時寫入,這點毋容置疑。

但是,讀取串口數據的時機該是何時?參考下面兩種方式。

①.DataReceived事件

SerialPort的DataReceived事件當對象對應的串口接收到了數據時便會觸發,用他來讀取數據簡直不要太絲滑

	    protected override void Awake(){base.Awake();//......_serialPort.DataReceived += OnDataReceived;//......}private void OnDataReceived(object sender, SerialDataReceivedEventArgs e){if (_serialPort.IsOpen && _serialPort.BytesToRead > 0){//建立數據緩沖區(大小為串口中可讀取數據大小:_serialPort.BytesToRead)byte[] bytes = new byte[_serialPort.BytesToRead];_serialPort.Read(bytes, 0, bytes.Length);//解析數據包string data = AnalyzePackage(bytes);}}

但是,這里必須得有一個但是,只要在Unity中用過SerialPort的都會知道DataReceived這玩意他不起作用,主要原因如下(別懷疑,就是問的AI):

1.Unity引擎對System.IO.Ports命名空間的支持有限,尤其是對DataReceived事件的支持存在缺陷。在Unity中,DataReceived事件通常不會像在常規C#項目中那樣正常觸發,這主要是因為Unity的運行環境與普通.NET應用有所不同,特別是在事件處理機制上存在差異。
2.DataReceived事件需要在一個獨立的線程中監聽串口數據,但在Unity中,主線程(用于游戲邏輯和渲染)和串口數據接收線程之間的同步機制存在問題。因此,DataReceived事件可能無法被正確觸發。
3.Unity的Update和FixedUpdate等事件函數運行在主線程中,而串口數據接收通常需要多線程支持。當串口操作在主線程中執行時,可能會因為線程沖突導致DataReceived事件無法觸發。

那么,我們不得不考慮更換其他方案了。

②.多線程接收數據

老樣子,像處理Socket通信那樣,多線程永遠是最強的利器:

    private Thread _receiveThread;protected override void Awake(){base.Awake();//新建一個線程,啟動數據接收方法ReceivedData_receiveThread = new Thread(new ThreadStart(ReceivedData));_receiveThread.Start();}protected override void OnDestroy(){base.OnDestroy();_receiveThread.Abort();_receiveThread = null;}/// <summary>/// 從串口接收數據/// </summary>private void ReceivedData(){while (true){if (_serialPort.IsOpen && _serialPort.BytesToRead > 0){try{//建立數據緩沖區(大小為串口中可讀取數據大小:_serialPort.BytesToRead)byte[] bytes = new byte[_serialPort.BytesToRead];_serialPort.Read(bytes, 0, bytes.Length);//解析數據包string data = AnalyzePackage(bytes);//......}catch (Exception e){Log.Error(e.Message);}}}}

5.粘包問題處理

當然,到此時我們并不能高枕無憂,還有另一個在數據交換領域普遍存在的問題需要我們解決,那就是數據的粘包問題。

粘包問題是指多個數據包在接收端被錯誤地合并成一個數據包,導致接收方無法正確區分每個數據包的邊界。這通常發生在連續發送多個數據包時,接收端來不及解析或緩沖區管理不當的情況下。

就像Socket通信一樣,發送方發出的數據是A03568,但接收方可能會分多次接收到,比如2次才接收完,那就可能是A03568,接收方只是一個機器不是人,自然不知道這2個包該連起來合成一個包,那么后續的處理自然就亂套了。

這就是通信協議存在的必要了,以我們的簡單通信協議為例(每個數據包以【換行符】代表結束符),在讀取數據的方法中進行防粘包處理:

    private byte[] _buffer = new byte[16];private List<byte> _receiveBuffer = new List<byte>();/// <summary>/// 從串口接收數據/// </summary>private void ReceivedData(){while (true){if (_serialPort.IsOpen && _serialPort.BytesToRead > 0){try{//讀取一次數據(最大不超過緩沖區大小)int count = _serialPort.Read(_buffer, 0, Mathf.Min(_buffer.Length, _serialPort.BytesToRead));for (int i = 0; i < count; i++){//如果為結束符,則代表一個數據包接收完成,解析該包if (_buffer[i] == EndSign){string data = AnalyzePackage(_receiveBuffer.ToArray());//清空緩沖區_receiveBuffer.Clear();//處理數據HandlerData(data);Log.Info($"接收串口數據:{data}");}//否則加入數據緩沖區else{_receiveBuffer.Add(_buffer[i]);}}}catch (Exception e){Log.Error(e.Message);}}}}

代碼很簡單,相信處理過字節流的人應該都能看懂,事實上在串口通信中如上的簡單通信協議已能勝任大多數情況。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/83855.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/83855.shtml
英文地址,請注明出處:http://en.pswp.cn/web/83855.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何寫好單元測試:Mock 脫離數據庫,告別 @SpringBootTest 的重型啟動

如何寫好單元測試&#xff1a;Mock 脫離數據庫&#xff0c;告別 SpringBootTest 的重型啟動 作者&#xff1a;Killian&#xff08;重慶&#xff09; — 歡迎各位架構獵頭、技術布道者聯系我&#xff0c;項目實戰豐富&#xff0c;代碼穩健&#xff0c;Mock測試愛好者。 技術棧&a…

【DNS】在 Windows 下修改 `hosts` 文件

在 Windows 下修改 hosts 文件&#xff0c;一般用于本地 DNS 覆蓋。操作步驟如下&#xff08;以 Windows 10/11 為例&#xff09;&#xff1a; 1. 以管理員權限打開記事本 點擊 開始 → 輸入 “記事本”在“記事本”圖標上右鍵 → 選擇 以管理員身份運行 如果提示“是否允許此…

共享內存實現進程通信

目錄 system V共享內存 共享內存示意圖 共享內存函數 shmget函數 shmat函數 shmdt函數 shmctl函數 代碼示例 shm頭文件 構造函數 獲取key值 創建者的構造方式 GetShmHelper 函數 GetShmUseCreate 函數 使用者的構造方式 GetShmForUse 函數 分離附加操作 DetachShm 函數 AttachS…

6月15日星期日早報簡報微語報早讀

6月15日星期日&#xff0c;農歷五月二十&#xff0c;早報#微語早讀。 1、證監會擬修訂期貨公司分類評價&#xff1a;明確扣分標準&#xff0c;優化加分標準&#xff1b; 2、國家考古遺址公園再添10家&#xff0c;全國已評定65家&#xff1b; 3、北京多所高校禁用羅馬仕充電寶…

破解關鍵領域軟件測試“三重難題”:安全、復雜性、保密性

在國家關鍵領域&#xff0c;軟件系統正成為核心戰斗力的一部分。相比通用軟件&#xff0c;關鍵領域軟件在 安全性、復雜性、實時性、保密性 等方面要求極高。如何保障安全合規前提下提升測試效率&#xff0c;確保系統穩定&#xff0c;已成為軟件質量保障的核心挑戰。 關鍵領域…

記錄一次 Oracle DG 異常停庫問題解決過程

記錄一次 Oracle DG 異常停庫問題解決過程 某醫院有以下架構的雙節點 Oracle 集群&#xff1a; 節點1:172.16.20.2 節點2:172.16.20.3 SCAN IP&#xff1a;172.16.20.1 DG&#xff1a;172.16.20.1206月12日&#xff0c;醫院信息科用戶反映無法連接 DG 服務器。 登錄 DG 服務…

MySQL使用EXPLAIN命令查看SQL的執行計劃

1?、EXPLAIN 的語法 MySQL 中的 EXPLAIN 命令是用于分析 SQL 查詢執行計劃的關鍵工具,它能幫助開發者理解查詢的執行方式并找出性能瓶頸??。 語法格式: EXPLAIN <sql語句> 【示例】查詢學生表關聯班級表的執行計劃。 (1)創建班級信息表和學生信息表,并創建索…

Go語言2個協程交替打印

WaitGroup 無緩沖channel waitgroup 用來控制2個協程 Add() 、Done()、Wait() channel用來實現信號的傳遞和信號的打印 ch1: 用來記錄打印的信號 ch2:用來實現信號的傳遞&#xff0c;實現2個協程的順序打印 package mainimport ("fmt""sync" )func ma…

微信小程序 路由跳轉

路由方式 官方參考文檔 wx.switchTab 實現底部導航欄 1.配置信息 app.json"tabBar": {"custom": true,"list": [{"pagePath": "pages/home/index","text": "首頁"},{"pagePath": "p…

[Java 基礎]正則表達式

正則表達式是一種強大的文本模式匹配工具&#xff0c;它使用一種特殊的語法來描述要搜索或操作的字符串模式。在 Java 中&#xff0c;我們可以使用 java.util.regex包提供的類來處理正則表達式。 :::color3 正則表達式不止 Java 語言提供了相應的功能&#xff0c;很多其他語言…

ArcGIS安裝出現1606錯誤解決辦法

問題背景&#xff1a; 由于最近Arcgis10.2打是有些功能不正常退出&#xff0c;比如arctoolbox中的&#xff0c;table to excel 功能&#xff0c;只要一點擊&#xff0c;arcgis就報錯退出&#xff0c;平常在使用過程中&#xff0c;也經常出現一些莫名其妙的崩潰現象&#xff0c…

wpf 解決DataGridTemplateColumn中width綁定失效問題

感謝酪酪烤奶 提供的Solution 文章目錄 感謝酪酪烤奶 提供的Solution使用示例示例代碼分析各類交互流程 WPF DataGrid 列寬綁定機制分析整體架構數據流分析1. ViewModel到Slider的綁定2. ViewModel到DataGrid列的綁定a. 綁定代理(BindingProxy)b. 列寬綁定c. 數據流 關鍵機制詳…

語音轉文本ASR、文本轉語音TTS

ASR Automatic Speech Recognition&#xff0c;語音轉文本。 技術難點&#xff1a; 聲學多樣性 口音、方言、語速、背景噪聲會影響識別準確性&#xff1b;多人對話場景&#xff08;如會議錄音&#xff09;需要區分說話人并分離語音。 語言模型適配 專業術語或網絡新詞需要動…

通用embedding模型和通用reranker模型,觀測調研

調研Qwen3-Embedding和Qwen3-Reranker 現在有一個的問答庫&#xff0c;包括150個QA-pair&#xff0c;用10個query去同時檢索問答庫的300個questionanswer Embedding模型&#xff0c;query-question的匹配分數 普遍高于 query-answer的匹配分數。比如對于10個query&#xff0c…

基于YOLOv8+Deepface的人臉檢測與識別系統

摘要 人臉檢測與識別系統是一個集成了先進計算機視覺技術的應用&#xff0c;通過深度學習模型實現人臉檢測、識別和管理功能。系統采用雙模式架構&#xff1a; ??注冊模式??&#xff1a;檢測新人臉并添加到數據庫??刪除模式??&#xff1a;識別數據庫中的人臉并移除匹…

Grdle版本與Android Gradle Plugin版本, Android Studio對應關系

Grdle版本與Android Gradle Plugin版本&#xff0c; Android Studio對應關系 各個 Android Gradle 插件版本所需的 Gradle 版本&#xff1a; https://developer.android.com/build/releases/gradle-plugin?hlzh-cn Maven上發布的Android Gradle Plugin&#xff08;AGP&#x…

用c語言實現簡易c語言掃雷游戲

void test(void) {int input 0;do{menu();printf("請選擇&#xff1a; >");scanf("%d", &input);switch (input){menu();case 1:printf("掃雷\n");game();break;case 2:printf("退出游戲\n");break;default:printf("輸入…

系統辨識的研究生水平讀書報告期末作業參考

這是一份關于系統辨識的研究生水平讀書報告&#xff0c;內容系統完整、邏輯性強&#xff0c;并深入探討了理論、方法與實際應用。報告字數超過6000字 從理論到實踐&#xff1a;系統辨識的核心思想、方法論與前沿挑戰 摘要 系統辨識作為連接理論模型與客觀世界的橋梁&#xff…

開源、免費、美觀的 Vue 后臺管理系統模板

隨著前端技術的不斷發展&#xff0c;Vue.js 憑借其輕量、高效、易上手的特性&#xff0c;成為國內外開發者最喜愛的前端框架之一。在構建后臺管理系統時&#xff0c;Vue 提供了以下優勢&#xff1a; 響應式數據綁定&#xff1a;讓頁面和數據保持同步&#xff0c;開發效率高。 …

適合 Acrobat DC 文件類型解析

文件類型 (File Type)ProgID (Continuous)ProgID (Classic)主要用途.pdfAcroExch.Document.DCAcroExch.Document.20XX (版本特定)Adobe PDF文檔格式&#xff0c;用于存儲文檔內容和格式.pdfxmlAcroExch.pdfxmlAcroExch.pdfxmlPDF與XML結合的格式&#xff0c;可能用于結構化數據…