serialport.cpp与serialport.h类文件源代码

该博客主要介绍了C++中用于串口通信的serialport.cpp和serialport.h类文件的源代码,包括初始化串口、设置参数、错误处理、线程同步和事件监听等功能。类库提供了串口打开、关闭、数据读写以及串口状态监控等操作,适用于嵌入式和设备控制等场景。
摘要由CSDN通过智能技术生成

serialport.cpp

#include "stdafx.h"
#include "SerialPort.h"

#include <assert.h>

int m_nComArray[20];//存放活跃的串口号
//
// Constructor
//
CSerialPort::CSerialPort()
{
    m_hComm = NULL;

    // initialize overlapped structure members to zero
    ///初始化异步结构体
    m_ov.Offset = 0;
    m_ov.OffsetHigh = 0;

    // create events
    m_ov.hEvent = NULL;
    m_hWriteEvent = NULL;
    m_hShutdownEvent = NULL;

    m_szWriteBuffer = NULL;

    m_bThreadAlive = FALSE;
    m_nWriteSize = 1;
    m_bIsSuspened = FALSE;
}

//
// Delete dynamic memory
//
CSerialPort::~CSerialPort()
{
    MSG message;

    //增加线程挂起判断,解决由于线程挂起导致串口关闭死锁的问题 add by itas109 2016-06-29
    if (IsThreadSuspend(m_Thread))
    {
        ResumeThread(m_Thread);
    }

    //串口句柄无效  add by itas109 2016-07-29
    if (m_hComm == INVALID_HANDLE_VALUE)
    {
        CloseHandle(m_hComm);
        m_hComm = NULL;
        return;
    }

    do
    {
        SetEvent(m_hShutdownEvent);
        //add by liquanhai  防止死锁  2011-11-06
        if (::PeekMessage(&message, m_pOwner, 0, 0, PM_REMOVE))
        {
            ::TranslateMessage(&message);
            ::DispatchMessage(&message);
        }
    } while (m_bThreadAlive);

    // if the port is still opened: close it 
    if (m_hComm != NULL)
    {
        CloseHandle(m_hComm);
        m_hComm = NULL;
    }
    // Close Handles  
    if (m_hShutdownEvent != NULL)
        CloseHandle(m_hShutdownEvent);
    if (m_ov.hEvent != NULL)
        CloseHandle(m_ov.hEvent);
    if (m_hWriteEvent != NULL)
        CloseHandle(m_hWriteEvent);

    //TRACE("Thread ended\n");

    if (m_szWriteBuffer != NULL)
    {
        delete[] m_szWriteBuffer;
        m_szWriteBuffer = NULL;
    }
}

//
// Initialize the port. This can be port 1 to MaxSerialPortNum.
///初始化串口。只能是1-MaxSerialPortNum
//
//parity:
//  n=none
//  e=even
//  o=odd
//  m=mark
//  s=space
//data:
//  5,6,7,8
//stop:
//  1,1.5,2 
//
BOOL CSerialPort::InitPort(HWND pPortOwner,    // the owner (CWnd) of the port (receives message)
    UINT  portnr,        // 串口号 (1..MaxSerialPortNum)
    UINT  baud,            // 速率
    TCHAR  parity,        // parity 
    UINT  databits,        // databits 
    UINT  stopbits,        // stopbits 
    DWORD dwCommEvents,    // EV_RXCHAR, EV_CTS etc
    UINT  writebuffersize,// size to the writebuffer

    DWORD   ReadIntervalTimeout,
    DWORD   ReadTotalTimeoutMultiplier,
    DWORD   ReadTotalTimeoutConstant,
    DWORD   WriteTotalTimeoutMultiplier,
    DWORD   WriteTotalTimeoutConstant)

{
    assert(portnr > 0 && portnr < MaxSerialPortNum);
    assert(pPortOwner != NULL);

    MSG message;

    //增加线程挂起判断,解决由于线程挂起导致串口关闭死锁的问题 add by itas109 2016-06-29
    if (IsThreadSuspend(m_Thread))
    {
        ResumeThread(m_Thread);
    }

    // if the thread is alive: Kill
    if (m_bThreadAlive)
    {
        do
        {
            SetEvent(m_hShutdownEvent);
            //add by liquanhai  防止死锁  2011-11-06
            if (::PeekMessage(&message, m_pOwner, 0, 0, PM_REMOVE))
            {
                ::TranslateMessage(&message);
                ::DispatchMessage(&message);
            }
        } while (m_bThreadAlive);
        //TRACE("Thread ended\n");
        //此处的延时很重要,因为如果串口开着,发送关闭指令到彻底关闭需要一定的时间,这个延时应该跟电脑的性能相关
        Sleep(50);//add by itas109 2016-08-02
    }

    // create events
    if (m_ov.hEvent != NULL)

SerialPort.h

#ifndef __SERIALPORT_H__
#define __SERIALPORT_H__

#include "stdio.h"
#include<windows.h>

struct serialPortInfo
{
    UINT portNr;//串口号
    DWORD bytesRead;//读取的字节数
};

#ifndef Wm_SerialPort_MSG_BASE 
#define Wm_SerialPort_MSG_BASE        WM_USER + 617        //!< 消息编号的基点  
#endif

#define Wm_SerialPort_BREAK_DETECTED    Wm_SerialPort_MSG_BASE + 1    // A break was detected on input.
#define Wm_SerialPort_CTS_DETECTED        Wm_SerialPort_MSG_BASE + 2    // The CTS (clear-to-send) signal changed state. 
#define Wm_SerialPort_DSR_DETECTED        Wm_SerialPort_MSG_BASE + 3    // The DSR (data-set-ready) signal changed state. 
#define Wm_SerialPort_ERR_DETECTED        Wm_SerialPort_MSG_BASE + 4    // A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY. 
#define Wm_SerialPort_RING_DETECTED        Wm_SerialPort_MSG_BASE + 5    // A ring indicator was detected. 
#define Wm_SerialPort_RLSD_DETECTED        Wm_SerialPort_MSG_BASE + 6    // The RLSD (receive-line-signal-detect) signal changed state. 
#define Wm_SerialPort_RXCHAR            Wm_SerialPort_MSG_BASE + 7    // A character was received and placed in the input buffer. 
#define Wm_SerialPort_RXFLAG_DETECTED    Wm_SerialPort_MSG_BASE + 8    // The event character was received and placed in the input buffer.  
#define Wm_SerialPort_TXEMPTY_DETECTED    Wm_SerialPort_MSG_BASE + 9    // The last character in the output buffer was sent.  
#define Wm_SerialPort_RXSTR             Wm_SerialPort_MSG_BASE + 10   // Receive string

#define MaxSerialPortNum 200   ///有效的串口总个数,不是串口的号 //add by itas109 2014-01-09
#define IsReceiveString  0     //采用何种方式接收:ReceiveString 1多字符串接收(对应响应函数为Wm_SerialPort_RXSTR),ReceiveString 0一个字符一个字符接收(对应响应函数为Wm_SerialPort_RXCHAR)
class CSerialPort
{
public:
    // contruction and destruction
    CSerialPort();
    virtual        ~CSerialPort();

    // port initialisation        
    // UINT stopsbits = ONESTOPBIT   stop is index 0 = 1 1=1.5 2=2 
    // 切记:stopsbits = 1,不是停止位为1。
    // by itas109 20160506
    BOOL        InitPort(HWND pPortOwner, UINT portnr = 1, UINT baud = 9600,
        TCHAR parity = _T('N'), UINT databits = 8, UINT stopsbits = ONESTOPBIT,
        DWORD dwCommEvents = EV_RXCHAR | EV_CTS, UINT nBufferSize = 512,

        DWORD ReadIntervalTimeout = 1000,
        DWORD ReadTotalTimeoutMultiplier = 1000,
        DWORD ReadTotalTimeoutConstant = 1000,
        DWORD WriteTotalTimeoutMultiplier = 1000,
        DWORD WriteTotalTimeoutConstant = 1000);

    // start/stop comm watching
    ///控制串口监视线程
    BOOL         StartMonitoring();//开始监听
    BOOL         ResumeMonitoring();//恢复监听
    BOOL         SuspendMonitoring();//挂起监听
    BOOL         IsThreadSuspend(HANDLE hThread);//判断线程是否挂起 //add by itas109 2016-06-29

    DWORD         GetWriteBufferSize();///获取写缓冲大小
    DWORD         GetCommEvents();///获取事件
    DCB             GetDCB();///获取DCB

    ///写数据到串口
    void        WriteToPort(char* string, size_t n); // add by mrlong 2007-12-25
    void        WriteToPort(PBYTE Buffer, size_t n);// add by mrlong
    void        ClosePort();                     // add by mrlong 2007-12-2  
    BOOL        IsOpen();

    void QueryKey(HKEY hKey);///查询注册表的串口号,将值存于数组中

#ifdef _AFX
    void Hkey2ComboBox(CComboBox& m_PortNO);///将QueryKey查询到的串口号添加到CComboBox控件中
#endif // _AFX

protected:
    // protected memberfunctions
    void        ProcessErrorMessage(TCHAR* ErrorText);///错误处理
    static DWORD WINAPI CommThread(LPVOID pParam);///线程函数
    static void    ReceiveChar(CSerialPort* port);
    static void ReceiveStr(CSerialPort* port); //add by itas109 2016-06-22
    static void    WriteChar(CSerialPort* port);

    // thread
    HANDLE                m_Thread;
    BOOL                m_bIsSuspened;///thread监视线程是否挂起

    // synchronisation objects
    CRITICAL_SECTION    m_csCommunicationSync;///临界资源
    BOOL                m_bThreadAlive;///监视线程运行标志

    // handles
    HANDLE                m_hShutdownEvent;  //stop发生的事件
    HANDLE                m_hComm;           // 串口句柄 
    HANDLE                m_hWriteEvent;     // write

OVERLAPPED            m_ov;///异步I/O
    COMMTIMEOUTS        m_SerialPortTimeouts;///超时设置
    DCB                    m_dcb;///设备控制块

    // owner window
    HWND                m_pOwner;

    // misc
    UINT                m_nPortNr;        ///串口号
    PBYTE                m_szWriteBuffer;///写缓冲区
    DWORD                m_dwCommEvents;
    DWORD                m_nWriteBufferSize;///写缓冲大小

    size_t                m_nWriteSize;//写入字节数 //add by mrlong 2007-12-25
};

#endif __SERIALPORT_H__
 

        ResetEvent(m_ov.hEvent);
    else
        m_ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    if (m_hWriteEvent != NULL)
        ResetEvent(m_hWriteEvent);
    else
        m_hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    if (m_hShutdownEvent != NULL)
        ResetEvent(m_hShutdownEvent);
    else
        m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    // initialize the event objects
    ///事件数组初始化,设定优先级别
    m_hEventArray[0] = m_hShutdownEvent;    // highest priority
    //为避免有些串口设备无数据输入,但一直返回读事件,使监听线程阻塞,
    //可以将读写放在两个线程中,或者修改读写事件优先级
    //修改优先级有两个方案:
    //方案一为监听线程中WaitCommEvent()后,添加如下两条语句:
    //if (WAIT_OBJECT_O == WaitForSingleObject(port->m_hWriteEvent, 0))
    //    ResetEvent(port->m_ov.hEvent);
    //方案二为初始化时即修改,即下面两条语句:
    m_hEventArray[1] = m_hWriteEvent;
    m_hEventArray[2] = m_ov.hEvent;


    // initialize critical section
    ///初始化临界资源
    InitializeCriticalSection(&m_csCommunicationSync);

    // set buffersize for writing and save the owner
    m_pOwner = pPortOwner;

    if (m_szWriteBuffer != NULL)
    {
        delete[] m_szWriteBuffer;
        m_szWriteBuffer = NULL;
    }
    m_szWriteBuffer = new BYTE[writebuffersize];

    m_nPortNr = portnr;

    m_nWriteBufferSize = writebuffersize;
    m_dwCommEvents = dwCommEvents;

    BOOL bResult = FALSE;
    TCHAR *szPort = new TCHAR[MAX_PATH];
    TCHAR *szBaud = new TCHAR[MAX_PATH];

    /*
    多个线程操作相同的数据时,一般是需要按顺序访问的,否则会引导数据错乱,
    无法控制数据,变成随机变量。为解决这个问题,就需要引入互斥变量,让每
    个线程都按顺序地访问变量。这样就需要使用EnterCriticalSection和
    LeaveCriticalSection函数。
    */
    // now it critical!
    EnterCriticalSection(&m_csCommunicationSy

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值