zoukankan      html  css  js  c++  java
  • [转]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
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    Flex的 Event中属性currentTarget与target的差别
    考研复试机试题(2010)
    Android应用性能优化之使用SparseArray替代HashMap
    机房收费重构版总结
    中英文对照 —— 缩略词
    NLP(paper + code)
    NLP(paper + code)
    优化与凸优化初始
    优化与凸优化初始
    工具类与工具函数 —— 素数相关
  • 原文地址:https://www.cnblogs.com/freeliver54/p/10007644.html
Copyright © 2011-2022 走看看