最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

.Net?Core應(yīng)用增強(qiáng)型跨平臺串口類庫CustomSerialPort()詳解

 更新時間:2022年01月15日 11:57:35   作者:赫山老妖  
本文詳細(xì)講解了.Net?Core應(yīng)用增強(qiáng)型跨平臺串口類庫CustomSerialPort(),文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下

摘要

在使用SerialPort進(jìn)行串口協(xié)議解析過程中,經(jīng)常遇到接收單幀協(xié)議數(shù)據(jù)串口接收事件多次觸發(fā),協(xié)議解析麻煩的問題。針對此情況,基于開源跨平臺串口類庫SerialPortStrem進(jìn)行了進(jìn)一步封裝,實現(xiàn)了一種接收超時響應(yīng)事件機(jī)制,簡化串口通訊的使用。

引言

最近,寫了一篇博文《.net core跨平臺應(yīng)用研究-串口篇》得到了一些園友的好評,文中介紹了在跨平臺應(yīng)用研究過程中,在dotnet core下使用SerialPort類庫在linux下不能支持的踩坑經(jīng)歷及解決辦法。

因網(wǎng)上關(guān)于SerialPort類庫使用的相關(guān)文章較多,在該文中,對串口類庫的使用,一筆帶過。但在實際使用,使用過SerialPort類庫的同學(xué),可能遇到過在數(shù)據(jù)接收時,由于數(shù)據(jù)接收事件的觸發(fā)具有不確定性,很多時候,一幀通訊協(xié)議數(shù)據(jù),會多次觸發(fā),造成程序處理協(xié)議數(shù)據(jù)較為麻煩的問題。

為簡化串口通訊類庫的使用,筆者結(jié)合自己的相關(guān)經(jīng)驗,封裝了一個自定義增強(qiáng)型跨平臺串口類庫,以解決一幀協(xié)議數(shù)據(jù),多次觸發(fā)的問題。

基礎(chǔ)類庫的選擇

由于考慮的是跨平臺應(yīng)用,SerialPort類庫并不支持linux系統(tǒng)(在前一篇文章中已介紹過踩坑經(jīng)歷),筆者選用了SerialPortStream類庫進(jìn)行封裝。

該類庫支持windows系統(tǒng)和Linux系統(tǒng),但在Linux系統(tǒng)下運行,需要額外編譯目標(biāo)平臺支持庫并進(jìn)行相關(guān)環(huán)境配置。

相關(guān)編譯配置說明在https://github.com/jcurl/SerialPortStream已有介紹,也可參考本人的拙作《.net core跨平臺應(yīng)用研究-串口篇》

類庫的實現(xiàn)

創(chuàng)建跨平臺類庫

為了支持跨平臺,我們使用Visual Studio 2017創(chuàng)建一個基于.NET Standard的類庫。

NET Standard是一項API規(guī)范,每一個特定的版本,都定義了必須實現(xiàn)的基類庫。

.NET Core是一個托管框架,針對構(gòu)建控制臺、云、ASP.NET Core和UWP應(yīng)用程序進(jìn)行了優(yōu)化。

每一種托管實現(xiàn)(如.NET Core、.NET Framework或Xamarin)都必須遵循.NET Standard實現(xiàn)基類庫(BCL)。

關(guān)于NET Standard和跨平臺的詳細(xì)說明在此:

//m.fzitv.net/article/234699.htm

筆者也不再啰嗦呵。

實現(xiàn)機(jī)制/條件

通常串口通訊中,發(fā)送數(shù)據(jù)后,會有一段時間用于等待接收方應(yīng)答,如此一來,兩次數(shù)據(jù)發(fā)送之間,必然會有一定的時間間隔。如ModbusRTU協(xié)議就規(guī)定,兩次數(shù)據(jù)報文發(fā)送之間,需要等待超過發(fā)送4個字節(jié)以上的間隔時間。

筆者在單片機(jī)以及實時性較高的嵌入式系統(tǒng)中,為處理串口接收與協(xié)議的無關(guān)性,通常采用數(shù)據(jù)幀接收超時來處理數(shù)據(jù)幀的接收。根據(jù)串口通訊的速率計算出兩次通訊之間所需要超時間隔,取兩倍超時間隔時間作為超時參數(shù),每接收到一個字節(jié),將數(shù)據(jù)放入緩沖區(qū)并進(jìn)行計時,當(dāng)最后一個字節(jié)的接收時間超過超時時間,返回接收數(shù)據(jù)并清空緩存,一次完整接收完成(DMA接收方式不在此討論)。

.net core跨平臺實現(xiàn)

在自定義的串口類中,訂閱基礎(chǔ)串口類數(shù)據(jù)接收事件,在接收事件每次觸發(fā)后,讀出當(dāng)前可用的緩沖數(shù)據(jù)到自定義緩沖區(qū),同時,標(biāo)記最后接收時間Tick為當(dāng)前系統(tǒng)Tick。判斷是否開啟了接收超時處理線程,如未開啟,則開啟一個接收超時處理線程。

接收超時處理線程中,以一個較小的時間間隔進(jìn)行判斷,如果最后接收時間與當(dāng)前時間之間的間隔小于設(shè)置值(默認(rèn)128ms),休眠一段時間(默認(rèn)16ms)后循環(huán)檢查。如間隔時間大于設(shè)定值,觸發(fā)外部接收訂閱事件,傳出接收到的數(shù)據(jù),退出超時處理線程。

此處應(yīng)有流程圖。呵呵,懶得畫了,大家自行腦補(bǔ)吧。 ^_^

在windows系統(tǒng)或linux系統(tǒng)中,因系統(tǒng)的多任務(wù)處理的特性,系統(tǒng)實時性較差,通常50ms以下時間間隔的定時任務(wù),較大程度會出現(xiàn)不可靠的情況(任務(wù)執(zhí)行時間都有可能超過調(diào)用間隔時間)。

因此,默認(rèn)超時時間間隔設(shè)置為128ms。也可根據(jù)實際使用情況調(diào)整,但最小間隔不宜低于64ms。

注:此處為個人經(jīng)驗和理解,如不認(rèn)同,請直接忽視。

主要代碼

串口接收事件代碼:

         protected void Sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
         {
             int canReadBytesLen = 0;
             if (ReceiveTimeoutEnable)
             {
                 while (sp.BytesToRead > 0)
                 {
                     canReadBytesLen = sp.BytesToRead;
                     if (receiveDatalen + canReadBytesLen > BufSize)
                     {
                         receiveDatalen = 0;
                         throw new Exception("Serial port receives buffer overflow!");
                     }
                     var receiveLen = sp.Read(recviceBuffer, receiveDatalen, canReadBytesLen);
                     if (receiveLen != canReadBytesLen)
                     {
                         receiveDatalen = 0;
                         throw new Exception("Serial port receives exception!");
                     }
                     //Array.Copy(recviceBuffer, 0, receivedBytes, receiveDatalen, receiveLen);
                     receiveDatalen += receiveLen;
                     lastReceiveTick = Environment.TickCount;
                     if (!TimeoutCheckThreadIsWork)
                     {
                         TimeoutCheckThreadIsWork = true;
                         Thread thread = new Thread(ReceiveTimeoutCheckFunc)
                         {
                             Name = "ComReceiveTimeoutCheckThread"
                         };
                         thread.Start();
                     }
                 }
             }
             else
             {
                 if (ReceivedEvent != null)
                 {
                     // 獲取字節(jié)長度
                     int bytesNum = sp.BytesToRead;
                     if (bytesNum == 0)
                         return;
                     // 創(chuàng)建字節(jié)數(shù)組
                     byte[] resultBuffer = new byte[bytesNum];
 
                     int i = 0;
                     while (i < bytesNum)
                     {
                         // 讀取數(shù)據(jù)到緩沖區(qū)
                         int j = sp.Read(recviceBuffer, i, bytesNum - i);
                         i += j;
                     }
                     Array.Copy(recviceBuffer, 0, resultBuffer, 0, i);
                     ReceivedEvent(this, resultBuffer);
                     //System.Diagnostics.Debug.WriteLine("len " + i.ToString() + " " + ByteToHexStr(resultBuffer));
                 }
                 //Array.Clear (receivedBytes,0,receivedBytes.Length );
                 receiveDatalen = 0;
             }
         }

接收超時處理線程代碼:

         /// <summary>
         /// 超時返回數(shù)據(jù)處理線程方法
         /// </summary>
         protected void ReceiveTimeoutCheckFunc()
         {
             while (TimeoutCheckThreadIsWork)
             {
                 if (Environment.TickCount - lastReceiveTick > ReceiveTimeout)
                 {
                     if (ReceivedEvent != null)
                     {
                         byte[] returnBytes = new byte[receiveDatalen];
                         Array.Copy(recviceBuffer, 0, returnBytes, 0, receiveDatalen);
                         ReceivedEvent(this, returnBytes);
                     }
                     //Array.Clear (receivedBytes,0,receivedBytes.Length );
                     receiveDatalen = 0;
                     TimeoutCheckThreadIsWork = false;
                 }
                 else
                     Thread.Sleep(16);
             }
         }

創(chuàng)建.net core控制臺程序

為驗證我們的類庫是否能夠正常工作,我們創(chuàng)建一個使用類庫的.net core控制臺程序。

為啥選擇dotnet core,原因很簡單,跨平臺。本程序分別需在windows和linux系統(tǒng)下進(jìn)行運行測試。

  •     顯示系統(tǒng)信息(系統(tǒng)標(biāo)識、程序標(biāo)識等)
  •     列舉系統(tǒng)可用串口資源
  •     選擇串口
  •     打開串口/關(guān)閉串口
  •     串口測試(打開/發(fā)送/關(guān)閉)
         static void Main(string[] args)
         {
             SetLibPath();
             ShowWelcome();
 
             GetPortNames();
             ShowPortNames();
 
             if (serailports.Length == 0)
             {
                 Console.WriteLine($"Press any key to exit");
                 Console.ReadKey();
 
                 return;
             }
 #if RunIsService
             RunService();
 #endif
 
             bool quit = false;
             while (!quit)
             {
                 Console.WriteLine("\r\nPlease Input command Key\r\n");
                 Console.WriteLine("p:Show SerialPort List");
                 Console.WriteLine($"t:Test Uart:\"{selectedComPort}\"");
                 Console.WriteLine($"o:Open Uart:\"{selectedComPort}\"");
                 Console.WriteLine($"c:Close Uart:\"{selectedComPort}\"");
                 Console.WriteLine("n:select next serial port");
                 Console.WriteLine("q:exit app");
                 Console.WriteLine();
                 var key = Console.ReadKey().KeyChar;
                 Console.WriteLine();
 
                 switch (key)
                 {
                     case (Char)27:
                     case 'q':
                     case 'Q':
                         quit = true;
                         break;
                     case 's':
                         ShowWelcome();
                         break;
                     case 'p':
                         ShowPortNames();
                         break;
                     case 'n':
                         SelectSerialPort();
                         break;
                     case 't':
                         TestUart(selectedComPort);
                         break;
                     case 'w':
                         TestWinUart(selectedComPort);
                         break;
                     case 'o':
                         OpenUart(selectedComPort);
                         break;
                     case 'c':
                         CloseUart();
                         break;
                 }
             }
         }

筆者使用類庫是直接引用類庫項目,大家需要使用的話,可在解決方案資源管理器中,項目的依賴項上點擊右鍵

在NuGet包管理器中,搜索SerialPort或flyfire即可找到并安裝本類庫。

類庫地址

類庫地址:https://www.nuget.org/packages/flyfire.CustomSerialPort

跨平臺測試

Windows測試輸出界面

ubuntu測試輸出界面

源碼地址

類庫源碼與例程地址:https://github.com/flyfire-cn/flyfire.CustomSerialPort

有需要的同學(xué),請自行獲取。

到此這篇關(guān)于.Net Core應(yīng)用增強(qiáng)型跨平臺串口類庫CustomSerialPort()的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • .Net Core中使用Quartz.Net實踐記錄

    .Net Core中使用Quartz.Net實踐記錄

    Quartz.NET是一套很好的任務(wù)調(diào)度框架。下面這篇文章主要給大家介紹了關(guān)于.Net Core中使用Quartz.Net的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-09-09
  • Visual Studio 2013如何使XML文件轉(zhuǎn)換成類

    Visual Studio 2013如何使XML文件轉(zhuǎn)換成類

    Visual Studio 2013如何使XML文件轉(zhuǎn)換成類?這篇文章主要介紹了Visual Studio2013輕松將你的XML文件轉(zhuǎn)換成類的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-07-07
  • ASP.NET中GridView的文件輸出流方式

    ASP.NET中GridView的文件輸出流方式

    本文的主要內(nèi)容是講ASP.NET中GridView輸出顯示的文件,這是個人項目中的一點小經(jīng)驗,希望能給到有需要幫助的人。
    2016-08-08
  • Asp.net FileUpload+Image制作頭像效果示例代碼

    Asp.net FileUpload+Image制作頭像效果示例代碼

    個人信息中通常需要自己的頭像或者照片,今天主要介紹一下使用FileUpload+img控件上傳照片,感興趣的朋友可以參考下
    2013-08-08
  • URLRewriter最簡單入門介紹 URLRewriter相關(guān)資源

    URLRewriter最簡單入門介紹 URLRewriter相關(guān)資源

    配置好后,查看日志看到的狀態(tài)都是200,IIS直接認(rèn)為這個文件是存在的了, 而不是301,或302,這在某些情況下可能會不適用,比如:搜索引擎優(yōu)化時目錄或文件調(diào)整。
    2008-07-07
  • 一文掌握.Net?core中的緩存

    一文掌握.Net?core中的緩存

    Cache(緩存)是優(yōu)化web應(yīng)用的常用方法,緩存存放在服務(wù)端的內(nèi)存中,被所有用戶共享,本文主要給大家介紹.netcore中的緩存知識,需要的朋友可以參考下
    2022-06-06
  • Entity Framework之DB First方式詳解

    Entity Framework之DB First方式詳解

    這篇文章主要為大家詳細(xì)介紹了Entity Framework三種方式之一DataBase First,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-07-07
  • asp.net 通過UserAgent判斷智能設(shè)備(Android,IOS)

    asp.net 通過UserAgent判斷智能設(shè)備(Android,IOS)

    搜集了比較全的 智能設(shè)備 的 Agent,然后又寫了程序,需要的朋友可以參考下
    2011-10-10
  • ASP.NET MVC學(xué)習(xí)之NuGet在VS中的運用淺談

    ASP.NET MVC學(xué)習(xí)之NuGet在VS中的運用淺談

    這篇文章主要給大家介紹了關(guān)于ASP.NET MVC學(xué)習(xí)之NuGet在VS中運用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-01-01
  • asp.net AjaxControlToolKit--TabContainer控件的介紹

    asp.net AjaxControlToolKit--TabContainer控件的介紹

    ModalPopup控件允許一個asp頁面的部分內(nèi)容以對話框的模式顯示給用戶,同時會限制用戶于頁面的其他部分交互。對話框顯示的內(nèi)容可以是一個層級,這個層級的背景可以使用戶自定義的格式,簡單的理解好比是一個對話框彈出來后,主頁面會顯示灰色,且不可操作。
    2009-06-06

最新評論

砚山县| 涿鹿县| 泸水县| 黑龙江省| 兴义市| 南投市| 元阳县| 游戏| 郧西县| 石景山区| 凉城县| 五家渠市| 汕尾市| 江山市| 金沙县| 商南县| 前郭尔| 南靖县| 屏南县| 肥东县| 辰溪县| 鄯善县| 调兵山市| 米易县| 班戈县| 睢宁县| 武清区| 和田市| 新郑市| 滦平县| 年辖:市辖区| 喀什市| 青川县| 兴安盟| 平果县| 无棣县| 甘肃省| 垦利县| 黔西县| 河东区| 松原市|