SerialPort 串口编程知识整理(一) - future - 博客园
2010-03-03 12:46 by doggies, 113 visits, , ,

1、基本概念

      MS .NET FrameWork2.0中对串口通讯进行了封装,我们可以在.net2.0及以上版本开发时直接使用SerialPort类对串口进行读写操作。

SerialPort类的属性主要包括:

串口名称(PortName

波特率(BaudRate

数据位 DataBits

停止位 StopBits

奇偶校验 Parity

握手协议 Handshake

SerialPort类的事件主要包括:

DataReceived:用于异步接收串口数据

SerialPort类的方法主要包括:

Open();Close();Read();Write()等。

相关内容可以参考MSDN或者博文

        

 

2、需求和场景介绍

      本文是基于之前开发的一个油站项目,主要功能是实现对加油机数据的采集和对加油机的一些控制,例如停开机、设置单价、定量定额加油等操作。

系统通过PC机串口,与下位机进行通讯,下位机和加油机进行通讯,负责采集加油机数据和控制加油机的操作。

 

3、设计思想

         (1)在界面设计上,由于系统启动后要始终实时监控加油数据,采用了SDI展示方式。

截图如下:

    (2)在系统设计方面,加油机监控软件具有一定的实时性、稳定性和数据并发的非功能性需求。所以在对加油数据采集时,使用事件DataReceived,用于异步接收串口数据。使用watchdog方式监控系统消息,通过缓存池对数据进行过滤,减轻数据库的压力,提高系统性能。

 

4、代码演示

         系统采用CS结构,使用SerialPort类进行串口通讯。对SerialPort类进行了封装,以保证多个窗体间对串口实例的调用。

         封装的串口通讯类参考:

串口通讯类:SerialPortDao.cs
/*----------------------------------------------------------------
           // Copyright (C) 北京****科技有限公司
           // 版权所有。 
           //
           // 文件名:SerialPortDao
           // 文件功能描述:封装串口组件,实现对串口的统一访问和操作
           //
           // 
           // 创建标识:** 2009-5-23
           //
           // 修改标识:**
           // 修改描述:
//----------------------------------------------------------------
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO.Ports;

namespace LY.FuelStationPOS.Protocol
{
    
/// <summary>
    
/// 提供对串口的统一访问
    
/// </summary>
    public sealed class SerialPortDao
    {
        
#region 事件和字段定义
        
public event PortDataReceivedEventHandle Received;
        
public SerialPort serialPort = null;
        
public bool ReceiveEventFlag = false;  //接收事件是否有效 false表示有效

        
private static readonly SerialPortDao instance = new SerialPortDao();

        
#endregion

        
#region 属性定义
        
private string protName;
        
public string PortName
        {
            
get { return serialPort.PortName; }
            
set 
            { 
                serialPort.PortName 
= value;
                protName 
= value;
            }
        }
        
#endregion

        
#region 构造函数
        
private SerialPortDao()
        {
            LoadSerialPort();
        }

        
private void LoadSerialPort()
        {
            serialPort 
= new SerialPort();
            serialPort.BaudRate 
= 9600;
            serialPort.Parity 
= Parity.Even;
            serialPort.DataBits 
= 8;
            serialPort.StopBits 
= StopBits.One;
            serialPort.Handshake 
= Handshake.None;
            serialPort.RtsEnable 
= true;
            serialPort.ReadTimeout 
= 2000;
            serialPort.DataReceived 
+= new SerialDataReceivedEventHandler(DataReceived);
        }

      
        
#endregion      
     
        
#region 串口操作集合
        
/// <summary>
        
/// 返回串口对象的单个实例
        
/// </summary>
        
/// <returns></returns>
        public static SerialPortDao GetSerialPortDao()
        {         
            
return instance;
        }       

        
/// <summary>
        
/// 释放串口资源
        
/// </summary>
        ~SerialPortDao()
        {
            Close();
        }

        
/// <summary>
        
/// 打开串口
        
/// </summary>
        public void Open()
        {
            
try
            {
                
if (!serialPort.IsOpen)
                {
                    serialPort.Open();                    
                }
            }
            
catch (Exception ex)
            {
                
throw ex;
            }           
        }

        
/// <summary>
        
/// 关闭串口
        
/// </summary>
        public void Close()
        {
            
if (serialPort.IsOpen)
            {
                serialPort.Close();
            }
        }

        
/// <summary>
        
/// 串口是否打开
        
/// </summary>
        
/// <returns></returns>
        public bool IsOpen()
        {
            
return serialPort.IsOpen;
        }

       

        
/// <summary>
        
/// 数据发送
        
/// </summary>
        
/// <param name="data">要发送的数据字节</param>
        public void SendData(byte[] data)
        {
            
try
            {
                serialPort.DiscardInBuffer();
                serialPort.Write(data, 
0, data.Length);
            }
            
catch (Exception ex)
            {
                
throw ex;
            }
        }

        
/// <summary>
        
/// 发送命令
        
/// </summary>
        
/// <param name="SendData">发送数据</param>
        
/// <param name="ReceiveData">接收数据</param>
        
/// <param name="Overtime">超时时间</param>
        
/// <returns></returns>
        public int SendCommand(byte[] SendData, ref byte[] ReceiveData, int Overtime)
        {
            
if (serialPort.IsOpen)
            {
                
try
                {
                    ReceiveEventFlag 
= true;        //关闭接收事件
                    serialPort.DiscardInBuffer();   //清空接收缓冲区                 
                    serialPort.Write(SendData, 0, SendData.Length);
                    System.Threading.Thread.Sleep(
50);
                    
int num = 0, ret = 0;
                    
while (num++ < Overtime)
                    {
                        
if (serialPort.BytesToRead >= ReceiveData.Length)
                        {
                            
break;
                        }
                        System.Threading.Thread.Sleep(
50);
                    }
                    
if (serialPort.BytesToRead >= ReceiveData.Length)
                    {
                        ret 
= serialPort.Read(ReceiveData, 0, ReceiveData.Length);
                    }
                    
else
                    {
                        ret 
= serialPort.Read(ReceiveData, 0, serialPort.BytesToRead);
                    }
                    ReceiveEventFlag 
= false;       //打开事件
                    return ret;
                }
                
catch (Exception ex)
                {
                    ReceiveEventFlag 
= false;
                    
throw ex;
                }
            }
            
return -1;
        }

        
///<summary>
        
///数据发送
        
///</summary>
        
///<param name="data">要发送的数据字符串</param>
        public void SendData(string data)
        {
            
//禁止接收事件时直接退出
            if (ReceiveEventFlag)
            {
                
return;
            }
            
if (serialPort.IsOpen)
            {
                serialPort.Write(data);
            }
        }

         
///<summary>
         
///将指定数量的字节写入输出缓冲区中的指定偏移量处。
         
///</summary>
         
///<param name="data">发送的字节数据</param>
         
///<param name="offset">写入偏移量</param>
         
///<param name="count">写入的字节数</param>
        public void SendData(byte[] data, int offset, int count)
        {
            
//禁止接收事件时直接退出
            if (ReceiveEventFlag)
            {
                
return;
            }
            
if (serialPort.IsOpen)
            {
                serialPort.Write(data, offset, count);
            }
        }
               

        
/// <summary>
        
/// 数据接收
        
/// </summary>
        
/// <param name="sender"></param>
        
/// <param name="e"></param>
        public void DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            
//禁止接收事件时直接退出
            if (ReceiveEventFlag)
            {
                
return;
            }

            
try
            {
                
byte[] data = new byte[serialPort.BytesToRead];
                serialPort.Read(data, 
0, data.Length);
                
if (Received != null)
                {
                    Received(sender, 
new PortDataReciveEventArgs(data));
                }
            }
            
catch (Exception ex)
            {
                
//throw ex;
            }
            
        }
    }
}
串口通讯数据类:PortDataReciveEventArgs
/*----------------------------------------------------------------
           // Copyright (C) 北京****科技有限公司
           // 版权所有。 
           //
           // 文件名:PortDataReciveEventArgs
           // 文件功能描述:重写PortDataReciveEventArgs参数类
           //
           // 
           // 创建标识:** 2009-5-23
           //
           // 修改标识:
           // 修改描述:
           //
           // 修改标识:
           // 修改描述:
//----------------------------------------------------------------
*/
using System;
using System.Collections.Generic;
using System.Text;

namespace LY.FuelStationPOS.Protocol
{
    
public delegate void PortDataReceivedEventHandle(object sender, PortDataReciveEventArgs e);

    
public class PortDataReciveEventArgs : EventArgs
    {
        
private byte[] data;

        
public byte[] Data
        {
            
get { return data; }
            
set { data = value; }
        }

        
public PortDataReciveEventArgs()
        {
            
this.data = null;
        }

        
public PortDataReciveEventArgs(byte[] data)
        {
            
this.data = data;
        }

        
    }
}

 

5、声明

     本文来源于实际项目,并且系统的串口通讯很稳定,各位朋友可以放心使用。本文的参考内容都已经标示,如果有任何问题欢迎回复。

     接下来准备要分享的内容包括:

     (1) 串口编程中经常用到的进制转换和其他公共方法

     (2) 串口编程的系统设计实用经验总结

     (3)多串口系统的分布式架构设计

郑重声明:资讯 【SerialPort 串口编程知识整理(一) - future - 博客园】由 发布,版权归原作者及其所在单位,其原创性以及文中陈述文字和内容未经(企业库qiyeku.com)证实,请读者仅作参考,并请自行核实相关内容。若本文有侵犯到您的版权, 请你提供相关证明及申请并与我们联系(qiyeku # qq.com)或【在线投诉】,我们审核后将会尽快处理。
—— 相关资讯 ——