溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

利用C#怎么讀取串口連接

發(fā)布時(shí)間:2021-01-05 14:17:34 來源:億速云 閱讀:250 作者:Leah 欄目:開發(fā)技術(shù)

本篇文章為大家展示了利用C#怎么讀取串口連接,內(nèi)容簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過這篇文章的詳細(xì)介紹希望你能有所收獲。

一、串口連接的打開與關(guān)閉

串口,即COM口,在.NET中使用 SerialPort 類進(jìn)行操作。串口開啟與關(guān)閉,是涉及慢速硬件的IO操作,頻繁打開或關(guān)閉會(huì)影響整體處理速度,甚至導(dǎo)致打開或關(guān)閉串口失敗。非特殊情況,串口一次性打開后,在退出程序時(shí)關(guān)閉串口即可。在打開串口前,可以設(shè)置一些常用的參數(shù)。常用的參數(shù)如下:

 (1)串口的接受/發(fā)送超時(shí)時(shí)間:ReadTimeout/WriteTimeout。

   (2)  串口的接受/發(fā)送緩存區(qū)大?。篟eadBufferSize/WriteBufferSize。

具體代碼如下:

// Open Com
   _serialPort = new SerialPort(com, baud);
   if (_serialPort.IsOpen) _serialPort.Close();

   // Set the read / write timeouts
   _serialPort.ReadTimeout = 500;
   _serialPort.WriteTimeout = 500;

   // Set read / write buffer Size,the default of value is 1MB
   _serialPort.ReadBufferSize = 1024 * 1024;
   _serialPort.WriteBufferSize = 1024 * 1024;

   _serialPort.Open();

   // Discard Buffer
   _serialPort.DiscardInBuffer();
   _serialPort.DiscardOutBuffer();

    需要注意的是超出緩沖區(qū)的部分會(huì)被直接丟棄。因此,如果需要使用串口傳送大文件,那接收方和發(fā)送方都需要將各自的緩沖區(qū)域設(shè)置的足夠大,以便能夠一次性存儲(chǔ)下大文件的二進(jìn)制數(shù)組。若條件限制,緩沖區(qū)域不能設(shè)置過大,那就需要在發(fā)送大文件的時(shí)候按照發(fā)送緩沖區(qū)大小分包去發(fā)送,接收方按順序把該數(shù)組組合起來形成接受文件的二進(jìn)制數(shù)組。

二、串口發(fā)送

SerialPort 類發(fā)送支持二進(jìn)制發(fā)送與文本發(fā)送,需要注意的是文本發(fā)送時(shí),需要知道轉(zhuǎn)換的規(guī)則,一般常用的是ASCII、UTF7、UTF-8、UNICODE、UTF32。具體代碼如下:

#region Send
    /// <summary>
    /// 發(fā)送消息(byte數(shù)組)
    /// </summary>
    /// <param name="buffer"></param>
    /// <param name="offset"></param>
    /// <param name="count"></param>
    public void Send(byte[] buffer, int offset, int count)
    {
      lock (_mux)
      {
        _serialPort.Write(buffer, offset, count);
        _sendCount += (count - offset);
      }
    }

    /// <summary>
    /// 發(fā)送消息(字符串)
    /// </summary>
    /// <param name="encoding">字符串編碼方式,具體方式見<see cref="Encoding"/></param>
    /// <param name="message"></param>
    public void Send(Encoding encoding , string message)
    {
      lock (_mux)
      {
        var buffer = encoding.GetBytes(message);
        _serialPort.Write(buffer, 0, buffer.Length);
        _sendCount += buffer.Length;
      }
    }
    #endregion

三、串口接受

串口接受需要注意,消息接受與消息處理要代碼分離。不能把流程處理的代碼放入信息接受處,因?yàn)橄⑻幚砘蚨嗷蛏贂?huì)有耗時(shí),這會(huì)造成當(dāng)發(fā)送方發(fā)送過快時(shí),接受方的接受緩沖區(qū)會(huì)緩存多條消息。我們可以把接受到的消息放入隊(duì)列中,然后在外部線程中,嘗試去拿出該條消息進(jìn)行消費(fèi)。采用 “生產(chǎn)-消費(fèi)”模式。具體代碼如下:

#region Receive
    private void PushMessage()
    {
      _serialPort.DataReceived += (sender, e) =>
      {
        lock (_mux)
        {
          if (_serialPort.IsOpen == false) return;
          int length = _serialPort.BytesToRead;
          byte[] buffer = new byte[length];
          _serialPort.Read(buffer, 0, length);
          _receiveCount += length;
          _messageQueue.Enqueue(buffer);
          _messageWaitHandle.Set();
        }
      };
    }

    /// <summary>
    /// 獲取串口接受到的內(nèi)容
    /// </summary>
    /// <param name="millisecondsToTimeout">取消息的超時(shí)時(shí)間</param>
    /// <returns>返回byte數(shù)組</returns>
    public byte[] TryMessage(int millisecondsToTimeout = -1)
    {
      if (_messageQueue.TryDequeue(out var message))
      {
        return message;
      }

      if (_messageWaitHandle.WaitOne(millisecondsToTimeout))
      {
        if (_messageQueue.TryDequeue(out message))
        {
          return message;
        }
      }
      return default;
    }
    #endregion

四、完整代碼與測(cè)試結(jié)果

串口工具類的完整代碼如下:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SerialportDemo
{
  public class SSerialPort
  {
    private SerialPort _serialPort;
    private readonly ConcurrentQueue<byte[]> _messageQueue;
    private readonly EventWaitHandle _messageWaitHandle;
    private int _receiveCount, _sendCount;
    private readonly object _mux;
    public int ReceiveCount
    {
      get => _receiveCount;
    }
    public  int SendCount
    {
      get => _sendCount;
    }
    public SSerialPort(string com, int baud )
    {
      // initialized
      _mux=new object();
      _receiveCount = 0;
      _sendCount = 0;
      _messageQueue = new ConcurrentQueue<byte[]>();
      _messageWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);

      // Open Com
      OpenCom(com.ToUpper(),baud);

      // Receive byte
      PushMessage();
    }

    private void OpenCom(string com, int baud)
    {
      // Open Com
      _serialPort = new SerialPort(com, baud);
      if (_serialPort.IsOpen) _serialPort.Close();

      // Set the read / write timeouts
      _serialPort.ReadTimeout = 500;
      _serialPort.WriteTimeout = 500;

      // Set read / write buffer Size,the default of value is 1MB
      _serialPort.ReadBufferSize = 1024 * 1024;
      _serialPort.WriteBufferSize = 1024 * 1024;

      _serialPort.Open();

      // Discard Buffer
      _serialPort.DiscardInBuffer();
      _serialPort.DiscardOutBuffer();
    }


    #region Static
    /// <summary>
    /// 獲取當(dāng)前計(jì)算機(jī)的串行端口名的數(shù)組
    /// </summary>
    /// <returns></returns>
    public static string[] GetPortNames()
    {
      return SerialPort.GetPortNames();
    }
    #endregion

    #region Receive
    private void PushMessage()
    {
      _serialPort.DataReceived += (sender, e) =>
      {
        lock (_mux)
        {
          if (_serialPort.IsOpen == false) return;
          int length = _serialPort.BytesToRead;
          byte[] buffer = new byte[length];
          _serialPort.Read(buffer, 0, length);
          _receiveCount += length;
          _messageQueue.Enqueue(buffer);
          _messageWaitHandle.Set();
        }
      };
    }

    /// <summary>
    /// 獲取串口接受到的內(nèi)容
    /// </summary>
    /// <param name="millisecondsToTimeout">取消息的超時(shí)時(shí)間</param>
    /// <returns>返回byte數(shù)組</returns>
    public byte[] TryMessage(int millisecondsToTimeout = -1)
    {
      if (_messageQueue.TryDequeue(out var message))
      {
        return message;
      }

      if (_messageWaitHandle.WaitOne(millisecondsToTimeout))
      {
        if (_messageQueue.TryDequeue(out message))
        {
          return message;
        }
      }
      return default;
    }
    #endregion


    #region Send
    /// <summary>
    /// 發(fā)送消息(byte數(shù)組)
    /// </summary>
    /// <param name="buffer"></param>
    /// <param name="offset"></param>
    /// <param name="count"></param>
    public void Send(byte[] buffer, int offset, int count)
    {
      lock (_mux)
      {
        _serialPort.Write(buffer, offset, count);
        _sendCount += (count - offset);
      }
    }

    /// <summary>
    /// 發(fā)送消息(字符串)
    /// </summary>
    /// <param name="encoding">字符串編碼方式,具體方式見<see cref="Encoding"/></param>
    /// <param name="message"></param>
    public void Send(Encoding encoding , string message)
    {
      lock (_mux)
      {
        var buffer = encoding.GetBytes(message);
        _serialPort.Write(buffer, 0, buffer.Length);
        _sendCount += buffer.Length;
      }
    }
    #endregion

    /// <summary>
    /// 清空接受/發(fā)送總數(shù)統(tǒng)計(jì)
    /// </summary>
    public void ClearCount()
    {
      lock (_mux)
      {
        _sendCount = 0;
        _receiveCount = 0;
      }
    }

    /// <summary>
    /// 關(guān)閉串口
    /// </summary>
    public void Close()
    {
      _serialPort.Close();
    }
  }
}

測(cè)試代碼如下:

class Program
  {
    static void Main(string[] args)
    {
      Console.WriteLine($"該計(jì)算機(jī)可使用的串口列表:{string.Join(",", SSerialPort.GetPortNames())}");

      Console.Write("請(qǐng)輸入需要打開的串口:");
      string port = Console.ReadLine();
      SSerialPort com = new SSerialPort(port, 57600);
      Console.WriteLine($"串口 {port} 打開成功...");

      Console.Write("請(qǐng)輸入需要打開的串口發(fā)送的消息:");
      string text = Console.ReadLine();

      while (true)
      {
        com.Send(Encoding.Default, text);
        Console.WriteLine($"總共發(fā)送 {com.SendCount}");
        var message = com.TryMessage();
        if (message != null)
        {
          Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss fff")} {Encoding.Default.GetString(message)}");

          //// TEST:從添加延時(shí)可以測(cè)試到,接受消息和處理消息必須分不同線程處理。因?yàn)閷?duì)于消息的處理或多或少都需要耗時(shí),這樣容易造成消息處理不及時(shí)。而添加到隊(duì)列后,我們可以隨時(shí)取出處理
          //System.Threading.Thread.Sleep(100*1);
        }
        Console.WriteLine($"總共接受 {com.ReceiveCount}");
      }


      Console.ReadKey();
    }
  }

使用串口工具測(cè)試如下,對(duì)于串口的接受如絲般順滑。當(dāng)我們?cè)谙⒅性黾訙y(cè)試延時(shí)后,就會(huì)發(fā)現(xiàn)當(dāng)串口工具繼續(xù)快速發(fā)送一段時(shí)間后關(guān)閉發(fā)送,發(fā)現(xiàn)使用隊(duì)列后,依然沒有丟失一條來自發(fā)送方的消息。

上述內(nèi)容就是利用C#怎么讀取串口連接,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI