C#调用PComm(线程安全)

在串口通讯这块,使用过DELPHI的SPCOMM,微软的MSCOMM,C#自带的串口类发现在多线程中都有可能不稳定,发生死锁等,找很久才能找出问题。
SPCOMM因为是基于WINDOWS消息的,在多线程中不好使,无法接收数据。
MSCOMM本来是一个商业控件,也不是线程安全的,在软件上使用不稳定,挑电脑。
C#自带的串口类有可能死锁等,也不好使。
后来 发现只有工业级PCOMM满足我的要求,PCOMM是线程安全的,不用担心不稳定,使用了几年了非常稳定(开到96个串口读写线程依然稳定),而且占用CPU低,特分享C#调用PCOMM的代码互相学习。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;
using System.Collections;

namespace PCommApp
{
public class PComm
{
[DllImport(“PComm.dll”, EntryPoint = “sio_open”)]
public static extern int sio_open(int port);

    [DllImport("PComm.dll", EntryPoint = "sio_ioctl")]
    public static extern int sio_ioctl(int port, int baud, int mode);

    [DllImport("PComm.dll", EntryPoint = "sio_DTR")]
    public static extern int sio_DTR(int port, int mode);

    [DllImport("PComm.dll", EntryPoint = "sio_RTS")]
    public static extern int sio_RTS(int port, int mode);

    [DllImport("PComm.dll", EntryPoint = "sio_close")]
    public static extern int sio_close(int port);

    [DllImport("PComm.dll", EntryPoint = "sio_read")]
    public static extern int sio_read(int port, ref byte buf, int length);

    [DllImport("PComm.dll", EntryPoint = "sio_write")]
    public static extern int sio_write(int port,ref byte buf, int length);

    [DllImport("PComm.dll", EntryPoint = "sio_SetReadTimeouts")]
    public static extern int sio_SetReadTimeouts(int port, int TotalTimeouts, int IntervalTimeouts);

    [DllImport("PComm.dll", EntryPoint = "sio_SetWriteTimeouts")]
    public static extern int sio_SetWriteTimeouts(int port,int TotalTimeouts,int IntervalTimeouts);

    [DllImport("PComm.dll", EntryPoint = "sio_AbortRead")]
    public static extern int sio_AbortRead(int port);

    [DllImport("PComm.dll", EntryPoint = "sio_AbortWrite")]
    public static extern int sio_AbortWrite(int port);

    [DllImport("PComm.dll", EntryPoint = "sio_getmode")]
    public static extern int sio_getmode(int port);

    [DllImport("PComm.dll", EntryPoint = "sio_getbaud")]
    public static extern int sio_getbaud(int port);

    [DllImport("PComm.dll", EntryPoint = "sio_flowctrl")]
    public static extern int sio_flowctrl(int port,int mode);

    // Function return error code
    private const int SIO_OK = 0;
    private const int SIO_BADPORT = -1;
    private const int SIO_OUTCONTROL = -2;
    private const int SIO_NODATA = -4;
    private const int SIO_OPENFAIL = -5;
    private const int SIO_RTS_BY_HW = -6;
    private const int SIO_BADPARAM = -7;
    private const int SIO_WIN32FAIL = -8;
    private const int SIO_BOARDNOTSUPPORT = -9;
    private const int SIO_ABORT_WRITE = -11;
    private const int SIO_WRITETIMEOUT = -12;

    // Self Define function return error code
    private const int ERR_NOANSWER = -101;

    // Baud rate
    private const int B50 = 0x0;
    private const int B75 = 0x1;
    private const int B110 = 0x2;
    private const int B134 = 0x3;
    private const int B150 = 0x4;
    private const int B300 = 0x5;
    private const int B600 = 0x6;
    private const int B1200 = 0x7;
    private const int B1800 = 0x8;
    private const int B2400 = 0x9;
    private const int B4800 = 0xA;
    private const int B7200 = 0xB;
    private const int B9600 = 0xC;
    private const int B19200 = 0xD;
    private const int B38400 = 0xE;
    private const int B57600 = 0xF;
    private const int B115200 = 0x10;
    private const int B230400 = 0x11;
    private const int B460800 = 0x12;
    private const int B921600 = 0x13;

    // Mode setting Data bits define
    private const int BIT_5 = 0x0;                
    private const int BIT_6 = 0x1;
    private const int BIT_7 = 0x2;
    private const int BIT_8 = 0x3;

    // Mode setting Stop bits define
    private const int STOP_1 = 0x0;               
    private const int STOP_2 = 0x4;

    // Mode setting Parity define
    private const int P_EVEN = 0x18;               
    private const int P_ODD = 0x8;
    private const int P_SPC = 0x38;
    private const int P_MRK = 0x28;
    private const int P_NONE = 0x0;

    // Private Key name
    private const string KEY_PORT = "Port";
    private const string KEY_BAUDRATE = "Baud_Rate";
    private const string KEY_PARITY = "Parity";
    private const string KEY_BYTESIZE = "Byte_Size";
    private const string KEY_STOPBITS = "Stop_Bits";
    private const string KEY_BEFOREDELAY = "Before_Delay";
    private const string KEY_BYTEDELAY = "Byte_Delay";
    private const string KEY_READINTERVALTIMEOUT = "Read_Interval_Timeout";
    private const string KEY_AFTERDELAY = "After_Delay";

    // Port param
    private int Gl_Int_Port=1;
    private int Gl_Int_Baudrate = B9600;
    private int Gl_Int_Parity = P_NONE;
    private int Gl_Int_ByteSize = BIT_8;
    private int Gl_Int_StopBits = STOP_1;

    // Delay param
    private int Gl_Int_BeforeDelay = 0;
    private int Gl_Int_ByteDelay = 0;
    private int Gl_Int_ReadIntervalTimeout = 50;
    private int Gl_Int_AfterDealy = 3000;

    /// <summary>
    /// 解析通讯参数
    /// </summary>
    /// <param name="Hb_CommParam"></param>
    private void AnalyseCommParam(Hashtable Ht_CommParam)
    {
        //Port
        Gl_Int_Port=int.Parse(Ht_CommParam[KEY_PORT].ToString());
        //Baud rate
        if (Ht_CommParam.Contains(KEY_BAUDRATE))
        {
            switch (Ht_CommParam[KEY_BAUDRATE].ToString()) 
            {
                case "50": Gl_Int_Baudrate = B50; break;
                case "75": Gl_Int_Baudrate = B75; break;
                case "110": Gl_Int_Baudrate = B110; break;
                case "134": Gl_Int_Baudrate = B134; break;
                case "150":Gl_Int_Baudrate = B150; break;
                case "300":Gl_Int_Baudrate = B300; break;
                case "600":Gl_Int_Baudrate = B600; break;
                case "1200":Gl_Int_Baudrate = B1200; break;
                case "1800":Gl_Int_Baudrate = B1800; break;
                case "2400":Gl_Int_Baudrate = B2400; break;
                case "4800":Gl_Int_Baudrate = B4800; break;
                case "7200":Gl_Int_Baudrate = B7200; break;
                case "9600":Gl_Int_Baudrate = B9600; break;
                case "19200": Gl_Int_Baudrate = B19200; break;
                case "38400": Gl_Int_Baudrate = B38400; break;
                case "57600": Gl_Int_Baudrate = B57600; break;
                case "115200": Gl_Int_Baudrate = B115200; break;
                case "230400": Gl_Int_Baudrate = B230400; break;
                case "460800": Gl_Int_Baudrate = B460800; break;
                case "921600": Gl_Int_Baudrate = B921600; break;
                default: Gl_Int_Baudrate = B9600; break;
            }
        }
        //Parity
        if (Ht_CommParam.Contains(KEY_PARITY))
        {
            switch (Ht_CommParam[KEY_PARITY].ToString())
            {
                case "Even": Gl_Int_Parity = P_EVEN; break;
                case "Odd": Gl_Int_Parity = P_ODD; break;
                case "Space": Gl_Int_Parity = P_SPC; break;
                case "Mark": Gl_Int_Parity = P_MRK; break;
                case "None": Gl_Int_Parity = P_NONE; break;
                default: Gl_Int_Parity = P_NONE; break;
            }
        }
        //Byte Size
        if (Ht_CommParam.Contains(KEY_BYTESIZE))
        {
            switch (Ht_CommParam[KEY_BYTESIZE].ToString())
            {
                case "5": Gl_Int_ByteSize = BIT_5; break;
                case "6": Gl_Int_ByteSize = BIT_6; break;
                case "7": Gl_Int_ByteSize = BIT_7; break;
                case "8": Gl_Int_ByteSize = BIT_8; break;
                default: Gl_Int_ByteSize = BIT_8; break;
            }
        }
        //Stop Bits
        if (Ht_CommParam.Contains(KEY_STOPBITS))
        {
            switch (Ht_CommParam[KEY_STOPBITS].ToString())
            {
                case "1": Gl_Int_StopBits = STOP_1; break;
                case "2": Gl_Int_StopBits = STOP_2; break;
                default: Gl_Int_StopBits = STOP_1; break;
            }
        }
        //Before Delay
        if (Ht_CommParam.Contains(KEY_BEFOREDELAY))
        {
            int.TryParse(Ht_CommParam[KEY_BEFOREDELAY].ToString(),out Gl_Int_BeforeDelay);
        }
        //Byte Delay
        if (Ht_CommParam.Contains(KEY_BYTEDELAY))
        {
            int.TryParse(Ht_CommParam[KEY_BYTEDELAY].ToString(), out Gl_Int_ByteDelay);
        }
        //Read Interval Timeout
        if (Ht_CommParam.Contains(KEY_READINTERVALTIMEOUT))
        {
            int.TryParse(Ht_CommParam[KEY_READINTERVALTIMEOUT].ToString(), out Gl_Int_ReadIntervalTimeout);
        }
        //After Delay
        if (Ht_CommParam.Contains(KEY_AFTERDELAY))
        {
            int.TryParse(Ht_CommParam[KEY_AFTERDELAY].ToString(),out Gl_Int_AfterDealy);
        }
    }

    /// <summary>
    /// 初始化串口通讯
    /// </summary>
    /// <param name="Hb_CommParam"></param>
    /// <returns>错误码</returns>
    public int InitComm(Hashtable Ht_CommParam)
    {
        AnalyseCommParam(Ht_CommParam);

        //Open port
        int i_RtnCode = sio_open(Gl_Int_Port);
        if (i_RtnCode != SIO_OK)
        {
            return i_RtnCode;
        }

        //Configure communication parameters
        int mode=Gl_Int_Parity | Gl_Int_ByteSize | Gl_Int_StopBits;
        i_RtnCode = sio_ioctl(Gl_Int_Port,Gl_Int_Baudrate,mode);
        if (i_RtnCode != SIO_OK)
        {
            return i_RtnCode;
        }

        //Flow control
        i_RtnCode = sio_flowctrl(Gl_Int_Port, 0);
        if (i_RtnCode != SIO_OK)
        {
            return i_RtnCode;
        }

        //DTR
        i_RtnCode = sio_DTR(Gl_Int_Port, 1);
        if (i_RtnCode != SIO_OK)
        {
            return i_RtnCode;
        }

        //RTS
        i_RtnCode = sio_RTS(Gl_Int_Port, 1);
        if (i_RtnCode != SIO_OK)
        {
            return i_RtnCode;
        }

        //Set timeout values for sio_read 
        sio_SetReadTimeouts(Gl_Int_Port,Gl_Int_AfterDealy,Gl_Int_ReadIntervalTimeout);
        return 0;
    }

    /// <summary>
    /// 发生十六进值字符串帧,
    /// </summary>
    /// <param name="Str_SendFrame">发送帧</param>
    /// <param name="Str_RecFrame">接受帧</param>
    /// <param name="i_NewBaudrate">新的波特率</param>
    /// <returns>错误码</returns>
    public int SendFrame(string Str_SendFrame, ref string Str_RecFrame, int i_NewBaudrate = 0)
    {
        int i_RtnCode;
        byte[] buffer=new byte[1024];
        byte[] recbuffer = new byte[4096];
        Str_SendFrame = Str_SendFrame.Replace(" ", "");
        int i_FrameLen = Str_SendFrame.Length / 2;
        for (int i = 0; i <= i_FrameLen - 1; i++)
        {
            buffer[i]=Convert.ToByte(Str_SendFrame.Substring(i*2,2),16);
        }

        //Write data
        if (Gl_Int_ByteDelay == 0)
        {
            i_RtnCode = sio_write(Gl_Int_Port,ref buffer[0], i_FrameLen);
            if (i_RtnCode < 0)
            {
                return i_RtnCode;
            }
        }
        else
        {
            for (int i = 0; i <= i_FrameLen - 1; i++)
            {
                i_RtnCode = sio_write(Gl_Int_Port, ref buffer[i], 1);
                if (i_RtnCode < 0)
                {
                    return i_RtnCode;
                }
                Thread.Sleep(Gl_Int_ByteDelay);
            }
        }

        //Change baudrate
        if (i_NewBaudrate < 0) //No need answer
        {
            return 0;
        }
        else if (i_NewBaudrate > 0) //Need change baudrate 
        {
            int mode = sio_getmode(Gl_Int_Port);
            int baud = sio_getbaud(Gl_Int_Port);
            if (baud != i_NewBaudrate)
            {
                Thread.Sleep(180);
                sio_ioctl(Gl_Int_Port, i_NewBaudrate, mode);
            }
        }

        //Read data
        i_RtnCode = sio_read(Gl_Int_Port, ref recbuffer[0], 4096);
        if (i_RtnCode < 0)
        {
            return i_RtnCode;
        }
        else if (i_RtnCode == 0)
        {
            return ERR_NOANSWER;
        }
        int i_RecFrameLen = i_RtnCode;
        StringBuilder Sb_RecFrame = new StringBuilder();
        for (int i = 0; i <= i_RecFrameLen - 1; i++)
        {
            Sb_RecFrame.Append(recbuffer[i].ToString("X"));
            Sb_RecFrame.Append(" ");
        }
        Str_RecFrame = Sb_RecFrame.ToString().Trim();
        return 0;
    }

    /// <summary>
    /// 关闭串口通讯
    /// </summary>
    /// <returns>错误码</returns>
    public int CloseComm()
    {
        int i_RtnCode=sio_close(Gl_Int_Port);
        if (i_RtnCode!=SIO_OK)
        {
            return i_RtnCode;
        }
        return 0;
    }

    /// <summary>
    /// 获取通讯错误消息
    /// </summary>
    /// <param name="i_ErrCode">错误码</param>
    /// <returns>错误消息</returns>
    public string GetCommErrMsg(int i_ErrCode)
    {
        switch (i_ErrCode)
        {
            case SIO_OK: return "成功";
            case SIO_BADPORT: return "串口号无效,检测串口号!";
            case SIO_OUTCONTROL: return "主板不是MOXA兼容的智能主板!";
            case SIO_NODATA: return "没有可读的数据!";
            case SIO_OPENFAIL: return "打开串口失败,检查串口是否被占用!";
            case SIO_RTS_BY_HW: return "不能控制串口因为已经通过sio_flowctrl设定为自动H/W流控制";
            case SIO_BADPARAM: return "串口参数错误,检查串口参数!";
            case SIO_WIN32FAIL: return "调用Win32函数失败!";
            case SIO_BOARDNOTSUPPORT: return "串口不支持这个函数!";
            case SIO_ABORT_WRITE:return "用户终止写数据块!";
            case SIO_WRITETIMEOUT:return "写数据超时!";
            case ERR_NOANSWER: return "无应答!";
            default: return i_ErrCode.ToString();
        }
    }
}

}

示列代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections;

namespace PCommApp
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

    private void btnSend_Click(object sender, EventArgs e)
    {
        btnSend.Enabled = false;
        try
        {
            Hashtable Ht = new Hashtable();
            Ht.Add("Port", txbPort.Text.ToString());
            Ht.Add("Baud_Rate", "9600");
            Ht.Add("Parity", "None");
            Ht.Add("Byte_Size", "8");
            Ht.Add("Stop_Bits", "1");
            Ht.Add("Before_Delay", "0");
            Ht.Add("Byte_Delay", "50");
            Ht.Add("Read_Interval_Timeout", "50");
            Ht.Add("After_Delay", "3000");

            string Str_SendFrame = txbSendFrame.Text.Trim();
            string Str_RecFrame = string.Empty;

            PComm SP = new PComm();
            //初始化串口
            int i_RtnCode = SP.InitComm(Ht);
            if (i_RtnCode != 0)
            {
                MessageBox.Show(SP.GetCommErrMsg(i_RtnCode));
                return;
            }

            try
            {
                //发送数据
                ShowDebugMsg("发送: " + Str_SendFrame, true);
                i_RtnCode = SP.SendFrame(Str_SendFrame, ref Str_RecFrame, 0);
                if (i_RtnCode != 0)
                {
                    ShowDebugMsg(SP.GetCommErrMsg(i_RtnCode), false);
                    return;
                }
                ShowDebugMsg("接收: " + Str_RecFrame, true);
            }
            finally
            {
                //关闭串口
                SP.CloseComm();
            }
        }
        finally
        {
            btnSend.Enabled = true;
        }
    }

    /// <summary>
    /// 显示调试消息
    /// </summary>
    /// <param name="Str_ShowMsg"></param>
    /// <param name="B_IsFrame"></param>
    private void ShowDebugMsg(string Str_ShowMsg,Boolean B_IsFrame)
    {
        if (B_IsFrame)
        {
            Str_ShowMsg = DateTime.Now.ToString()+ " " + Str_ShowMsg;
        }
        txtDebug.AppendText(Str_ShowMsg+"\r\n");
        txtDebug.ScrollToCaret();
        txtDebug.Refresh();
    }
}

}

完整代码资源链接: 这里写代码片http://download.csdn.net/detail/a295281315/9766510

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值