CSerialPort类解析

CSerialPort类解析(一) 

CserialPort类的功能及成员函数介绍

CserialPort类是免费提供的串口累,Codeguru是一个非常不错的源代码网站

CserialPort类支持线连接(非MODEM)的串口编程操作。

CserialPort类是基于多线程的,其工作流程如下:首先设置好串口参数,再开启串口检测工作线程,串口检测工作线程检测到串口接收到的数据、流控制事件或其他串口事件后,就以消息方式通知主程序,激发消息处理函数来进行数据处理,这是对接受数据而言的,发送数据可直接向串口发送。

CserialPort类定义的消息如表

消息名称

消息号

功能说明

WM_COMM_BREAK_DETECTED

WM_USER+1

检测到输入中断

WM_COMM_CTS_DETECTED

WM_USER+2

检测到CTS(清除发送)信号状态改变

WM_COMM_DSR_DETECTED

WM_USER+3

检测到DSR(数据设备准备就绪)信号状态改变

WM_COMM_ERR_DETECTED

WM_USER+4

发生线状态错误(包括CE_FRAMECE_OVERRUN,和CE_RXPARITY

WM_COMM_RING_DETECTED

WM_USER+5

检测到响铃指示信号

WM_COMM_RLSD_DETECTED

WM_USER+6

检测到RLSD(接收线信号)状态改变

WM_COMM_RXCHAR

WM_USER+7

接收到一个字符并已放入接受缓冲区

WM_COMM_RXFLAG_DETECTED

WM_USER+8

检测到接受到字符(该字符已放入接受缓冲区)事件

WM_COMM_TXEMPTY_DETECTED

WM_USER+9

检测到发送缓冲区最后一个字符已经被发送


CSerialPort类解析(二)

介绍几个经常用到的函数:
1、串口初始化函数InitPort
BOOL CSerialPort::InitPort(CWnd *pPortOwner,    // the owner (CWnd) of the port (receives message) 
                           UINT  portnr,        // portnumber (1..4) 
                           UINT  baud,            // baudrate 
                           char  parity,        // parity 
                           UINT  databits,        // databits 
                           UINT  stopbits,        // stopbits 
                           DWORD dwCommEvents,    // EV_RXCHAR, EV_CTS etc 
                           UINT  writebuffersize)    // size to the writebuffer 

    assert(portnr > 0 && portnr < 5); 
    assert(pPortOwner != NULL); 
 
    // if the thread is alive: Kill 
    if (m_bThreadAlive) 
    { 
        do 
        { 
            SetEvent(m_hShutdownEvent); 
        } 
        while (m_bThreadAlive); 
        TRACE("Thread ended\n"); 
    } 
 
    // create events 
    if (m_ov.hEvent != NULL) 
        ResetEvent(m_ov.hEvent); 
    m_ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 
 
    if (m_hWriteEvent != NULL) 
        ResetEvent(m_hWriteEvent); 
    m_hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 
 
    if (m_hShutdownEvent != NULL) 
        ResetEvent(m_hShutdownEvent); 
    m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 
 
    // initialize the event objects 
    m_hEventArray[0] = m_hShutdownEvent;    // highest priority 
    m_hEventArray[1] = m_ov.hEvent; 
    m_hEventArray[2] = m_hWriteEvent; 
 
    // 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 = new char[writebuffersize]; 
 
    m_nPortNr = portnr; 
 
    m_nWriteBufferSize = writebuffersize; 
    m_dwCommEvents = dwCommEvents; 
 
    BOOL bResult = FALSE; 
    char *szPort = new char[50]; 
    char *szBaud = new char[50]; 
 
    // now it critical! 
    EnterCriticalSection(&m_csCommunicationSync); 
 
    // if the port is already opened: close it 
    if (m_hComm != NULL) 
    { 
        CloseHandle(m_hComm); 
        m_hComm = NULL; 
    } 
 
    // prepare port strings 
    sprintf(szPort, "COM%d", portnr); 
    sprintf(szBaud, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, stopbits); 
 
    // get a handle to the port 
    m_hComm = CreateFile(szPort,                        // communication port string (COMX) 
                         GENERIC_READ | GENERIC_WRITE,    // read/write types 
                         0,                                // comm devices must be opened with exclusive access 
                         NULL,                            // no security attributes 
                         OPEN_EXISTING,                    // comm devices must use OPEN_EXISTING 
                         FILE_FLAG_OVERLAPPED,            // Async I/O 
                         0);                            // template must be 0 for comm devices 
 
    if (m_hComm == INVALID_HANDLE_VALUE) 
    { 
        // port not found 
        delete [] szPort; 
        delete [] szBaud; 
 
        return FALSE; 
    } 
 
    // set the timeout values 
    m_CommTimeouts.ReadIntervalTimeout = 1000
    m_CommTimeouts.ReadTotalTimeoutMultiplier = 1000
    m_CommTimeouts.ReadTotalTimeoutConstant = 1000
    m_CommTimeouts.WriteTotalTimeoutMultiplier = 1000
    m_CommTimeouts.WriteTotalTimeoutConstant = 1000
 
    // configure 
    if (SetCommTimeouts(m_hComm, &m_CommTimeouts)) 
    { 
        if (SetCommMask(m_hComm, dwCommEvents)) 
        { 
            if (GetCommState(m_hComm, &m_dcb)) 
            { 
                m_dcb.fRtsControl = RTS_CONTROL_ENABLE;        // set RTS bit high! 
                if (BuildCommDCB(szBaud, &m_dcb)) 
                { 
                    if (SetCommState(m_hComm, &m_dcb)) 
                        ; // normal operation... continue 
                    else 
                        ProcessErrorMessage("SetCommState()"); 
                } 
                else 
                    ProcessErrorMessage("BuildCommDCB()"); 
            } 
            else 
                ProcessErrorMessage("GetCommState()"); 
        } 
        else 
            ProcessErrorMessage("SetCommMask()"); 
    } 
    else 
        ProcessErrorMessage("SetCommTimeouts()"); 
 
    delete [] szPort; 
    delete [] szBaud; 
 
    // flush the port 
    PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT); 
 
    // release critical section 
    LeaveCriticalSection(&m_csCommunicationSync); 
 
    TRACE("Initialisation for communicationport %d completed.\nUse Startmonitor to communicate.\n", portnr); 
 
    return TRUE; 

 
 这个函数是用来初始化串口的,即设置串口的通信参数:需要打开的串口号、波特率、奇偶校验方式、数据位、停止位,这里还可 以用来进行事件的设定

如果串口初始化成功,就返回TRUE,若串口被其他设备占用、不存在或存在其他股占,就返回FALSE,编程者可以在这儿提示串口操作是否成功

如果在当前主串口调用这个函数,那么pPortOwner可用this指针表示,串口号在函数中做了限制,只能用1234四个串口号,而事实上在编程时可能用到更多串口号,可以通过通过注释掉本函数中的“assert(portur>0&&portnr<5)”语句取消对串口号的限制


CSerialPort类解析(三)

2、启动串口通信监测线程函数StartMonitoring()

串口初始化成功后,就可以调用BOOL StartMonitoring()来启动串口检测线程,线程启动成功,发挥TRUE

BOOL CSerialPort::StartMonitoring() 

    if (!(m_Thread = AfxBeginThread(CommThread, this))) 
        return FALSE; 
    TRACE("Thread started\n"); 
    return TRUE; 

3、暂停或停止监测线程函数StopMonitoring()

该函数暂停或停止串口检测,要注意的是,调用该函数后,串口资源仍然被占用// 
// Suspend the comm thread 
// 
BOOL CSerialPort::StopMonitoring() 

     TRACE("Thread suspended\n"); 
     m_Thread->SuspendThread(); 
     return TRUE; 

4、关闭串口函数ClosePort()

该函数功能是关闭串口,释放串口资源,调用该函数后,如果要继续使用串口,还需要调用InitPort()函数

5、通过串口发送字符/写串口函数WriteToPort()

该函数完成写串口功能,即向串口发送字符。

// 
// Write a string to the port 
// 
void CSerialPort::WriteToPort(char *string) 

     assert(m_hComm != 0); 
 
     memset(m_szWriteBuffer, 0sizeof(m_szWriteBuffer)); 
     strcpy(m_szWriteBuffer, string); 
 
     // set event for write 
     SetEvent(m_hWriteEvent); 

以上是常用的函数介绍,熟悉该类的使用后,可以仔细看看其他函数,对上面介绍的函数,在对串口资源的使用上要记住一下三点:

打开串口用调用InitPort()和StartMonitoring();关闭串口用StopMonitoring()和ClosePort()而且以上函数的调用顺序不能乱

通过串口发送字符调用函数WriteToPort()

接受串口收到的字符需要自己编写WM_COMM_RXCHAR消息处理函数,需要手工添加,


CSerialPort类解析(四)  

操作:

首先,需要操作一个串口,所以只需要定义1个类对象就可以了,如要操作多个串口,则要为每个串口均定一个类对象,这可以通过数据方式来实现,这里定义的类对象为m_SerialPort,再定义一个布尔变量m_bSerialPortOpened用来标志串口是否打开。

CserialPort类中有多个串口事件可以响应,在一般串口编程中,只需要处理WM_COMM_RXCHAR消息就可以了,该类所有的消息均需要人工添加消息处理函数,将处理函数名定义为OnComm(),首先在SerialPortTestDlg.h(头文件)中添加串口字符接受消息WM_COMM_RXCHAR(串口接受缓冲区内有一个字符)的响应函数声明:

//Generated message map funnctions 
//{{AFX_MSG(CSCportTestView) 
afx_msg long OnComm(WPARAM ch, LPARAM port); 
//}}AFX_MSG 
 

然后在,SerilPortTestDlg.cpp文件中进行WM_COMM_RXCHAR消息映射

BEGIN_MESSAE_MAP(CSerialPortTestDlg, CDialog)s 
//{{AFX_MSG_MAP(CSerialPortTestDlg) 
ON_MESSAGE(WM_COMM_RXCHAR, OnComm) 
//}}AFX_MSG_MAP 
END_MESSAGE_MAP() 
 接着,在SerialPortTestDlg.cpp文件中加入函数OnComm()的实现,并在其中完成对节诶受到的字符的处理,将接收到的字符显示在接受编辑框中:

long CSerialPortTestDlg::OnComm(WPARAM ch, LPARAM port) 

    m_strEditReceiveMsg += ch; 
    UpdateData(FLASE);//将接收到的字符显示在接受编辑框中 
    returne 0

说明:WPARAMLPARAM类型是多态数据类型,在WIN32中为32位,支持多种数据类型,根据需要自动适应,这样,程序有很强的适应性,再次,我们可以分贝理解为charinteger类型数据,每当串口接受缓冲区内有一个字符时,就会产生一个WM_COMM_RXCHAR消息,除法OnComm()函数,这时就可以在函数中进行数据处理,所以,这个消息就是整个程序的源头。

CSerialPort类的改进

虽然CSerialPort类是一个非常好的类,但毕竟只是集中了作者一个人的智慧和经验,他也有许多缺陷,

原类只能发送字符(ASCII文本)不能处理二进制发送(也就是不能发送0X00

该类不能很好的释放串口

存在内存泄漏

所以,可以进行如下改进

改进一、ASCII文本和二进制数据发送方式兼容

CSerialPort类中只有一个发送函数WriteToPort()

// 
// Write a string to the port 
// 
void CSerialPort::WriteToPort(char *string) 

    assert(m_hComm != 0); 
 
    memset(m_szWriteBuffer, 0sizeof(m_szWriteBuffer)); 
    strcpy(m_szWriteBuffer, string); 
 
    // set event for write 
    SetEvent(m_hWriteEvent); 

调用上面的函数就只能用字符串方式,而c语言中,空字符(NULL,其中ASCII码值为0,即通常所说的十六禁止0x00字符),是串结束符,当检测到NULL字符后,就认为该字符串结束了,所以0x00字符以ASCII文本方式是不能从串口发送出去的,那么解决这一问题的方法就是用二进制发送,其实这里说的二进制,只不过是不以我们通常所说的“可见”或“能显示的字符”发送,比如,要发如下的一组值:
char chSend[5]={0x33,0x96,0x00,0x31,0xf1};

下面来对类做一些改进,解决这个问题,原理就是用字符数据来发送数据,并在发送时指定其长度,这样,数据没有发送完,发送过程就不会停止,CSerialPort类是用API函数编写的在,只要在WriteFile()函数中指定其实际要发送的长度,就可以将数据全部发送出去:

实现步骤如下:

1、SerialPort.h文件中为CSerialPort类添加一个整形publicdn成员变量,:int m_nWriteSize;用于指定发送字符数据的长度

添加三个发送函数


CSerialPort类解析(五)——源代码 

1、Win32下串口大致操作流程
(1)打开串口:CreateFile函数
(2)建立串口通信事件:CreateEvent函数
(3)初始化串口:SetCommState函数
(4)建立监视线程,即读写数据线程,因为我们不知道什么时候数据会到来,这里是一个异步事件
(5)写数据:WriteFile
(6)结束:关闭线程->停止WaitCommEvent->CloseHandle

2.SerialPort类的数据结构
大致了解操作流程后,先看一下SerialPort类,均在代码注释了
数据成员:

[cpp]  view plain copy
  1. public:  
  2.     int     m_nWriteSize;   //要写入串口的数据大小  
  3.     HANDLE  m_hComm;    //串口句柄  
  4. protected:  
  5.     // thread监视线程  
  6.     CWinThread*         m_Thread;  
  7.   
  8.     // synchronisation objects  
  9.     //临界资源  
  10.     CRITICAL_SECTION    m_csCommunicationSync;  
  11.     //监视线程运行标志  
  12.     BOOL                m_bThreadAlive;  
  13.   
  14.     // handles  
  15.     /*事件句柄*/  
  16.     HANDLE              m_hWriteEvent;  
  17.     HANDLE              m_hShutdownEvent;  
  18.   
  19.     // There is a general shutdown when the port is closed.   
  20.     //事件数组,包括一个写事件,接收事件,关闭事件  
  21.     HANDLE              m_hEventArray[3];  
  22.   
  23.     // structures  
  24.     OVERLAPPED          m_ov;   //异步I/O模型  
  25.     COMMTIMEOUTS        m_CommTimeouts; //超时设置  
  26.     DCB         m_dcb;      //设备控制块  
  27.   
  28.     // owner window  
  29.     CWnd*               m_pOwner;  
  30.   
  31.     // misc  
  32.     UINT                m_nPortNr;  
  33.     char*               m_szWriteBuffer;        //写缓冲区  
  34.     DWORD               m_dwCommEvents;         //  
  35.     DWORD               m_nWriteBufferSize;     //写缓冲大小  

函数成员:

[cpp]  view plain copy
  1. public:  
  2.     /*******************Port Operation***********************/  
  3.   
  4.     // port initialisation        
  5.     /*初始化串口*/  
  6.     BOOL        InitPort(CWnd* pPortOwner,   
  7.                 UINT portnr = 1,  
  8.                 UINT baud = 19200,   
  9.                 char parity = 'N',   
  10.                 UINT databits = 8,   
  11.                 UINT stopbits = 1,   
  12.                 DWORD dwCommEvents = EV_RXCHAR,   
  13.                 UINT writebuffersize = 1024);  
  14.     //关闭端口  
  15.     void ClosePort();  
  16.     // start/stop comm watching  
  17.     //控制串口监视线程  
  18.     BOOL        StartMonitoring();  //开启  
  19.     BOOL        RestartMonitoring();    //复位  
  20.     BOOL        StopMonitoring();   //停止  
  21.   
  22.     DWORD       GetWriteBufferSize();//获取写缓冲大小  
  23.     DWORD       GetCommEvents();    //获取事件  
  24.     DCB         GetDCB();   //获取DCB  
  25.     //写数据到串口  
  26.     void        WriteToPort(char* string);  
  27.     void        WriteToPort(char* string,int n);  
  28.     void        WriteToPort(LPCTSTR string);  
  29.     void        WriteToPort(LPCTSTR string,int n);  
  30. protected:  
  31. /***************** protected memberfunctions **********************/  
  32.     void        ProcessErrorMessage(char* ErrorText);  
  33.     //线程函数  
  34.     static UINT CommThread(LPVOID pParam);  
  35.     //接收字符  
  36.     static void ReceiveChar(CSerialPort* port, COMSTAT comstat);  
  37.     //写字符  
  38.     static void WriteChar(CSerialPort* port);  

3.串口操作

(1)初始化串口

流程:检查参数-->检测线程-->创建事件(监视线程)-->打开端口-->设置异步IO结构参数,详细见代码:

[cpp]  view plain copy
  1. /*初始化串口*/  
  2. BOOL CSerialPort::InitPort(CWnd* pPortOwner,    // the owner (CWnd) of the port (receives message)  
  3.                            UINT  portnr,        // portnumber (1..4)  
  4.                            UINT  baud,          // baudrate  
  5.                            char  parity,        // parity   
  6.                            UINT  databits,      // databits   
  7.                            UINT  stopbits,      // stopbits   
  8.                            DWORD dwCommEvents,  // EV_RXCHAR, EV_CTS etc  
  9.                            UINT  writebuffersize)   // size to the writebuffer  
  10. {  
  11.     assert(portnr > 0 && portnr < 5);  
  12.     assert(pPortOwner != NULL);  
  13.   
  14.     // if the thread is alive: Kill  
  15.     //线程在的话关断它  
  16.     if (m_bThreadAlive)  
  17.     {  
  18.         do  
  19.         {  
  20.             SetEvent(m_hShutdownEvent);  
  21.         } while (m_bThreadAlive);  
  22.         TRACE("Thread ended/n");  
  23.     }  
  24.   
  25.     // create events  
  26.     //创建事件  
  27.     if (m_ov.hEvent != NULL)  
  28.         ResetEvent(m_ov.hEvent);  
  29.     else  
  30.         m_ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);  
  31.   
  32.     if (m_hWriteEvent != NULL)  
  33.         ResetEvent(m_hWriteEvent);  
  34.     else  
  35.         m_hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);  
  36.       
  37.     if (m_hShutdownEvent != NULL)  
  38.         ResetEvent(m_hShutdownEvent);  
  39.     else  
  40.         m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);  
  41.   
  42.     // initialize the event objects  
  43.     //事件数组初始化,设定优先级别  
  44.     m_hEventArray[0] = m_hShutdownEvent;    // highest priority  
  45.     m_hEventArray[1] = m_ov.hEvent;  
  46.     m_hEventArray[2] = m_hWriteEvent;  
  47.   
  48.     // initialize critical section  
  49.     //初始化一个临界资源对象  
  50.     InitializeCriticalSection(&m_csCommunicationSync);  
  51.       
  52.     // set buffersize for writing and save the owner  
  53.     m_pOwner = pPortOwner;  
  54.   
  55.     if (m_szWriteBuffer != NULL)  
  56.         delete [] m_szWriteBuffer;  
  57.     m_szWriteBuffer = new char[writebuffersize];  
  58.   
  59.     m_nPortNr = portnr;  
  60.   
  61.     m_nWriteBufferSize = writebuffersize;  
  62.     m_dwCommEvents = dwCommEvents;  
  63.   
  64.     BOOL bResult = FALSE;  
  65.     char *szPort = new char[50];  
  66.     char *szBaud = new char[50];  
  67.   
  68.     // now it critical!  
  69.     /********************************************* 
  70.     多个线程操作相同的数据时,一般是需要按顺序访问的,否则会引导数据错乱, 
  71.     无法控制数据,变成随机变量。为解决这个问题,就需要引入互斥变量,让每个 
  72.     线程都按顺序地访问变量。这样就需要使用EnterCriticalSection和LeaveCriticalSection函数。 
  73.     **********************************************************************/  
  74.     //进入临界区  
  75.     EnterCriticalSection(&m_csCommunicationSync);  
  76.   
  77.     // if the port is already opened: close it  
  78.     //端口已经打开的就关闭它  
  79.     if (m_hComm != NULL)  
  80.     {  
  81.         CloseHandle(m_hComm);  
  82.         m_hComm = NULL;  
  83.     }  
  84.   
  85.     // prepare port strings  
  86.     //串口参数  
  87.     sprintf(szPort, "COM%d", portnr);  
  88.     sprintf(szBaud, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, stopbits);  
  89.   
  90.     // get a handle to the port  
  91.     /**************************************************************** 
  92.     *通信程序在CreateFile处指定串口设备及相关的操作属性,再返回一个句柄, 
  93.     *该句柄将被用于后续的通信操作,并贯穿整个通信过程串口打开后,其属性 
  94.     *被设置为默认值,根据具体需要,通过调用GetCommState(hComm,&&dcb)读取 
  95.     *当前串口设备控制块DCB设置,修改后通过SetCommState(hComm,&&dcb)将其写 
  96.     *入。运用ReadFile()与WriteFile()这两个API函数实现串口读写操作,若为异 
  97.     *步通信方式,两函数中最后一个参数为指向OVERLAPPED结构的非空指针,在读 
  98.     *写函数返回值为FALSE的情况下,调用GetLastError()函数,返回值为ERROR_IO_PENDING, 
  99.     *表明I/O操作悬挂,即操作转入后台继续执行。此时,可以用WaitForSingleObject() 
  100.     *来等待结束信号并设置最长等待时间 
  101.     *****************************************************************/  
  102.     m_hComm = CreateFile(szPort,                        // communication port string (COMX)串口号  
  103.                          GENERIC_READ | GENERIC_WRITE,  // read/write types 可以读写  
  104.                          0,                             // comm devices must be opened with exclusive access独占方式打开串口  
  105.                          NULL,                          // no security attributes 无安全属性  
  106.                          OPEN_EXISTING,                 // comm devices must use OPEN_EXISTING  
  107.                          FILE_FLAG_OVERLAPPED,          // Async I/O 异步I/O  
  108.                          0);                            // template must be 0 for comm devices  
  109.   
  110.     //如果创建不成功,错误处理  
  111.     if (m_hComm == INVALID_HANDLE_VALUE)  
  112.     {  
  113.         // port not found  
  114.         delete [] szPort;  
  115.         delete [] szBaud;  
  116.   
  117.         return FALSE;  
  118.     }  
  119.   
  120.     // set the timeout values  
  121.     //设置超时上限(异步IO)  
  122.     m_CommTimeouts.ReadIntervalTimeout = 1000;  
  123.     m_CommTimeouts.ReadTotalTimeoutMultiplier = 1000;  
  124.     m_CommTimeouts.ReadTotalTimeoutConstant = 1000;  
  125.     m_CommTimeouts.WriteTotalTimeoutMultiplier = 1000;  
  126.     m_CommTimeouts.WriteTotalTimeoutConstant = 1000;  
  127.   
  128.     // configure  
  129.     /*分别调用Windows API设置串口参数*/  
  130.     if (SetCommTimeouts(m_hComm, &m_CommTimeouts))          //设置超时  
  131.     {     
  132.         /******************************************************* 
  133.         若对端口数据的响应时间要求较严格,可采用事件驱动方式。 
  134.         事件驱动方式通过设置事件通知,当所希望的事件发生时,Windows 
  135.         发出该事件已发生的通知,这与DOS环境下的中断方式很相似。Windows 
  136.         定义了9种串口通信事件,较常用的有以下三种:  
  137.  
  138.         EV_RXCHAR:接收到一个字节,并放入输入缓冲区;  
  139.  
  140.         EV_TXEMPTY:输出缓冲区中的最后一个字符,发送出去;  
  141.  
  142.         EV_RXFLAG:接收到事件字符(DCB结构中EvtChar成员),放入输入缓冲区 
  143.  
  144.         在用SetCommMask()指定了有用的事件后,应用程序可调用WaitCommEvent()来等待事 
  145.         件的发生。SetCommMask(hComm,0)可使WaitCommEvent()中止。 
  146.         **************************************************************/              
  147.         if (SetCommMask(m_hComm, dwCommEvents))             //设置通信事件   
  148.         {  
  149.             if (GetCommState(m_hComm, &m_dcb))              //获取当前DCB参数  
  150.             {  
  151.                 m_dcb.EvtChar = 'q';                    //设置字件字符  
  152.                 m_dcb.fRtsControl = RTS_CONTROL_ENABLE;     // set RTS bit high!  
  153.                 if (BuildCommDCB(szBaud, &m_dcb))       //填写DCB结构  
  154.                 {  
  155.                     if (SetCommState(m_hComm, &m_dcb))  //配置DCB  
  156.                         ; // normal operation... continue  
  157.                     else  
  158.                         ProcessErrorMessage("SetCommState()");  
  159.                 }  
  160.                 else  
  161.                     ProcessErrorMessage("BuildCommDCB()");  
  162.             }  
  163.             else  
  164.                 ProcessErrorMessage("GetCommState()");  
  165.         }  
  166.         else  
  167.             ProcessErrorMessage("SetCommMask()");  
  168.     }  
  169.     else  
  170.         ProcessErrorMessage("SetCommTimeouts()");  
  171.   
  172.     delete [] szPort;  
  173.     delete [] szBaud;  
  174.   
  175.     // flush the port  
  176.     //终止读写并清空接收和发送  
  177.     PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);  
  178.   
  179.     // release critical section  
  180.     //出临界区  
  181.     LeaveCriticalSection(&m_csCommunicationSync);  
  182.   
  183.     TRACE("Initialisation for communicationport %d completed./nUse Startmonitor to communicate./n", portnr);  
  184.   
  185.     return TRUE;  
  186. }  

 

(2)监视线程的控制

先看比较简单的线程控制吧,主要有开启线程,复位和停止

 

[cpp]  view plain copy
  1. //开始监视串口  
  2. BOOL CSerialPort::StartMonitoring()  
  3. {  
  4.     if (!(m_Thread = AfxBeginThread(CommThread, this)))  
  5.         return FALSE;  
  6.     TRACE("Thread started/n");  
  7.     return TRUE;      
  8. }  
  9.   
  10. //  
  11. // Restart the comm thread  
  12. //  
  13. //重启监视线程(挂起重启)  
  14. BOOL CSerialPort::RestartMonitoring()  
  15. {  
  16.     TRACE("Thread resumed/n");  
  17.     m_Thread->ResumeThread();  
  18.     return TRUE;      
  19. }  
  20.   
  21.   
  22. //  
  23. // Suspend the comm thread  
  24. //  
  25. //挂起监视线程  
  26. BOOL CSerialPort::StopMonitoring()  
  27. {  
  28.     TRACE("Thread suspended/n");  
  29.     m_Thread->SuspendThread();   
  30.     return TRUE;      
  31. }  

(3)监视线程

我们把读写串口的操作全部交给监视线程,现在简单看一下监视线程的大致流程:

检查串口-->进入循环{WaitCommEvent(不阻塞询问)询问事件-->如果有事件来到-->到相应处理(关闭/读/写)}

详细代码如下:

[cpp]  view plain copy
  1. //监视串口函数  
  2. UINT CSerialPort::CommThread(LPVOID pParam)  
  3. {  
  4.     // Cast the void pointer passed to the thread back to  
  5.     // a pointer of CSerialPort class  
  6.     CSerialPort *port = (CSerialPort*)pParam;  
  7.       
  8.     // Set the status variable in the dialog class to  
  9.     // TRUE to indicate the thread is running.  
  10.     port->m_bThreadAlive = TRUE;   
  11.           
  12.     // Misc. variables  
  13.     DWORD BytesTransfered = 0;   
  14.     DWORD Event = 0;  
  15.     DWORD CommEvent = 0;  
  16.     DWORD dwError = 0;  
  17.     COMSTAT comstat;  
  18.     BOOL  bResult = TRUE;  
  19.           
  20.     // Clear comm buffers at startup  
  21.     //检查串口是否打开  
  22.     if (port->m_hComm)       // check if the port is opened  
  23.         PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);  
  24.   
  25.     // begin forever loop.  This loop will run as long as the thread is alive.  
  26.     //不断读取数据  
  27.     for (;;)   
  28.     {   
  29.   
  30.         // Make a call to WaitCommEvent().  This call will return immediatly  
  31.         // because our port was created as an async port (FILE_FLAG_OVERLAPPED  
  32.         // and an m_OverlappedStructerlapped structure specified).  This call will cause the   
  33.         // m_OverlappedStructerlapped element m_OverlappedStruct.hEvent, which is part of the m_hEventArray to   
  34.         // be placed in a non-signeled state if there are no bytes available to be read,  
  35.         // or to a signeled state if there are bytes available.  If this event handle   
  36.         // is set to the non-signeled state, it will be set to signeled when a   
  37.         // character arrives at the port.  
  38.   
  39.         // we do this for each port!  
  40.         /******************************************************************** 
  41.         WaitCommEvent函数第3个参数1pOverlapped可以是一个OVERLAPPED结构的变量指针 
  42.         ,也可以是NULL,当用NULL时,表示该函数是同步的,否则表示该函数是异步的。 
  43.         调用WaitCommEvent时,如果异步操作不能立即完成,会立即返回FALSE,系统在 
  44.         WaitCommEvent返回前将OVERLAPPED结构成员hEvent设为无信号状态,等到产生通信 
  45.         事件时,系统将其置有信号 
  46.         ***********************************************************************/  
  47.         bResult = WaitCommEvent(port->m_hComm, &Event, &port->m_ov);//表示该函数是异步的   
  48.   
  49.           
  50.         if (!bResult)    
  51.         {   
  52.             //如果WaitCommEvent返回Error为NULL,则查询错误信息  
  53.             // If WaitCommEvent() returns FALSE, process the last error to determin  
  54.             // the reason..  
  55.             switch (dwError = GetLastError())   
  56.             {   
  57.             case ERROR_IO_PENDING:      //正常情况,没有字符可读  
  58.                 {   
  59.                     // This is a normal return value if there are no bytes  
  60.                     // to read at the port.  
  61.                     // Do nothing and continue  
  62.                     break;  
  63.                 }  
  64.             case 87:                    //系统错误  
  65.                 {  
  66.                     // Under Windows NT, this value is returned for some reason.  
  67.                     // I have not investigated why, but it is also a valid reply  
  68.                     // Also do nothing and continue.  
  69.                     break;  
  70.                 }  
  71.             default:                    //发生其他错误  
  72.                 {  
  73.                     // All other error codes indicate a serious error has  
  74.                     // occured.  Process this error.  
  75.                     port->ProcessErrorMessage("WaitCommEvent()");  
  76.                     break;  
  77.                 }  
  78.             }  
  79.         }  
  80.         else                //WaitCommEvent()能正确返回  
  81.         {  
  82.             // If WaitCommEvent() returns TRUE, check to be sure there are  
  83.             // actually bytes in the buffer to read.    
  84.             //  
  85.             // If you are reading more than one byte at a time from the buffer   
  86.             // (which this program does not do) you will have the situation occur   
  87.             // where the first byte to arrive will cause the WaitForMultipleObjects()   
  88.             // function to stop waiting.  The WaitForMultipleObjects() function   
  89.             // resets the event handle in m_OverlappedStruct.hEvent to the non-signelead state  
  90.             // as it returns.    
  91.             //  
  92.             // If in the time between the reset of this event and the call to   
  93.             // ReadFile() more bytes arrive, the m_OverlappedStruct.hEvent handle will be set again  
  94.             // to the signeled state. When the call to ReadFile() occurs, it will   
  95.             // read all of the bytes from the buffer, and the program will  
  96.             // loop back around to WaitCommEvent().  
  97.             //   
  98.             // At this point you will be in the situation where m_OverlappedStruct.hEvent is set,  
  99.             // but there are no bytes available to read.  If you proceed and call  
  100.             // ReadFile(), it will return immediatly due to the async port setup, but  
  101.             // GetOverlappedResults() will not return until the next character arrives.  
  102.             //  
  103.             // It is not desirable for the GetOverlappedResults() function to be in   
  104.             // this state.  The thread shutdown event (event 0) and the WriteFile()  
  105.             // event (Event2) will not work if the thread is blocked by GetOverlappedResults().  
  106.             //  
  107.             // The solution to this is to check the buffer with a call to ClearCommError().  
  108.             // This call will reset the event handle, and if there are no bytes to read  
  109.             // we can loop back through WaitCommEvent() again, then proceed.  
  110.             // If there are really bytes to read, do nothing and proceed.  
  111.           
  112.             bResult = ClearCommError(port->m_hComm, &dwError, &comstat);  
  113.   
  114.             if (comstat.cbInQue == 0)  
  115.                 continue;  
  116.         }   // end if bResult  
  117.           
  118.         //主等待函数,会阻塞线程  
  119.         // Main wait function.  This function will normally block the thread  
  120.         // until one of nine events occur that require action.  
  121.         //等待3个事件:关断/读/写,有一个事件发生就返回  
  122.         Event = WaitForMultipleObjects(3,               //3个事件  
  123.                                     port->m_hEventArray,//事件数组  
  124.                                     FALSE,              //有一个事件发生就返回  
  125.                                     INFINITE);          //超时时间  
  126.   
  127.         switch (Event)  
  128.         {  
  129.         case 0:  
  130.             {  
  131.                 // Shutdown event.  This is event zero so it will be  
  132.                 // the higest priority and be serviced first.  
  133.                 //关断事件,关闭串口  
  134.                 CloseHandle(port->m_hComm);  
  135.                 port->m_hComm=NULL;  
  136.                 port->m_bThreadAlive = FALSE;  
  137.                   
  138.                 // Kill this thread.  break is not needed, but makes me feel better.  
  139.                 AfxEndThread(100);  
  140.   
  141.                 break;  
  142.             }  
  143.         case 1: // read event   将定义的各种消息发送出去  
  144.             {  
  145.                 //接收  
  146.                 GetCommMask(port->m_hComm, &CommEvent);  
  147.                 if (CommEvent & EV_RXCHAR)  
  148.                     // Receive character event from port.  
  149.                     ReceiveChar(port, comstat);  
  150.                 if (CommEvent & EV_CTS)  
  151.                     ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_CTS_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);  
  152.                 if (CommEvent & EV_BREAK)  
  153.                     ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_BREAK_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);  
  154.                 if (CommEvent & EV_ERR)  
  155.                     ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_ERR_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);  
  156.                 if (CommEvent & EV_RING)  
  157.                     ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RING_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);  
  158.                   
  159.                 if (CommEvent & EV_RXFLAG)  
  160.                     ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RXFLAG_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);  
  161.                       
  162.                 break;  
  163.             }    
  164.         case 2: // write event发送数据  
  165.             {  
  166.                 // Write character event from port  
  167.                 //写  
  168.                 WriteChar(port);  
  169.                 break;  
  170.             }  
  171.   
  172.         } // end switch  
  173.   
  174.     } // close forever loop  
  175.   
  176.     return 0;  
  177. }  

(4)读取数据操作

读取数据是一个异步操作,当有数据发来时,会触发读事件m_ov.hEvent,监视线程捕捉到事件后并获知是读事件,进入相关读处理,这里调用函数ReceiveChar

,ReceiveChar中调用ReadFile函数将串口数据读到Buffer缓冲中,相关代码如下:

[cpp]  view plain copy
  1. //接收数据  
  2. void CSerialPort::ReceiveChar(CSerialPort* port, COMSTAT comstat)  
  3. {  
  4.     BOOL  bRead = TRUE;   
  5.     BOOL  bResult = TRUE;  
  6.     DWORD dwError = 0;  
  7.     DWORD BytesRead = 0;  
  8.     unsigned char RXBuff;  
  9.   
  10.     for (;;)   
  11.     {   
  12.         // Gain ownership of the comm port critical section.  
  13.         // This process guarantees no other part of this program   
  14.         // is using the port object.   
  15.           
  16.         EnterCriticalSection(&port->m_csCommunicationSync);  
  17.   
  18.         // ClearCommError() will update the COMSTAT structure and  
  19.         // clear any other errors.  
  20.         //更新COMSTAT  
  21.         bResult = ClearCommError(port->m_hComm, &dwError, &comstat);  
  22.   
  23.         LeaveCriticalSection(&port->m_csCommunicationSync);  
  24.   
  25.         // start forever loop.  I use this type of loop because I  
  26.         // do not know at runtime how many loops this will have to  
  27.         // run. My solution is to start a forever loop and to  
  28.         // break out of it when I have processed all of the  
  29.         // data available.  Be careful with this approach and  
  30.         // be sure your loop will exit.  
  31.         // My reasons for this are not as clear in this sample   
  32.         // as it is in my production code, but I have found this   
  33.         // solutiion to be the most efficient way to do this.  
  34.         //所有字符均被读出,中断循环  
  35.         if (comstat.cbInQue == 0)  
  36.         {  
  37.             // break out when all bytes have been read  
  38.             break;  
  39.         }  
  40.                           
  41.         EnterCriticalSection(&port->m_csCommunicationSync);  
  42.   
  43.         if (bRead)  
  44.         {  
  45.             //串口读出,读出缓冲区中字节  
  46.             bResult = ReadFile(port->m_hComm,        // Handle to COMM port   
  47.                                &RXBuff,             // RX Buffer Pointer  
  48.                                1,                   // Read one byte  
  49.                                &BytesRead,          // Stores number of bytes read  
  50.                                &port->m_ov);     // pointer to the m_ov structure  
  51.             // deal with the error code   
  52.             //若返回错误,错误处理  
  53.             if (!bResult)    
  54.             {   
  55.                 switch (dwError = GetLastError())   
  56.                 {   
  57.                     case ERROR_IO_PENDING:    
  58.                         {   
  59.                             // asynchronous i/o is still in progress   
  60.                             // Proceed on to GetOverlappedResults();  
  61.                             //异步IO仍在进行  
  62.                             bRead = FALSE;  
  63.                             break;  
  64.                         }  
  65.                     default:  
  66.                         {   //其他错误处理  
  67.                             // Another error has occured.  Process this error.  
  68.                             port->ProcessErrorMessage("ReadFile()");  
  69.                             break;  
  70.                         }   
  71.                 }  
  72.             }  
  73.             else  
  74.             {   //ReadFile返回TRUE  
  75.                 // ReadFile() returned complete. It is not necessary to call GetOverlappedResults()  
  76.                 bRead = TRUE;  
  77.             }  
  78.         }  // close if (bRead)  
  79.         //异步IO操作仍在进行,需要调用GetOverlappedResult查询  
  80.         if (!bRead)  
  81.         {  
  82.             bRead = TRUE;  
  83.             bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port   
  84.                                           &port->m_ov,       // Overlapped structure  
  85.                                           &BytesRead,       // Stores number of bytes read  
  86.                                           TRUE);            // Wait flag  
  87.   
  88.             // deal with the error code   
  89.             if (!bResult)    
  90.             {  
  91.                 port->ProcessErrorMessage("GetOverlappedResults() in ReadFile()");  
  92.             }     
  93.         }  // close if (!bRead)  
  94.                   
  95.         LeaveCriticalSection(&port->m_csCommunicationSync);  
  96.   
  97.         // notify parent that a byte was received  
  98.         ::SendMessage((port->m_pOwner)->m_hWnd, WM_COMM_RXCHAR, (WPARAM) RXBuff, (LPARAM) port->m_nPortNr);  
  99.     } // end forever loop  
  100.   
  101. }  

(5)写数据

也是由监视线程操作,不过触发事件交给主线程来触发,函数是WriteToPort

[cpp]  view plain copy
  1. //  
  2. // Write a string to the port  
  3. //  
  4. void CSerialPort::WriteToPort(char* string)  
  5. {         
  6.     assert(m_hComm != 0);  
  7.     //写进写缓冲区  
  8.     memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));  
  9.     strcpy(m_szWriteBuffer, string);  
  10.     m_nWriteSize=strlen(string);  
  11.   
  12.     // set event for write  
  13.     SetEvent(m_hWriteEvent);  
  14. }  

线程调用的函数WriteChar,把缓冲里的数据写到串口中,期间调用WriteFile

详细代码:

[cpp]  view plain copy
  1. //写数据  
  2. void CSerialPort::WriteChar(CSerialPort* port)  
  3. {  
  4.     BOOL bWrite = TRUE;  
  5.     BOOL bResult = TRUE;  
  6.   
  7.     DWORD BytesSent = 0;  
  8.   
  9.     ResetEvent(port->m_hWriteEvent);     //复位写事件句柄  
  10.   
  11.     // Gain ownership of the critical section  
  12.     EnterCriticalSection(&port->m_csCommunicationSync);  
  13.   
  14.     if (bWrite)  
  15.     {  
  16.         // Initailize variables  
  17.         port->m_ov.Offset = 0;  
  18.         port->m_ov.OffsetHigh = 0;  
  19.   
  20.         // Clear buffer  
  21.         PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);  
  22.   
  23.         //串口写入  
  24.         bResult = WriteFile(port->m_hComm,                           // Handle to COMM Port  
  25.                             port->m_szWriteBuffer,                   // Pointer to message buffer in calling finction  
  26. //                          strlen((char*)port->m_szWriteBuffer),    // Length of message to send  
  27.                             port->m_nWriteSize,  // Length of message to send  
  28.                             &BytesSent,                             // Where to store the number of bytes sent  
  29.                             &port->m_ov);                            // Overlapped structure  
  30.   
  31.         // deal with any error codes  
  32.         if (!bResult)    
  33.         {  
  34.             DWORD dwError = GetLastError();  
  35.             switch (dwError)  
  36.             {  
  37.                 case ERROR_IO_PENDING:  
  38.                     {  
  39.                         // continue to GetOverlappedResults()  
  40.                         BytesSent = 0;  
  41.                         bWrite = FALSE;  
  42.                         break;  
  43.                     }  
  44.                 default:  
  45.                     {  
  46.                         // all other error codes  
  47.                         port->ProcessErrorMessage("WriteFile()");  
  48.                     }  
  49.             }  
  50.         }   
  51.         else  
  52.         {  
  53.             LeaveCriticalSection(&port->m_csCommunicationSync);  
  54.         }  
  55.     } // end if(bWrite)  
  56.   
  57.     if (!bWrite)  
  58.     {  
  59.         bWrite = TRUE;  
  60.       
  61.         bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port   
  62.                                       &port->m_ov,       // Overlapped structure  
  63.                                       &BytesSent,       // Stores number of bytes sent  
  64.                                       TRUE);            // Wait flag  
  65.   
  66.         LeaveCriticalSection(&port->m_csCommunicationSync);  
  67.   
  68.         // deal with the error code   
  69. //      if (!bResult)    
  70.         {  
  71. //          port->ProcessErrorMessage("GetOverlappedResults() in WriteFile()");  
  72.         }     
  73.     } // end if (!bWrite)  
  74.   
  75.     //Verify that the data size send equals what we tried to send  
  76.     if (BytesSent != port->m_nWriteSize) // Length of message to send)  
  77.     {  
  78.         TRACE("WARNING: WriteFile() error.. Bytes Sent: %d; Message Length: %d/n", BytesSent, strlen((char*)port->m_szWriteBuffer));  
  79.     }  
  80. //  ::SendMessage((port->m_pOwner)->m_hWnd, WM_COMM_TXEMPTY_DETECTED, (WPARAM) RXBuff, (LPARAM) port->m_nPortNr);  
  81. //  ::SendMessage((port->m_pOwner)->m_hWnd, WM_COMM_TXEMPTY_DETECTED,0,(LPARAM) port->m_nPortNr);  
  82. }  

(6)其他操作

其他比如获取DCB,关闭等,比较简单,不做分析

代码如下:

[cpp]  view plain copy
  1. //  
  2. // Return the device control block  
  3. //  
  4. DCB CSerialPort::GetDCB()  
  5. {  
  6.     return m_dcb;  
  7. }  
  8.   
  9. //  
  10. // Return the communication event masks  
  11. //  
  12. DWORD CSerialPort::GetCommEvents()  
  13. {  
  14.     return m_dwCommEvents;  
  15. }  
  16.   
  17. //  
  18. // Return the output buffer size  
  19. //  
  20. DWORD CSerialPort::GetWriteBufferSize()  
  21. {  
  22.     return m_nWriteBufferSize;  
  23. }  
  24.   
  25.   
  26. void CSerialPort::ClosePort()  
  27. {  
  28.         SetEvent(m_hShutdownEvent);  
  29. }  





  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CSerialPort First Version by Remon Spekreijse on 2000-02-08 http://www.codeguru.com/cpp/i-n/network/serialcommunications/article.php/c2483/A-communication-class-for-serial-port.htm Second Version by mrlong on 2007-12-25 https://code.google.com/p/mycom/ 增加 ClosePort 增加 WriteToPort 两个方法 增加 SendData 与 RecvData 方法 by liquanhai on 2011-11-04 http://blog.csdn.net/liquanhai/article/details/4955253 增加 ClosePort 中交出控制权,防止死锁问题 by liquanhai on 2011-11-06 http://blog.csdn.net/liquanhai/article/details/6941574 增加 ReceiveChar 中防止线程死锁 by viruscamp on 2013-12-04 https://github.com/viruscamp/CSerialPort 增加 IsOpen 判断是否打开 修正 InitPort 中 parity Odd Even 参数取值错误 修改 InitPortportnr 取值范围,portnr>9 时特殊处理 取消对 MFC 的依赖,使用 HWND 替代 CWnd,使用 win32 thread 函数而不是 MFC 的 增加用户消息编号自定义,方法来自 CnComm by itas109 on 2014-01-10 http://blog.csdn.net/itas109/article/details/18358297 解决COM10以上端口无法显示的问题 扩展可选择端口,最大值MaxSerialPortNum可以自定义 添加QueryKey()和Hkey2ComboBox两个方法,用于自动查询当前有效的串口号。 by liquanhai on 2014-12-18 增加一些处理措施,主要是对减少CPU占用率 by itas109 on 2016-05-07 http://blog.csdn.net/itas109 修复每次打开串口发送一次,当串口无应答时,需要关闭再打开或者接收完数据才能发送的问题。 解决办法:在m_hEventArray中调整m_hWriteEvent的优先级高于读的优先级。CommThread(LPVOID pParam)函数中读写的位置也调换。 参考:http://zhidao.baidu.com/link?url=RSrbPcfTZRULFFd2ziHZPBwnoXv1iCSu_Nmycb_yEw1mklT8gkoNZAkWpl3UDhk8L35DtRPo5VV5kEGpOx-Gea 修复停止位在头文件中定义成1导致SetCommState报错的问题,应为1对应的停止位是1.5。UINT stopsbits = ONESTOPBIT switch(stopbits)和switch(parity)增加默认情况,增强程序健壮性 by itas109 on 2016-06-22 http://blog.csdn.net/itas109 增加ReceiveStr方法,用于接收字符串(接收缓冲区有多少字符就接收多少字符)。 解决ReceiveChar只能接收单个字符的问题。 by itas109 on 2016-06-29 http://blog.csdn.net/itas109 解决RestartMonitoring方法和StopMonitoring方法命令不准确引起的歧义,根据实际作用。 将RestartMonitoring更改为ResumeMonitoring,将StopMonitoring更改为SuspendMonitoring。 增加IsThreadSuspend方法,用于判断线程是否挂起。 改进ClosePort方法,增加线程挂起判断,解决由于线程挂起导致串口关闭死锁的问题。 增加IsReceiveString宏定义,用于接收时采用单字节接收还是多字节接收 by itas109 on 2016-08-02 http://blog.csdn.net/itas109 https://github.com/itas109 改进IsOpen方法,m_hComm增加INVALID_HANDLE_VALUE的情况,因为CreateFile方法失败返回的是INVALID_HANDLE_VALUE,不是NULL 改进ClosePort方法:增加串口句柄无效的判断(防止关闭死锁);m_hWriteEvent不使用CloseHandle关闭 改进CommThread、ReceiveChar、ReceiveStr和WriteChar方法中异常处理的判断,增加三种判断:串口打开失败(error code:ERROR_INVALID_HANDLE)、连接过程中非法断开(error code:ERROR_BAD_COMMAND)和拒绝访问(error code:ERROR_ACCESS_DENIED) 采用安全函数sprintf_s和strcpy_s函数替换掉sprintf和strcpy 改进QueryKey方法,用于查询注册表的可用串口值,可以搜索到任意的可用串口 改进InitPort方法,串口打开失败,增加提示信息:串口不存在(error code:ERROR_FILE_NOT_FOUND)和串口拒绝访问(error code:ERROR_ACCESS_DENIED) 加入viruscamp 取消对 MFC 的依赖 改进InitPort方法,如果上次串口是打开,再次调用InitPort方法,关闭串口需要做一定的延时,否则有几率导致ERROR_ACCESS_DENIED拒绝访问,也就是串口占用问题 初始化默认波特率修改为9600 修复一些释放的BUG 规范了一些错误信息,参考winerror.h -- error code definitions for the Win32 API functions 删除SendData和RecvData方法 by itas109 on 2016-08-10 http://blog.csdn.net/itas109 https://github.com/itas109 改进ReceiveStr方法,comstat.cbInQue = 0xcccccccc的情况(如串口异常断开),会导致RXBuff初始化失败 by itas109 on 2017-02-14 http://blog.csdn.net/itas109 https://github.com/itas109 兼容ASCII和UNICODE编码 ReceiveStr函数中发送函数SendMessage的第二个参数采用结构体形式,包括portNr串口号和bytesRead读取的字节数,可以处理16进制的时候0x00截断问题 精简不必要的函数SendData和RecvData 尽量的取消对 MFC 的依赖,Hkey2ComboBox函数暂时保留 其他小问题修改 博客:blog.csdn.net/itas109 Email:[email protected]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值