[轉]C#串口通信 SerialPort類


本文轉自:https://blog.csdn.net/weixin_41415541/article/details/80921956

因為公司項目需要將USB掃碼槍改為串口掃碼槍,串口掃碼的好處在於不需要一個輸入框來接受USB掃出來的文本,能解決多個掃碼槍一起掃碼時的並發問題,所以需要用到多線程及串口技術。

一、串口通信簡介
串行接口(串口)是一種可以將接受來自CPU的並行數據字符轉換為連續的串行數據流發送出去,同時可將接受的串行數據流轉換為並行的數據字符供給CPU的器件。一般完成這種功能的電路,我們稱為串行接口電路。

串口通信(Serial Communications)的概念非常簡單,串口按位(bit)發送和接收字節。盡管比按字節(byte)的並行通信慢,但是串口可以在使用一根線發送數據的同時用另一根線接收數據。串口通信最重要的參數是波特率、數據位、停止位和奇偶校驗。對於兩個進行通信的端口,這些參數必須匹配。

  1. 波特率:這是一個衡量符號傳輸速率的參數。指的是信號被調制以后在單位時間內的變化,即單位時間內載波參數變化的次數,如每秒鍾傳送960個字符,而每個字符格式包含10位(1個起始位,1個停止位,8個數據位),這時的波特率為960Bd,比特率為10位*960個/秒=9600bps。

  2. 數據位:這是衡量通信中實際數據位的參數。當計算機發送一個信息包,實際的數據往往不會是8位的,標准的值是6、7和8位。標准的ASCII碼是0~127(7位),擴展的ASCII碼是0~255(8位)。

  3. 停止位:用於表示單個包的最后幾位。典型的值為1,1.5和2位。由於數據是在傳輸線上定時的,並且每一個設備有其自己的時鍾,很可能在通信中兩台設備間出現了小小的不同步。因此停止位不僅僅是表示傳輸的結束,並且提供計算機校正時鍾同步的機會。

  4. 校驗位:在串口通信中一種簡單的檢錯方式。有四種檢錯方式:偶、奇、高和低。當然沒有校驗位也是可以的。

二、C#串口編程類
從.NET Framework 2.0開始,C#提供了SerialPort類用於實現串口控制。命名空間:System.IO.Ports。其中詳細成員介紹參看MSDN文檔。下面介紹其常用的字段、方法和事件。

 

三.基本用法
1.簡單的SerialPort類的使用

using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
using Business.UI.SerialPort.Configure;
using DevExpress.Data;
using DevExpress.XtraEditors;
using DevExpress.XtraEditors.Controls;
using DevExpress.XtraEditors.Popup;
using Newtonsoft.Json;
using Red.Utility.Win;
using Red.Utility.Win.DevUtility;


namespace Business.UI.SerialPort
{

public class ScanProvider
{
#region 初始化串口掃描槍


public System.IO.Ports.SerialPort _serialPort=new System.IO.Ports.SerialPort();


public ScanProvider(SerialEntity serialEntity)
{

// 串口名
_serialPort.PortName = serialEntity.PortName;
// 波特率
_serialPort.BaudRate = serialEntity.BaudRate;
// 數據位
_serialPort.DataBits = serialEntity.DataBits;
// 停止位
_serialPort.StopBits = (StopBits)Enum.Parse((typeof(StopBits)), serialEntity.StopBits);
// 無奇偶校驗位
_serialPort.Parity = (Parity)Enum.Parse((typeof(Parity)),serialEntity.Parity);


_serialPort.DataReceived += _serialPort_DataReceived;
}


/// <summary>
///
/// </summary>
/// <param name="portName">串口名</param>
/// <param name="baudRate">波特率</param>
public ScanProvider(System.IO.Ports.SerialPort _serialPort, string portName, int baudRate)
{
this._serialPort = _serialPort;
// 串口名
_serialPort.PortName = portName;
// 波特率
_serialPort.BaudRate = baudRate;
// 數據位
_serialPort.DataBits = 8;
// 停止位
_serialPort.StopBits = System.IO.Ports.StopBits.One;
// 無奇偶校驗位
_serialPort.Parity = System.IO.Ports.Parity.None;
_serialPort.DataReceived += _serialPort_DataReceived;
}

#endregion


#region Public


/// <summary>
/// 是否處於打開狀態
/// </summary>
public bool IsOpen
{
get { return _serialPort != null && _serialPort.IsOpen; }
}


/// <summary>
/// 打開串口
/// </summary>
/// <returns></returns>
public bool Open()
{
try
{

if (_serialPort == null)
return this.IsOpen;



if (_serialPort.IsOpen)
this.Close();



_serialPort.Open();


}
catch (Exception e)
{


Logger.Error(e);
_serialPort.Close();
}



return this.IsOpen;
}


/// <summary>
/// 關閉串口
/// </summary>
public void Close()
{
if (this.IsOpen)
_serialPort.Close();
}


/// <summary>
/// 向串口內寫入
/// </summary>
/// <param name="send">寫入數據</param>
/// <param name="offSet">偏移量</param>
/// <param name="count">寫入數量</param>
public void Write(byte[] send, int offSet, int count)
{
if (this.IsOpen)
{
_serialPort.Write(send, offSet, count);
}
}


public void Dispose()
{
if (this._serialPort == null)
return;
if (this._serialPort.IsOpen)
this.Close();
this._serialPort.Dispose();
this._serialPort = null;
}


#endregion

void _serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
// 等待100ms,防止讀取不全的情況
Thread.Sleep(100);


ReceiveDate();

}


public void ReceiveDate()
{
byte[] m_recvBytes = new byte[_serialPort.BytesToRead]; //定義緩沖區大小
int result = _serialPort.Read(m_recvBytes, 0, m_recvBytes.Length); //從串口讀取數據
if (result <= 0)
return;
string strResult = Encoding.UTF8.GetString(m_recvBytes, 0, m_recvBytes.Length); //對數據進行轉換
_serialPort.DiscardInBuffer();


if (this.DataReceived != null)
this.DataReceived(this, new SerialSortEventArgs() { Code = strResult });
}


public event EventHandler<SerialSortEventArgs > DataReceived;



}

}

以上就是簡單的SerialPort類的使用。

2.加入多線程操作

using System.Threading;
using Business.UI.SerialPort;
using Business.UI.SerialPort.Configure;
using Red.Utility.Win;
using Newtonsoft.Json;
using Red.Utility.Common.Log;
using Business.UI.Work.Scan;




namespace Business.UI.SerialPort
{
public class DoScan
{
//主線程
private Thread mainThread;
//子線程列表
public List<Thread> listTread=new List<Thread>();
//串口列表
List<SerialEntity> listSerial;
//同步上下文
private SynchronizationContext mainThreadSynContext;
//運行狀態
public bool isRuning;


public DoScan()
{
mainThread = Thread.CurrentThread;
mainThreadSynContext = SynchronizationContext.Current;//獲取當前線程的同步上下文;
}


//啟動
public void start()
{
isRuning = true;
//1.獲取所有配置
listSerial = Configuration.Read(); //獲取配置文件里的串口參數
if (listSerial == null)
{
Logger.Error("未發現配置");
return;
}
//2.遍歷,啟動子線程 ,打開端口

foreach (var serial in listSerial)
{
var workThread = new Thread(OpenCom);
workThread.Name = serial.PortName;
workThread.IsBackground = true;
workThread.Start(serial);
listTread.Add(workThread);
}

}
//停止
public void stop()
{



isRuning = false;
Thread.Sleep(2000);//2秒后

if (listTread==null)
{
return;
}
//關閉串口

foreach (var thread in listTread)
{

thread.Abort();

}
listTread.Clear();
}






#region 子線程使用
//打開串口
public void OpenCom(object serialEntity)
{
//打開串口


var _scanner = new ScanProvider((SerialEntity)serialEntity);


if (_scanner.Open())
{
//關聯事件處理程序
_scanner.DataReceived += _scanner_DataReceived;

mainThreadSynContext.Post(new SendOrPostCallback(OnConnected), "open:" + _scanner._serialPort.PortName);//通知主線程
}


//定時器 定時通知
System.Timers.Timer timer = new System.Timers.Timer();
timer.Interval = 2000;
timer.Elapsed += delegate
{
mainThreadSynContext.Post(new SendOrPostCallback(OnConnected), "state:" + _scanner._serialPort.PortName +"-"+ _scanner.IsOpen);//通知主線程
};
timer.Enabled = true;//生效


while (true)
{
if (!isRuning)
{
timer.Enabled = false;//生效
timer.Dispose();
CloseCom(_scanner);
return;
}
Thread.Sleep(1000);
}


}


/// <summary>
/// 關閉串口
/// </summary>
/// <param name="_scanner"></param>
public void CloseCom(ScanProvider _scanner)
{
var portname = _scanner._serialPort.PortName;
_scanner.Dispose();
mainThreadSynContext.Post(new SendOrPostCallback(OnConnected), "close:" + portname);//通知主線程
}


//由於是主線程的同步對象Post調用,這個是在主線程中執行的
private void OnConnected(object state)
{
//這里就回到了主線程里面了
//做一些事情
Logger.Debug(state.ToString());
}
//接收到數據
private void _scanner_DataReceived(object sender, SerialSortEventArgs e)
{


string code = e.Code;



Scanner scan = new Scanner(e.Code);//業務邏輯處理
}


#endregion


}

}

---------------------
作者:只會CVS
來源:CSDN
原文:https://blog.csdn.net/weixin_41415541/article/details/80921956
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM