Showing
2 changed files
with
979 additions
and
0 deletions
SerialPort.cpp
0 → 100644
| 1 | +/* | |
| 2 | +** FILENAME CSerialPort.cpp | |
| 3 | +** | |
| 4 | +** PURPOSE This class can read, write and watch one serial port. | |
| 5 | +** It sends messages to its owner when something happends on the port | |
| 6 | +** The class creates a thread for reading and writing so the main | |
| 7 | +** program is not blocked. | |
| 8 | +** | |
| 9 | +** CREATION DATE 15-09-1997 | |
| 10 | +** LAST MODIFICATION 12-11-1997 | |
| 11 | +** | |
| 12 | +** AUTHOR Remon Spekreijse | |
| 13 | +** | |
| 14 | +** 2007-12-25 mrlong https://code.google.com/p/mycom/ | |
| 15 | +** 2011-11-06 liquanhai http://blog.csdn.net/liquanhai/article/details/6941574 | |
| 16 | +** 2013-12-04 viruscamp http://github.com/viruscamp/CSerialPort | |
| 17 | +** | |
| 18 | +*/ | |
| 19 | + | |
| 20 | +#include "stdafx.h" | |
| 21 | +#include "SerialPort.h" | |
| 22 | + | |
| 23 | +#include <assert.h> | |
| 24 | + | |
| 25 | +// | |
| 26 | +// Constructor | |
| 27 | +// | |
| 28 | +CSerialPort::CSerialPort() | |
| 29 | +{ | |
| 30 | + m_hComm = NULL; | |
| 31 | + | |
| 32 | + // initialize overlapped structure members to zero | |
| 33 | + m_ov.Offset = 0; | |
| 34 | + m_ov.OffsetHigh = 0; | |
| 35 | + | |
| 36 | + // create events | |
| 37 | + m_ov.hEvent = NULL; | |
| 38 | + m_hWriteEvent = NULL; | |
| 39 | + m_hShutdownEvent = NULL; | |
| 40 | + | |
| 41 | + m_szWriteBuffer = NULL; | |
| 42 | + | |
| 43 | + m_bThreadAlive = FALSE; | |
| 44 | + m_nWriteSize = 1; | |
| 45 | + m_userdata = 0; | |
| 46 | +} | |
| 47 | + | |
| 48 | +// | |
| 49 | +// Delete dynamic memory | |
| 50 | +// | |
| 51 | +CSerialPort::~CSerialPort() | |
| 52 | +{ | |
| 53 | + do | |
| 54 | + { | |
| 55 | + SetEvent(m_hShutdownEvent); | |
| 56 | + } while (m_bThreadAlive); | |
| 57 | + | |
| 58 | + if (m_hComm != NULL) | |
| 59 | + { | |
| 60 | + CloseHandle(m_hComm); | |
| 61 | + m_hComm = NULL; | |
| 62 | + } | |
| 63 | + // Close Handles | |
| 64 | + if(m_hShutdownEvent!=NULL) | |
| 65 | + CloseHandle( m_hShutdownEvent); | |
| 66 | + if(m_ov.hEvent!=NULL) | |
| 67 | + CloseHandle( m_ov.hEvent ); | |
| 68 | + if(m_hWriteEvent!=NULL) | |
| 69 | + CloseHandle( m_hWriteEvent ); | |
| 70 | + | |
| 71 | + //TRACE("Thread ended\n"); | |
| 72 | + | |
| 73 | + delete [] m_szWriteBuffer; | |
| 74 | +} | |
| 75 | + | |
| 76 | +// | |
| 77 | +// Initialize the port. This can be port 1 to 4. | |
| 78 | +// | |
| 79 | +// | |
| 80 | +//parity: | |
| 81 | +// n=none | |
| 82 | +// e=even | |
| 83 | +// o=odd | |
| 84 | +// m=mark | |
| 85 | +// s=space | |
| 86 | +//data: | |
| 87 | +// 5,6,7,8 | |
| 88 | +//stop: | |
| 89 | +// 1,1.5,2 | |
| 90 | +// | |
| 91 | +BOOL CSerialPort::InitPort(HWND pPortOwner, // the owner (CWnd) of the port (receives message) | |
| 92 | + UINT portnr, // portnumber (1..4) | |
| 93 | + UINT baud, // baudrate | |
| 94 | + char parity, // parity | |
| 95 | + UINT databits, // databits | |
| 96 | + UINT stopbits, // stopbits | |
| 97 | + DWORD dwCommEvents, // EV_RXCHAR, EV_CTS etc | |
| 98 | + UINT writebuffersize,// size to the writebuffer | |
| 99 | + DWORD userdata, | |
| 100 | + | |
| 101 | + DWORD ReadIntervalTimeout, | |
| 102 | + DWORD ReadTotalTimeoutMultiplier, | |
| 103 | + DWORD ReadTotalTimeoutConstant, | |
| 104 | + DWORD WriteTotalTimeoutMultiplier, | |
| 105 | + DWORD WriteTotalTimeoutConstant ) | |
| 106 | + | |
| 107 | +{ | |
| 108 | + assert(portnr > 0 && portnr < 200); | |
| 109 | + assert(pPortOwner != NULL); | |
| 110 | + | |
| 111 | + // if the thread is alive: Kill | |
| 112 | + if (m_bThreadAlive) | |
| 113 | + { | |
| 114 | + do | |
| 115 | + { | |
| 116 | + SetEvent(m_hShutdownEvent); | |
| 117 | + } while (m_bThreadAlive); | |
| 118 | + //TRACE("Thread ended\n"); | |
| 119 | + } | |
| 120 | + m_userdata = userdata; | |
| 121 | + | |
| 122 | + // create events | |
| 123 | + if (m_ov.hEvent != NULL) | |
| 124 | + ResetEvent(m_ov.hEvent); | |
| 125 | + else | |
| 126 | + m_ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); | |
| 127 | + | |
| 128 | + if (m_hWriteEvent != NULL) | |
| 129 | + ResetEvent(m_hWriteEvent); | |
| 130 | + else | |
| 131 | + m_hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL); | |
| 132 | + | |
| 133 | + if (m_hShutdownEvent != NULL) | |
| 134 | + ResetEvent(m_hShutdownEvent); | |
| 135 | + else | |
| 136 | + m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL); | |
| 137 | + | |
| 138 | + // initialize the event objects | |
| 139 | + m_hEventArray[0] = m_hShutdownEvent; // highest priority | |
| 140 | + m_hEventArray[1] = m_ov.hEvent; | |
| 141 | + m_hEventArray[2] = m_hWriteEvent; | |
| 142 | + | |
| 143 | + // initialize critical section | |
| 144 | + InitializeCriticalSection(&m_csCommunicationSync); | |
| 145 | + | |
| 146 | + // set buffersize for writing and save the owner | |
| 147 | + m_pOwner = pPortOwner; | |
| 148 | + | |
| 149 | + if (m_szWriteBuffer != NULL) | |
| 150 | + delete [] m_szWriteBuffer; | |
| 151 | + m_szWriteBuffer = new char[writebuffersize]; | |
| 152 | + | |
| 153 | + m_nPortNr = portnr; | |
| 154 | + | |
| 155 | + m_nWriteBufferSize = writebuffersize; | |
| 156 | + m_dwCommEvents = dwCommEvents; | |
| 157 | + | |
| 158 | + BOOL bResult = FALSE; | |
| 159 | + char *szPort = new char[50]; | |
| 160 | + char *szBaud = new char[50]; | |
| 161 | + | |
| 162 | + // now it critical! | |
| 163 | + EnterCriticalSection(&m_csCommunicationSync); | |
| 164 | + | |
| 165 | + // if the port is already opened: close it | |
| 166 | + if (m_hComm != NULL) | |
| 167 | + { | |
| 168 | + CloseHandle(m_hComm); | |
| 169 | + m_hComm = NULL; | |
| 170 | + } | |
| 171 | + | |
| 172 | + // prepare port strings | |
| 173 | + sprintf(szPort, "\\\\.\\COM%d", portnr); | |
| 174 | + | |
| 175 | + // stop is index 0 = 1 1=1.5 2=2 | |
| 176 | + int mystop; | |
| 177 | + int myparity; | |
| 178 | + switch(stopbits) | |
| 179 | + { | |
| 180 | + case 0: | |
| 181 | + mystop = ONESTOPBIT; | |
| 182 | + break; | |
| 183 | + case 1: | |
| 184 | + mystop = ONE5STOPBITS; | |
| 185 | + break; | |
| 186 | + case 2: | |
| 187 | + mystop = TWOSTOPBITS; | |
| 188 | + break; | |
| 189 | + } | |
| 190 | + myparity = 0; | |
| 191 | + parity = toupper(parity); | |
| 192 | + switch(parity) | |
| 193 | + { | |
| 194 | + case 'N': | |
| 195 | + myparity = 0; | |
| 196 | + break; | |
| 197 | + case 'O': | |
| 198 | + myparity = 1; | |
| 199 | + break; | |
| 200 | + case 'E': | |
| 201 | + myparity = 2; | |
| 202 | + break; | |
| 203 | + case 'M': | |
| 204 | + myparity = 3; | |
| 205 | + break; | |
| 206 | + case 'S': | |
| 207 | + myparity = 4; | |
| 208 | + break; | |
| 209 | + } | |
| 210 | + sprintf(szBaud, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, mystop); | |
| 211 | + | |
| 212 | + // get a handle to the port | |
| 213 | + m_hComm = CreateFile(szPort, // communication port string (COMX) | |
| 214 | + GENERIC_READ | GENERIC_WRITE, // read/write types | |
| 215 | + 0, // comm devices must be opened with exclusive access | |
| 216 | + NULL, // no security attributes | |
| 217 | + OPEN_EXISTING, // comm devices must use OPEN_EXISTING | |
| 218 | + FILE_FLAG_OVERLAPPED, // Async I/O | |
| 219 | + 0); // template must be 0 for comm devices | |
| 220 | + | |
| 221 | + if (m_hComm == INVALID_HANDLE_VALUE) | |
| 222 | + { | |
| 223 | + // port not found | |
| 224 | + delete [] szPort; | |
| 225 | + delete [] szBaud; | |
| 226 | + | |
| 227 | + return FALSE; | |
| 228 | + } | |
| 229 | + | |
| 230 | + // set the timeout values | |
| 231 | + m_CommTimeouts.ReadIntervalTimeout = ReadIntervalTimeout * 1000; | |
| 232 | + m_CommTimeouts.ReadTotalTimeoutMultiplier = ReadTotalTimeoutMultiplier * 1000; | |
| 233 | + m_CommTimeouts.ReadTotalTimeoutConstant = ReadTotalTimeoutConstant * 1000; | |
| 234 | + m_CommTimeouts.WriteTotalTimeoutMultiplier = WriteTotalTimeoutMultiplier * 1000; | |
| 235 | + m_CommTimeouts.WriteTotalTimeoutConstant = WriteTotalTimeoutConstant * 1000; | |
| 236 | + | |
| 237 | + // configure | |
| 238 | + if (SetCommTimeouts(m_hComm, &m_CommTimeouts)) | |
| 239 | + { | |
| 240 | + if (SetCommMask(m_hComm, dwCommEvents)) | |
| 241 | + { | |
| 242 | + if (GetCommState(m_hComm, &m_dcb)) | |
| 243 | + { | |
| 244 | + m_dcb.EvtChar = 'q'; | |
| 245 | + m_dcb.fRtsControl = RTS_CONTROL_ENABLE; // set RTS bit high! | |
| 246 | + m_dcb.BaudRate = baud; // add by mrlong | |
| 247 | + m_dcb.Parity = myparity; | |
| 248 | + m_dcb.ByteSize = databits; | |
| 249 | + m_dcb.StopBits = mystop; | |
| 250 | + | |
| 251 | + //if (BuildCommDCB(szBaud, &m_dcb)) | |
| 252 | + //{ | |
| 253 | + if (SetCommState(m_hComm, &m_dcb) == FALSE) | |
| 254 | + ProcessErrorMessage("SetCommState()"); | |
| 255 | + //} | |
| 256 | + //else | |
| 257 | + // ProcessErrorMessage("BuildCommDCB()"); | |
| 258 | + } | |
| 259 | + else | |
| 260 | + ProcessErrorMessage("GetCommState()"); | |
| 261 | + } | |
| 262 | + else | |
| 263 | + ProcessErrorMessage("SetCommMask()"); | |
| 264 | + } | |
| 265 | + else | |
| 266 | + ProcessErrorMessage("SetCommTimeouts()"); | |
| 267 | + | |
| 268 | + delete [] szPort; | |
| 269 | + delete [] szBaud; | |
| 270 | + | |
| 271 | + // flush the port | |
| 272 | + PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT); | |
| 273 | + | |
| 274 | + // release critical section | |
| 275 | + LeaveCriticalSection(&m_csCommunicationSync); | |
| 276 | + | |
| 277 | + //TRACE("Initialisation for communicationport %d completed.\nUse Startmonitor to communicate.\n", portnr); | |
| 278 | + | |
| 279 | + return TRUE; | |
| 280 | +} | |
| 281 | + | |
| 282 | +// | |
| 283 | +// The CommThread Function. | |
| 284 | +// | |
| 285 | +DWORD WINAPI CSerialPort::CommThread(LPVOID pParam) | |
| 286 | +{ | |
| 287 | + // Cast the void pointer passed to the thread back to | |
| 288 | + // a pointer of CSerialPort class | |
| 289 | + CSerialPort *port = (CSerialPort*)pParam; | |
| 290 | + | |
| 291 | + // Set the status variable in the dialog class to | |
| 292 | + // TRUE to indicate the thread is running. | |
| 293 | + port->m_bThreadAlive = TRUE; | |
| 294 | + | |
| 295 | + // Misc. variables | |
| 296 | + DWORD BytesTransfered = 0; | |
| 297 | + DWORD Event = 0; | |
| 298 | + DWORD CommEvent = 0; | |
| 299 | + DWORD dwError = 0; | |
| 300 | + COMSTAT comstat; | |
| 301 | + BOOL bResult = TRUE; | |
| 302 | + | |
| 303 | + // Clear comm buffers at startup | |
| 304 | + if (port->m_hComm) // check if the port is opened | |
| 305 | + PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT); | |
| 306 | + | |
| 307 | + // begin forever loop. This loop will run as long as the thread is alive. | |
| 308 | + for (;;) | |
| 309 | + { | |
| 310 | + | |
| 311 | + // Make a call to WaitCommEvent(). This call will return immediatly | |
| 312 | + // because our port was created as an async port (FILE_FLAG_OVERLAPPED | |
| 313 | + // and an m_OverlappedStructerlapped structure specified). This call will cause the | |
| 314 | + // m_OverlappedStructerlapped element m_OverlappedStruct.hEvent, which is part of the m_hEventArray to | |
| 315 | + // be placed in a non-signeled state if there are no bytes available to be read, | |
| 316 | + // or to a signeled state if there are bytes available. If this event handle | |
| 317 | + // is set to the non-signeled state, it will be set to signeled when a | |
| 318 | + // character arrives at the port. | |
| 319 | + | |
| 320 | + // we do this for each port! | |
| 321 | + | |
| 322 | + bResult = WaitCommEvent(port->m_hComm, &Event, &port->m_ov); | |
| 323 | + | |
| 324 | + if (!bResult) | |
| 325 | + { | |
| 326 | + // If WaitCommEvent() returns FALSE, process the last error to determin | |
| 327 | + // the reason.. | |
| 328 | + switch (dwError = GetLastError()) | |
| 329 | + { | |
| 330 | + case ERROR_IO_PENDING: | |
| 331 | + { | |
| 332 | + // This is a normal return value if there are no bytes | |
| 333 | + // to read at the port. | |
| 334 | + // Do nothing and continue | |
| 335 | + break; | |
| 336 | + } | |
| 337 | + case 87: | |
| 338 | + { | |
| 339 | + // Under Windows NT, this value is returned for some reason. | |
| 340 | + // I have not investigated why, but it is also a valid reply | |
| 341 | + // Also do nothing and continue. | |
| 342 | + break; | |
| 343 | + } | |
| 344 | + default: | |
| 345 | + { | |
| 346 | + // All other error codes indicate a serious error has | |
| 347 | + // occured. Process this error. | |
| 348 | + port->ProcessErrorMessage("WaitCommEvent()"); | |
| 349 | + break; | |
| 350 | + } | |
| 351 | + } | |
| 352 | + } | |
| 353 | + else | |
| 354 | + { | |
| 355 | + // If WaitCommEvent() returns TRUE, check to be sure there are | |
| 356 | + // actually bytes in the buffer to read. | |
| 357 | + // | |
| 358 | + // If you are reading more than one byte at a time from the buffer | |
| 359 | + // (which this program does not do) you will have the situation occur | |
| 360 | + // where the first byte to arrive will cause the WaitForMultipleObjects() | |
| 361 | + // function to stop waiting. The WaitForMultipleObjects() function | |
| 362 | + // resets the event handle in m_OverlappedStruct.hEvent to the non-signelead state | |
| 363 | + // as it returns. | |
| 364 | + // | |
| 365 | + // If in the time between the reset of this event and the call to | |
| 366 | + // ReadFile() more bytes arrive, the m_OverlappedStruct.hEvent handle will be set again | |
| 367 | + // to the signeled state. When the call to ReadFile() occurs, it will | |
| 368 | + // read all of the bytes from the buffer, and the program will | |
| 369 | + // loop back around to WaitCommEvent(). | |
| 370 | + // | |
| 371 | + // At this point you will be in the situation where m_OverlappedStruct.hEvent is set, | |
| 372 | + // but there are no bytes available to read. If you proceed and call | |
| 373 | + // ReadFile(), it will return immediatly due to the async port setup, but | |
| 374 | + // GetOverlappedResults() will not return until the next character arrives. | |
| 375 | + // | |
| 376 | + // It is not desirable for the GetOverlappedResults() function to be in | |
| 377 | + // this state. The thread shutdown event (event 0) and the WriteFile() | |
| 378 | + // event (Event2) will not work if the thread is blocked by GetOverlappedResults(). | |
| 379 | + // | |
| 380 | + // The solution to this is to check the buffer with a call to ClearCommError(). | |
| 381 | + // This call will reset the event handle, and if there are no bytes to read | |
| 382 | + // we can loop back through WaitCommEvent() again, then proceed. | |
| 383 | + // If there are really bytes to read, do nothing and proceed. | |
| 384 | + | |
| 385 | + bResult = ClearCommError(port->m_hComm, &dwError, &comstat); | |
| 386 | + | |
| 387 | + if (comstat.cbInQue == 0) | |
| 388 | + continue; | |
| 389 | + } // end if bResult | |
| 390 | + | |
| 391 | + // Main wait function. This function will normally block the thread | |
| 392 | + // until one of nine events occur that require action. | |
| 393 | + Event = WaitForMultipleObjects(3, port->m_hEventArray, FALSE, INFINITE); | |
| 394 | + | |
| 395 | + switch (Event) | |
| 396 | + { | |
| 397 | + case 0: | |
| 398 | + { | |
| 399 | + // Shutdown event. This is event zero so it will be | |
| 400 | + // the higest priority and be serviced first. | |
| 401 | + | |
| 402 | + port->m_bThreadAlive = FALSE; | |
| 403 | + | |
| 404 | + // Kill this thread. break is not needed, but makes me feel better. | |
| 405 | + //AfxEndThread(100); | |
| 406 | + ::ExitThread(100); | |
| 407 | + | |
| 408 | + break; | |
| 409 | + } | |
| 410 | + case 1: // read event | |
| 411 | + { | |
| 412 | + GetCommMask(port->m_hComm, &CommEvent); | |
| 413 | + if (CommEvent & EV_RXCHAR) //接收到字符,并置于输入缓冲区中 | |
| 414 | + ReceiveChar(port); | |
| 415 | + | |
| 416 | + if (CommEvent & EV_CTS) //CTS信号状态发生变化 | |
| 417 | + ::SendMessage(port->m_pOwner, WM_COMM_CTS_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr); | |
| 418 | + if (CommEvent & EV_RXFLAG) //接收到事件字符,并置于输入缓冲区中 | |
| 419 | + ::SendMessage(port->m_pOwner, WM_COMM_RXFLAG_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr); | |
| 420 | + if (CommEvent & EV_BREAK) //输入中发生中断 | |
| 421 | + ::SendMessage(port->m_pOwner, WM_COMM_BREAK_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr); | |
| 422 | + if (CommEvent & EV_ERR) //发生线路状态错误,线路状态错误包括CE_FRAME,CE_OVERRUN和CE_RXPARITY | |
| 423 | + ::SendMessage(port->m_pOwner, WM_COMM_ERR_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr); | |
| 424 | + if (CommEvent & EV_RING) //检测到振铃指示 | |
| 425 | + ::SendMessage(port->m_pOwner, WM_COMM_RING_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr); | |
| 426 | + | |
| 427 | + break; | |
| 428 | + } | |
| 429 | + case 2: // write event | |
| 430 | + { | |
| 431 | + // Write character event from port | |
| 432 | + WriteChar(port); | |
| 433 | + break; | |
| 434 | + } | |
| 435 | + | |
| 436 | + } // end switch | |
| 437 | + | |
| 438 | + } // close forever loop | |
| 439 | + | |
| 440 | + return 0; | |
| 441 | +} | |
| 442 | + | |
| 443 | +// | |
| 444 | +// start comm watching | |
| 445 | +// | |
| 446 | +BOOL CSerialPort::StartMonitoring() | |
| 447 | +{ | |
| 448 | + //if (!(m_Thread = AfxBeginThread(CommThread, this))) | |
| 449 | + if (!(m_Thread = ::CreateThread (NULL, 0, CommThread, this, 0, NULL ))) | |
| 450 | + return FALSE; | |
| 451 | + //TRACE("Thread started\n"); | |
| 452 | + return TRUE; | |
| 453 | +} | |
| 454 | + | |
| 455 | +// | |
| 456 | +// Restart the comm thread | |
| 457 | +// | |
| 458 | +BOOL CSerialPort::RestartMonitoring() | |
| 459 | +{ | |
| 460 | + //TRACE("Thread resumed\n"); | |
| 461 | + //m_Thread->ResumeThread(); | |
| 462 | + ::ResumeThread(m_Thread); | |
| 463 | + return TRUE; | |
| 464 | +} | |
| 465 | + | |
| 466 | + | |
| 467 | +// | |
| 468 | +// Suspend the comm thread | |
| 469 | +// | |
| 470 | +BOOL CSerialPort::StopMonitoring() | |
| 471 | +{ | |
| 472 | + //TRACE("Thread suspended\n"); | |
| 473 | + //m_Thread->SuspendThread(); | |
| 474 | + ::SuspendThread(m_Thread); | |
| 475 | + return TRUE; | |
| 476 | +} | |
| 477 | + | |
| 478 | + | |
| 479 | +// | |
| 480 | +// If there is a error, give the right message | |
| 481 | +// | |
| 482 | +void CSerialPort::ProcessErrorMessage(char* ErrorText) | |
| 483 | +{ | |
| 484 | + char *Temp = new char[200]; | |
| 485 | + | |
| 486 | + LPVOID lpMsgBuf; | |
| 487 | + | |
| 488 | + FormatMessage( | |
| 489 | + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, | |
| 490 | + NULL, | |
| 491 | + GetLastError(), | |
| 492 | + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language | |
| 493 | + (LPTSTR) &lpMsgBuf, | |
| 494 | + 0, | |
| 495 | + NULL | |
| 496 | + ); | |
| 497 | + | |
| 498 | + sprintf(Temp, "WARNING: %s Failed with the following error: \n%s\nPort: %d\n", (char*)ErrorText, lpMsgBuf, m_nPortNr); | |
| 499 | + MessageBox(NULL, Temp, "Application Error", MB_ICONSTOP); | |
| 500 | + | |
| 501 | + LocalFree(lpMsgBuf); | |
| 502 | + delete[] Temp; | |
| 503 | +} | |
| 504 | + | |
| 505 | +// | |
| 506 | +// Write a character. | |
| 507 | +// | |
| 508 | +void CSerialPort::WriteChar(CSerialPort* port) | |
| 509 | +{ | |
| 510 | + BOOL bWrite = TRUE; | |
| 511 | + BOOL bResult = TRUE; | |
| 512 | + | |
| 513 | + DWORD BytesSent = 0; | |
| 514 | + DWORD SendLen = port->m_nWriteSize; | |
| 515 | + ResetEvent(port->m_hWriteEvent); | |
| 516 | + | |
| 517 | + | |
| 518 | + // Gain ownership of the critical section | |
| 519 | + EnterCriticalSection(&port->m_csCommunicationSync); | |
| 520 | + | |
| 521 | + if (bWrite) | |
| 522 | + { | |
| 523 | + // Initailize variables | |
| 524 | + port->m_ov.Offset = 0; | |
| 525 | + port->m_ov.OffsetHigh = 0; | |
| 526 | + | |
| 527 | + // Clear buffer | |
| 528 | + PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT); | |
| 529 | + | |
| 530 | + bResult = WriteFile(port->m_hComm, // Handle to COMM Port | |
| 531 | + port->m_szWriteBuffer, // Pointer to message buffer in calling finction | |
| 532 | + SendLen, // add by mrlong | |
| 533 | + //strlen((char*)port->m_szWriteBuffer), // Length of message to send | |
| 534 | + &BytesSent, // Where to store the number of bytes sent | |
| 535 | + &port->m_ov); // Overlapped structure | |
| 536 | + | |
| 537 | + // deal with any error codes | |
| 538 | + if (!bResult) | |
| 539 | + { | |
| 540 | + DWORD dwError = GetLastError(); | |
| 541 | + switch (dwError) | |
| 542 | + { | |
| 543 | + case ERROR_IO_PENDING: | |
| 544 | + { | |
| 545 | + // continue to GetOverlappedResults() | |
| 546 | + BytesSent = 0; | |
| 547 | + bWrite = FALSE; | |
| 548 | + break; | |
| 549 | + } | |
| 550 | + default: | |
| 551 | + { | |
| 552 | + // all other error codes | |
| 553 | + port->ProcessErrorMessage("WriteFile()"); | |
| 554 | + } | |
| 555 | + } | |
| 556 | + } | |
| 557 | + else | |
| 558 | + { | |
| 559 | + LeaveCriticalSection(&port->m_csCommunicationSync); | |
| 560 | + } | |
| 561 | + } // end if(bWrite) | |
| 562 | + | |
| 563 | + if (!bWrite) | |
| 564 | + { | |
| 565 | + bWrite = TRUE; | |
| 566 | + | |
| 567 | + bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port | |
| 568 | + &port->m_ov, // Overlapped structure | |
| 569 | + &BytesSent, // Stores number of bytes sent | |
| 570 | + TRUE); // Wait flag | |
| 571 | + | |
| 572 | + LeaveCriticalSection(&port->m_csCommunicationSync); | |
| 573 | + | |
| 574 | + // deal with the error code | |
| 575 | + if (!bResult) | |
| 576 | + { | |
| 577 | + port->ProcessErrorMessage("GetOverlappedResults() in WriteFile()"); | |
| 578 | + } | |
| 579 | + } // end if (!bWrite) | |
| 580 | + | |
| 581 | + // Verify that the data size send equals what we tried to send | |
| 582 | + if (BytesSent != SendLen /*strlen((char*)port->m_szWriteBuffer)*/) // add by | |
| 583 | + { | |
| 584 | + //TRACE("WARNING: WriteFile() error.. Bytes Sent: %d; Message Length: %d\n", BytesSent, strlen((char*)port->m_szWriteBuffer)); | |
| 585 | + } | |
| 586 | +} | |
| 587 | + | |
| 588 | +// | |
| 589 | +// Character received. Inform the owner | |
| 590 | +// | |
| 591 | +void CSerialPort::ReceiveChar(CSerialPort* port) | |
| 592 | +{ | |
| 593 | + BOOL bRead = TRUE; | |
| 594 | + BOOL bResult = TRUE; | |
| 595 | + DWORD dwError = 0; | |
| 596 | + DWORD BytesRead = 0; | |
| 597 | + DWORD lparam = 0; | |
| 598 | + COMSTAT comstat; | |
| 599 | + unsigned char RXBuff; | |
| 600 | + | |
| 601 | + for (;;) | |
| 602 | + { | |
| 603 | + //add by liquanhai 2011-11-06 防止死锁 | |
| 604 | + if(WaitForSingleObject(port->m_hShutdownEvent,0)==WAIT_OBJECT_0) | |
| 605 | + return; | |
| 606 | + | |
| 607 | + // Gain ownership of the comm port critical section. | |
| 608 | + // This process guarantees no other part of this program | |
| 609 | + // is using the port object. | |
| 610 | + | |
| 611 | + EnterCriticalSection(&port->m_csCommunicationSync); | |
| 612 | + | |
| 613 | + // ClearCommError() will update the COMSTAT structure and | |
| 614 | + // clear any other errors. | |
| 615 | + | |
| 616 | + bResult = ClearCommError(port->m_hComm, &dwError, &comstat); | |
| 617 | + | |
| 618 | + LeaveCriticalSection(&port->m_csCommunicationSync); | |
| 619 | + | |
| 620 | + // start forever loop. I use this type of loop because I | |
| 621 | + // do not know at runtime how many loops this will have to | |
| 622 | + // run. My solution is to start a forever loop and to | |
| 623 | + // break out of it when I have processed all of the | |
| 624 | + // data available. Be careful with this approach and | |
| 625 | + // be sure your loop will exit. | |
| 626 | + // My reasons for this are not as clear in this sample | |
| 627 | + // as it is in my production code, but I have found this | |
| 628 | + // solutiion to be the most efficient way to do this. | |
| 629 | + | |
| 630 | + if (comstat.cbInQue == 0) | |
| 631 | + { | |
| 632 | + // break out when all bytes have been read | |
| 633 | + break; | |
| 634 | + } | |
| 635 | + | |
| 636 | + EnterCriticalSection(&port->m_csCommunicationSync); | |
| 637 | + | |
| 638 | + if (bRead) | |
| 639 | + { | |
| 640 | + bResult = ReadFile(port->m_hComm, // Handle to COMM port | |
| 641 | + &RXBuff, // RX Buffer Pointer | |
| 642 | + 1, // Read one byte | |
| 643 | + &BytesRead, // Stores number of bytes read | |
| 644 | + &port->m_ov); // pointer to the m_ov structure | |
| 645 | + // deal with the error code | |
| 646 | + if (!bResult) | |
| 647 | + { | |
| 648 | + switch (dwError = GetLastError()) | |
| 649 | + { | |
| 650 | + case ERROR_IO_PENDING: | |
| 651 | + { | |
| 652 | + // asynchronous i/o is still in progress | |
| 653 | + // Proceed on to GetOverlappedResults(); | |
| 654 | + bRead = FALSE; | |
| 655 | + break; | |
| 656 | + } | |
| 657 | + default: | |
| 658 | + { | |
| 659 | + // Another error has occured. Process this error. | |
| 660 | + port->ProcessErrorMessage("ReadFile()"); | |
| 661 | + break; | |
| 662 | + } | |
| 663 | + } | |
| 664 | + } | |
| 665 | + else | |
| 666 | + { | |
| 667 | + // ReadFile() returned complete. It is not necessary to call GetOverlappedResults() | |
| 668 | + bRead = TRUE; | |
| 669 | + } | |
| 670 | + } // close if (bRead) | |
| 671 | + | |
| 672 | + if (!bRead) | |
| 673 | + { | |
| 674 | + bRead = TRUE; | |
| 675 | + bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port | |
| 676 | + &port->m_ov, // Overlapped structure | |
| 677 | + &BytesRead, // Stores number of bytes read | |
| 678 | + TRUE); // Wait flag | |
| 679 | + | |
| 680 | + // deal with the error code | |
| 681 | + if (!bResult) | |
| 682 | + { | |
| 683 | + port->ProcessErrorMessage("GetOverlappedResults() in ReadFile()"); | |
| 684 | + } | |
| 685 | + } // close if (!bRead) | |
| 686 | + | |
| 687 | + LeaveCriticalSection(&port->m_csCommunicationSync); | |
| 688 | + | |
| 689 | + // notify parent that a byte was received | |
| 690 | + lparam = RXBuff; | |
| 691 | + lparam = lparam<<16|port->m_nPortNr; | |
| 692 | + ::SendMessage((port->m_pOwner), WM_COMM_RXCHAR, port->m_userdata,lparam); | |
| 693 | + } // end forever loop | |
| 694 | + | |
| 695 | +} | |
| 696 | + | |
| 697 | +// | |
| 698 | +// Write a string to the port | |
| 699 | +// | |
| 700 | +void CSerialPort::WriteToPort(char* string) | |
| 701 | +{ | |
| 702 | + assert(m_hComm != 0); | |
| 703 | + | |
| 704 | + memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer)); | |
| 705 | + strcpy(m_szWriteBuffer, string); | |
| 706 | + m_nWriteSize=strlen(string); // add by mrlong | |
| 707 | + // set event for write | |
| 708 | + SetEvent(m_hWriteEvent); | |
| 709 | +} | |
| 710 | + | |
| 711 | +// | |
| 712 | +// Return the device control block | |
| 713 | +// | |
| 714 | +DCB CSerialPort::GetDCB() | |
| 715 | +{ | |
| 716 | + return m_dcb; | |
| 717 | +} | |
| 718 | + | |
| 719 | +// | |
| 720 | +// Return the communication event masks | |
| 721 | +// | |
| 722 | +DWORD CSerialPort::GetCommEvents() | |
| 723 | +{ | |
| 724 | + return m_dwCommEvents; | |
| 725 | +} | |
| 726 | + | |
| 727 | +// | |
| 728 | +// Return the output buffer size | |
| 729 | +// | |
| 730 | +DWORD CSerialPort::GetWriteBufferSize() | |
| 731 | +{ | |
| 732 | + return m_nWriteBufferSize; | |
| 733 | +} | |
| 734 | + | |
| 735 | +BOOL CSerialPort::IsOpen() | |
| 736 | +{ | |
| 737 | + return m_hComm != NULL; | |
| 738 | +} | |
| 739 | + | |
| 740 | +void CSerialPort::ClosePort() | |
| 741 | +{ | |
| 742 | + MSG message; | |
| 743 | + do | |
| 744 | + { | |
| 745 | + SetEvent(m_hShutdownEvent); | |
| 746 | + if(::PeekMessage(&message,m_pOwner,0,0,PM_REMOVE)) | |
| 747 | + { | |
| 748 | + ::TranslateMessage(&message); | |
| 749 | + ::DispatchMessage(&message); | |
| 750 | + } | |
| 751 | + } while (m_bThreadAlive); | |
| 752 | + | |
| 753 | + // if the port is still opened: close it | |
| 754 | + if (m_hComm != NULL) | |
| 755 | + { | |
| 756 | + CloseHandle(m_hComm); | |
| 757 | + m_hComm = NULL; | |
| 758 | + } | |
| 759 | + | |
| 760 | + // Close Handles | |
| 761 | + if(m_hShutdownEvent!=NULL) | |
| 762 | + ResetEvent(m_hShutdownEvent); | |
| 763 | + if(m_ov.hEvent!=NULL) | |
| 764 | + ResetEvent(m_ov.hEvent); | |
| 765 | + if(m_hWriteEvent!=NULL) | |
| 766 | + ResetEvent(m_hWriteEvent); | |
| 767 | + | |
| 768 | + //delete [] m_szWriteBuffer; | |
| 769 | + | |
| 770 | +} | |
| 771 | + | |
| 772 | +void CSerialPort::WriteToPort(char* string,int n) | |
| 773 | +{ | |
| 774 | + assert(m_hComm != 0); | |
| 775 | + memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer)); | |
| 776 | + memcpy(m_szWriteBuffer, string, n); | |
| 777 | + m_nWriteSize = n; | |
| 778 | + | |
| 779 | + // set event for write | |
| 780 | + SetEvent(m_hWriteEvent); | |
| 781 | +} | |
| 782 | + | |
| 783 | +void CSerialPort::WriteToPort(LPCTSTR string) | |
| 784 | +{ | |
| 785 | + assert(m_hComm != 0); | |
| 786 | + memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer)); | |
| 787 | + strcpy(m_szWriteBuffer, string); | |
| 788 | + m_nWriteSize=strlen(string); | |
| 789 | + // set event for write | |
| 790 | + SetEvent(m_hWriteEvent); | |
| 791 | +} | |
| 792 | + | |
| 793 | +void CSerialPort::WriteToPort(BYTE* Buffer, int n) | |
| 794 | +{ | |
| 795 | + assert(m_hComm != 0); | |
| 796 | + memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer)); | |
| 797 | + int i; | |
| 798 | + for(i=0; i<n; i++) | |
| 799 | + { | |
| 800 | + m_szWriteBuffer[i] = Buffer[i]; | |
| 801 | + } | |
| 802 | + m_nWriteSize=n; | |
| 803 | + | |
| 804 | + // set event for write | |
| 805 | + SetEvent(m_hWriteEvent); | |
| 806 | +} | |
| 807 | + | |
| 808 | + | |
| 809 | +void CSerialPort::SendData(LPCTSTR lpszData, const int nLength) | |
| 810 | +{ | |
| 811 | + assert(m_hComm != 0); | |
| 812 | + memset(m_szWriteBuffer, 0, nLength); | |
| 813 | + strcpy(m_szWriteBuffer, lpszData); | |
| 814 | + m_nWriteSize=nLength; | |
| 815 | + // set event for write | |
| 816 | + SetEvent(m_hWriteEvent); | |
| 817 | +} | |
| 818 | + | |
| 819 | +BOOL CSerialPort::RecvData(LPTSTR lpszData, const int nSize) | |
| 820 | +{ | |
| 821 | + // | |
| 822 | + //接收数据 | |
| 823 | + // | |
| 824 | + assert(m_hComm!=0); | |
| 825 | + memset(lpszData,0,nSize); | |
| 826 | + DWORD mylen = 0; | |
| 827 | + DWORD mylen2 = 0; | |
| 828 | + while (mylen<nSize) { | |
| 829 | + if(!ReadFile(m_hComm,lpszData,nSize,&mylen2,NULL)) | |
| 830 | + return FALSE; | |
| 831 | + mylen += mylen2; | |
| 832 | + | |
| 833 | + | |
| 834 | + } | |
| 835 | + | |
| 836 | + return TRUE; | |
| 837 | +} | ... | ... |
SerialPort.h
0 → 100644
| 1 | +/* | |
| 2 | +** FILENAME CSerialPort.h | |
| 3 | +** | |
| 4 | +** PURPOSE This class can read, write and watch one serial port. | |
| 5 | +** It sends messages to its owner when something happends on the port | |
| 6 | +** The class creates a thread for reading and writing so the main | |
| 7 | +** program is not blocked. | |
| 8 | +** | |
| 9 | +** CREATION DATE 15-09-1997 | |
| 10 | +** LAST MODIFICATION 12-11-1997 | |
| 11 | +** | |
| 12 | +** AUTHOR Remon Spekreijse | |
| 13 | +** | |
| 14 | +** | |
| 15 | +************************************************************************************ | |
| 16 | +** author: mrlong date:2007-12-25 | |
| 17 | +** | |
| 18 | +** 改进 | |
| 19 | +** 1) 增加 ClosePort | |
| 20 | +** 2) 增加 WriteToPort 两个方法 | |
| 21 | +** 3) 增加 SendData 与 RecvData 方法 | |
| 22 | +************************************************************************************ | |
| 23 | +************************************************************************************ | |
| 24 | +** author:liquanhai date:2011-11-04 | |
| 25 | +** | |
| 26 | +** 改进 | |
| 27 | +** 1) 增加 ClosePort 中交出控制权,防止死锁问题 | |
| 28 | +** 2) 增加 ReceiveChar 中防止线程死锁 | |
| 29 | +************************************************************************************ | |
| 30 | +************************************************************************************ | |
| 31 | +** author:viruscamp date:2013-12-04 | |
| 32 | +** | |
| 33 | +** 改进 | |
| 34 | +** 1) 增加 IsOpen 判断是否打开 | |
| 35 | +** 2) 修正 InitPort 中 parity Odd Even 参数取值错误 | |
| 36 | +** 3) 修改 InitPort 中 portnr 取值范围,portnr>9 时特殊处理 | |
| 37 | +** 4) 取消对 MFC 的依赖,使用 HWND 替代 CWnd,使用 win32 thread 函数而不是 MFC 的 | |
| 38 | +** 5) 增加用户消息编号自定义,方法来自 CnComm | |
| 39 | +*/ | |
| 40 | + | |
| 41 | +#ifndef __SERIALPORT_H__ | |
| 42 | +#define __SERIALPORT_H__ | |
| 43 | + | |
| 44 | +#ifndef WM_COMM_MSG_BASE | |
| 45 | + #define WM_COMM_MSG_BASE WM_USER + 617 //!< 消息编号的基点 | |
| 46 | +#endif | |
| 47 | + | |
| 48 | +#define WM_COMM_BREAK_DETECTED WM_COMM_MSG_BASE + 1 // A break was detected on input. | |
| 49 | +#define WM_COMM_CTS_DETECTED WM_COMM_MSG_BASE + 2 // The CTS (clear-to-send) signal changed state. | |
| 50 | +#define WM_COMM_DSR_DETECTED WM_COMM_MSG_BASE + 3 // The DSR (data-set-ready) signal changed state. | |
| 51 | +#define WM_COMM_ERR_DETECTED WM_COMM_MSG_BASE + 4 // A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY. | |
| 52 | +#define WM_COMM_RING_DETECTED WM_COMM_MSG_BASE + 5 // A ring indicator was detected. | |
| 53 | +#define WM_COMM_RLSD_DETECTED WM_COMM_MSG_BASE + 6 // The RLSD (receive-line-signal-detect) signal changed state. | |
| 54 | +#define WM_COMM_RXCHAR WM_COMM_MSG_BASE + 7 // A character was received and placed in the input buffer. | |
| 55 | +#define WM_COMM_RXFLAG_DETECTED WM_COMM_MSG_BASE + 8 // The event character was received and placed in the input buffer. | |
| 56 | +#define WM_COMM_TXEMPTY_DETECTED WM_COMM_MSG_BASE + 9 // The last character in the output buffer was sent. | |
| 57 | + | |
| 58 | +class CSerialPort | |
| 59 | +{ | |
| 60 | +public: | |
| 61 | + // contruction and destruction | |
| 62 | + CSerialPort(); | |
| 63 | + virtual ~CSerialPort(); | |
| 64 | + | |
| 65 | + // port initialisation | |
| 66 | + BOOL InitPort(HWND pPortOwner, UINT portnr = 1, UINT baud = 19200, | |
| 67 | + char parity = 'N', UINT databits = 8, UINT stopsbits = 1, | |
| 68 | + DWORD dwCommEvents = EV_RXCHAR | EV_CTS, UINT nBufferSize = 512, | |
| 69 | + DWORD userdata = 0, | |
| 70 | + | |
| 71 | + DWORD ReadIntervalTimeout = 1000, | |
| 72 | + DWORD ReadTotalTimeoutMultiplier = 1000, | |
| 73 | + DWORD ReadTotalTimeoutConstant = 1000, | |
| 74 | + DWORD WriteTotalTimeoutMultiplier = 1000, | |
| 75 | + DWORD WriteTotalTimeoutConstant = 1000); | |
| 76 | + | |
| 77 | + // start/stop comm watching | |
| 78 | + BOOL StartMonitoring(); | |
| 79 | + BOOL RestartMonitoring(); | |
| 80 | + BOOL StopMonitoring(); | |
| 81 | + | |
| 82 | + DWORD GetWriteBufferSize(); | |
| 83 | + DWORD GetCommEvents(); | |
| 84 | + DCB GetDCB(); | |
| 85 | + | |
| 86 | + void WriteToPort(char* string); | |
| 87 | + void WriteToPort(char* string,int n); // add by mrlong 2007-12-25 | |
| 88 | + void WriteToPort(LPCTSTR string); // add by mrlong 2007-12-25 | |
| 89 | + void WriteToPort(BYTE* Buffer, int n);// add by mrlong | |
| 90 | + void ClosePort(); // add by mrlong 2007-12-2 | |
| 91 | + BOOL IsOpen(); | |
| 92 | + | |
| 93 | + void SendData(LPCTSTR lpszData, const int nLength); //串口发送函数 by mrlong 2008-2-15 | |
| 94 | + BOOL RecvData(LPTSTR lpszData, const int nSize); //串口接收函数 by mrlong 2008-2-15 | |
| 95 | + | |
| 96 | +protected: | |
| 97 | + // protected memberfunctions | |
| 98 | + void ProcessErrorMessage(char* ErrorText); | |
| 99 | + static DWORD WINAPI CommThread(LPVOID pParam); | |
| 100 | + static void ReceiveChar(CSerialPort* port); | |
| 101 | + static void WriteChar(CSerialPort* port); | |
| 102 | + | |
| 103 | + // thread | |
| 104 | + //CWinThread* m_Thread; | |
| 105 | + HANDLE m_Thread; | |
| 106 | + | |
| 107 | + // synchronisation objects | |
| 108 | + CRITICAL_SECTION m_csCommunicationSync; | |
| 109 | + BOOL m_bThreadAlive; | |
| 110 | + | |
| 111 | + // handles | |
| 112 | + HANDLE m_hShutdownEvent; //stop发生的事件 | |
| 113 | + HANDLE m_hComm; // read | |
| 114 | + HANDLE m_hWriteEvent; // write | |
| 115 | + | |
| 116 | + // Event array. | |
| 117 | + // One element is used for each event. There are two event handles for each port. | |
| 118 | + // A Write event and a receive character event which is located in the overlapped structure (m_ov.hEvent). | |
| 119 | + // There is a general shutdown when the port is closed. | |
| 120 | + HANDLE m_hEventArray[3]; | |
| 121 | + | |
| 122 | + // structures | |
| 123 | + OVERLAPPED m_ov; | |
| 124 | + COMMTIMEOUTS m_CommTimeouts; | |
| 125 | + DCB m_dcb; | |
| 126 | + | |
| 127 | + // owner window | |
| 128 | + //CWnd* m_pOwner; | |
| 129 | + HWND m_pOwner; | |
| 130 | + | |
| 131 | + | |
| 132 | + // misc | |
| 133 | + UINT m_nPortNr; //????? | |
| 134 | + char* m_szWriteBuffer; | |
| 135 | + DWORD m_dwCommEvents; | |
| 136 | + DWORD m_nWriteBufferSize; | |
| 137 | + | |
| 138 | + int m_nWriteSize; //add by mrlong 2007-12-25 | |
| 139 | + int m_userdata; | |
| 140 | +}; | |
| 141 | + | |
| 142 | +#endif __SERIALPORT_H__ | ... | ... |
Please
register
or
login
to post a comment