EVC串口通信编程【转】
一、打开串口
hSerial = CreateFile(L"COM1:", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if(m_hSerial == NULL) { ///L"串口打开失败"; return; }
///配置串口
DCB PortDCB; PortDCB.DCBlength = sizeof(DCB); // 默认串口参数 GetCommState(hSerial, &PortDCB); PortDCB.BaudRate = 115200; // baud PortDCB.ByteSize = 8; // Number of bits/byte, 4-8 PortDCB.Parity = NOPARITY; PortDCB.StopBits = ONESTOPBIT; if (! SetCommState(hSerial, &PortDCB)) { ///L"配置串口失败"; return; }
配置超时值
COMMTIMEOUTS CommTimeouts;
GetCommTimeouts(m_hSerial, &CommTimeouts); CommTimeouts.ReadIntervalTimeout = MAXDWORD; CommTimeouts.ReadTotalTimeoutMultiplier = 10; CommTimeouts.ReadTotalTimeoutConstant = 10; CommTimeouts.WriteTotalTimeoutMultiplier = 50; CommTimeouts.WriteTotalTimeoutConstant = 100; if (!SetCommTimeouts(hSerial, &CommTimeouts)) { ///L"不能设置超时参数"; return; } |
CE的串口驱动不支持重叠,因此收和发就要分开。要接收串口数据就必须创建一个线程专门用于接收数据。串口的配置不需要设置很多参数,默认的配置大部分是不需要修改的。一般改动就是波特率、位数、奇偶校检等几项。超时值是需要改动的。ReadIntervalTimeout是指两个字符传送之间的超时时间。一次写操作的超时时间等于WriteTotalTimeoutMultiplier 乘以 要发送的字符数 加上WriteTotalTimeoutConstant。 单位是毫秒。读操作的超时和写类似。所以设置超时是一个关键。设置太小可能丢失数据。
二、关闭串口
关闭串口用关闭句柄函数。
if(hSerial != NULL) { CloseHandle(hSerial); hSerial = NULL; } |
三、向串口发送数据
WriteFile (hSerial, // 句柄 &Byte, // 数据缓冲区地址 nByte, // 数据大小 &dwNumBytes, // 返回发送出去的字节数 NULL // 不支持重叠 ); |
向串口发送数据一般都会成功。需要注意的是如果终端设备需要一定处理时间或者称反应时间的话,那么两个写操作之间一定要注意时间间隔不能太小。具体的时间由终端设备的反应时间和缓冲区大小有关。
四、读取串口数据
串口麻烦就麻烦在读取数据上。除了考虑及时的读取数据外,还要解决接收到的数据的处理工作。如果在读取串口数据的线程中安置数据处理工作,那么可能会丢失数据(终端设备发送数据但是没收到),也有可能不会丢失(终端设备发送的数据的时间、大小都是确定的)。如果肯定接收的数据在处理工作结束后终端设备才发送数据,那么完全可以将数据处理工作放在读取串口的线程中。对于及时的读取数据,下面提供了一种解决办法:
*** 假设接收的都是字符 ***
UINT ReadThread(LPVOID pParam) 接收串口数据线程 { HANDLE hPort = *(HANDLE*)pParam; BYTE Byte; int iCounter = 0; DWORD dwBytes; char ReceiveBuf[1000]; ///缓冲区的大小
SetCommMask (hPort, EV_RXCHAR); ///只接收字符 while (hPort != INVALID_HANDLE_VALUE) { DWORD dwCommStatus; WaitCommEvent(hPort, &dwCommStatus, 0); SetCommMask (hPort, EV_RXCHAR); ///重新设置要等待的信号 接收数据 do { ReadFile(hPort, &Byte, 1, &dwBytes, 0); if(dwBytes == 1) { ReceiveBuf[iCounter++] = Byte; if(iCounter == 1000) { ///L"接收缓冲区已满"; return -1; } } } while (dwBytes == 1); if(iCounter == 0) 没接到数据 { continue; } //保存数据 char* pTmp = new char[iCounter + 1]; if(pTmp == NULL) { ///L"内存不足,接收串口数据线程关闭"; return -1; } memcpy(pTmp, ReceiveBuf, iCounter); pTmp[iCounter] = NULL; 字符串结尾 创建新线程处理数据 AfxBeginThread(ProcessData, pTmp); 在ProcessData函数中处理数据。别忘了delete pTmp; iCounter = 0; 清空计数器 } ///end while return 0; } |
在ReadThread的代码中,对接收数据后的处理采用另外一个线程来执行,这种解决方法不适合所有需要,只适合读取终端设备返回来的少量数据。软件开发者要编写串口读线程代码应该具体问题具体分析。另外如果想在任意时刻停止读串口线程,应该按如下形式调用函数:
SetCommMask(hPort,EV_RXCHAR|EV_TXEMPTY);
EV_TXEMPTY表示当输出字符都发送出去作为一个事件。那么我们可以发送任意一个字符,这个字符发送出去后WaitCommEvent就返回。