最近用到了串口编程,所以写篇随笔将其记录下来,能为其他人提供到一点帮助也好。
Windows下串口通信的基本流程
先来梳理一下在Windows下对串口读写数据操作的基本流程。在Windows下(linux也是)串口的读写是以文件的方式去操作的,所以使用WindowsAPI:CreateFile来打开串口,代码为:
LPCTSTR Comname = _T("COM3");
HANDLE hSerial;
hSerial = CreateFile(Comname, GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (hSerial == INVALID_HANDLE_VALUE)
{
printf("open fail \n");
CloseHandle(hSerial);
return -1;
}
打开串口后我们对串口设置缓冲去的大小:
SetupComm(hSerial, 1024, 1024);
接下来开始设置串口的参数:
DCB dcb;
GetCommState(hSerial, &dcb);
dcb.DCBlength = sizeof(DCB);
dcb.BaudRate = CBR_115200;
dcb.ByteSize = 8;
dcb.StopBits = ONESTOPBIT;
dcb.Parity = NOPARITY;
SetCommState(hSerial, &dcb);
然后对串口设置超时
COMMTIMEOUTS timeout;
timeout.ReadIntervalTimeout = MAXDWORD;
timeout.ReadTotalTimeoutConstant = 0;
timeout.ReadTotalTimeoutMultiplier = 0;
timeout.WriteTotalTimeoutConstant = 500;
timeout.WriteTotalTimeoutMultiplier = 5000;
SetCommTimeouts(hSerial, &timeout);
PurgeComm(hSerial, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR);
由于我们使用异步的方式打开的串口,所以需要一个OVERLAPPED结构,先创建它后面会用到
OVERLAPPED ov_write, ov_read;
memset(&ov_write, 0, sizeof(OVERLAPPED));
memset(&ov_read, 0, sizeof(OVERLAPPED));
ov_write.hEvent = CreateEvent(NULL, false, false, NULL);
ov_read.hEvent = CreateEvent(NULL, false, false, NULL);
然后为串口设置言关心的事件
SetCommMask(hSerial, EV_RXCHAR);
然后就是获取串口接收到的内容了,这里新开一个线程去后台监听串口是否收到数据,当有数据时将数据取出来,首先需要写一个读取串口的方法:
void readData(HANDLE serial, OVERLAPPED* ov_r)
{
DWORD dwGetEventMask = 0;
DWORD TrByte = 0;
BOOL Status = 0;
DWORD CError = 0;
COMSTAT comstat;
char readBuffer[1024] = { 0 };
while (true)
{
Status = WaitCommEvent(serial, &dwGetEventMask, ov_r);
if (Status == FALSE && GetLastError() == ERROR_IO_PENDING)
{
Status = GetOverlappedResult(serial, ov_r, &TrByte, TRUE);
}
ClearCommError(serial, &CError, &comstat);
if (Status == TRUE && dwGetEventMask & EV_RXCHAR && comstat.cbInQue > 0)
{
DWORD readedSize = 0;
memset(readBuffer, 0, sizeof(readBuffer));
Status = ReadFile(serial, readBuffer, sizeof(readBuffer), &readedSize, ov_r);
if (Status)
{
std::cout << "Read: " << readBuffer << std::endl;
}
PurgeComm(serial, PURGE_RXABORT | PURGE_RXCLEAR);
}
}
}
然后在刚才SetCommMask(hSerial, EV_RXCHAR);的后面启动线程去执行这个函数
std::thread readThread(&readData, hSerial, &ov_read);
至此便开始了对串口的监听,如果要向串口调用WindowsAPI函数WriteFile即可:
WriteFile(hSerial, writeBuffer, sizeof(writeBuffer), &writedSize, &ov_write);
C++封装
最后为了方便以后的复用,我使用C++对其进行了封装形成一个类,使用方法也简单了很多,以下为一个简单的示例:
#include "JSerialPortTool.h"
void showReadContant(char*indata);
int main()
{
JSerialPortTool serialTool(_T("COM1"));
serialTool.SetComConf(CBR_115200, 8, 1, NOPARITY);
serialTool.SetTimeOut(MAXDWORD, 0, 0, 500, 5000);
serialTool.connectReadSlot(showReadContant);
while (true)
{
char writebuff[1024] = { 0 };
std::cin >> writebuff;
serialTool.WriteData(writebuff);
}
return 0;
}
void showReadContant(char*indata)
{
std::cout << "get contance:" << indata << std::endl;
}
资源下载
CSDN下载 Gitee下载
|