关于Win32串口
因为近段时间接触Hid相对来说多一些,由此忽略了串口中获取cbInQue这个重要的东西,下面是错误代码
1 // Win32SerialPortLib.cpp : 定义 DLL 应用程序的导出函数。 2 // 3 4 #include "stdafx.h" 5 #include "Win32SerialPortLib.h" 6 #include <stdio.h> 7 8 HANDLE hcomm; 9 EXTERN_C WIN32SERIALPORTLIB_API bool Open(char *com, int baud) 10 { 11 char szDCB[50]; 12 sprintf_s(szDCB, "baud=%d parity=%c data=%d stop=%d", baud, 'N', 8, 1); 13 hcomm = CreateFileA(com, 14 GENERIC_READ | GENERIC_WRITE, 15 0, 16 NULL, 17 OPEN_EXISTING, 18 0, 19 0); 20 if (hcomm == INVALID_HANDLE_VALUE) return false; 21 return true; 22 } 23 24 EXTERN_C WIN32SERIALPORTLIB_API int Read(unsigned char *data, DWORD len) 25 { 26 DWORD readed; 27 BOOL rt = ReadFile(hcomm, data, len, &readed, NULL); 28 if (!rt) 29 { 30 PurgeComm(hcomm, PURGE_RXCLEAR | PURGE_RXABORT); 31 return 0; 32 } 33 34 return readed; 35 } 36 37 EXTERN_C WIN32SERIALPORTLIB_API void Write(unsigned char *data, DWORD len) 38 { 39 if (hcomm == INVALID_HANDLE_VALUE) return; 40 DWORD writed; 41 BOOL rt = WriteFile(hcomm, data, len, &writed, NULL); 42 if (!rt) 43 { 44 PurgeComm(hcomm, PURGE_TXCLEAR | PURGE_TXABORT); 45 return; 46 } 47 } 48 49 EXTERN_C WIN32SERIALPORTLIB_API void Close() 50 { 51 if (hcomm != INVALID_HANDLE_VALUE) 52 { 53 CloseHandle(hcomm); 54 hcomm = INVALID_HANDLE_VALUE; 55 } 56 }
经过测试
1 // Win32SerialPortTest.cpp : 定义控制台应用程序的入口点。 2 // 3 4 #include "stdafx.h" 5 #include <thread> 6 #include <iostream> 7 #include "Win32SerialPort.h" 8 using namespace std; 9 10 bool _stoprequired = false; 11 12 void read_callback(); 13 void printhex(unsigned char *data, int len); 14 15 int _tmain(int argc, _TCHAR* argv[]) 16 { 17 Open("COM5", 9600); 18 thread read_t(read_callback); 19 read_t.detach(); 20 21 cout << endl; 22 cout << "continue in main thread." << endl; 23 //ab 69 42 01 fe c0 00 00 c0 24 unsigned char data[] = { 0xab, 0x69, 0x42, 0x01, 0xfe, 0xc0, 0x00, 0x00, 0xc0 }; 25 Write(data, sizeof(data) / sizeof(unsigned char)); 26 27 // 获取标准输入输出设备句柄 28 HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); 29 HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE); 30 31 DWORD dwRes, dwState = 0; 32 INPUT_RECORD keyRec; 33 COORD crHome = { 0, 0 }, crPos; 34 char ch; 35 CONSOLE_SCREEN_BUFFER_INFO bInfo; 36 while (true) 37 { 38 ReadConsoleInput(hIn, &keyRec, 1, &dwRes); 39 if (keyRec.EventType == KEY_EVENT) 40 { 41 // Press key down 42 if (keyRec.Event.KeyEvent.bKeyDown) 43 { 44 // 基础功能键 45 switch (keyRec.Event.KeyEvent.wVirtualKeyCode) 46 { 47 // 回车 48 case VK_RETURN: 49 printf("\n"); 50 break; 51 52 // 空格 53 case VK_SPACE: 54 Write(data, sizeof(data) / sizeof(unsigned char)); 55 break; 56 57 case VK_BACK: // 按删除时删掉一个字符(只能当前行操作) 58 GetConsoleScreenBufferInfo(hOut, &bInfo); 59 crPos = bInfo.dwCursorPosition; 60 if (crPos.X != 0) 61 { 62 crPos.X -= 1; 63 } 64 SetConsoleCursorPosition(hOut, crPos); 65 printf(" "); 66 SetConsoleCursorPosition(hOut, crPos); 67 break; 68 69 case VK_ESCAPE: // 按ESC键时退出 70 _stoprequired = true; 71 Close(); 72 Sleep(50); 73 CloseHandle(hOut); // 关闭标准输出设备句柄 74 CloseHandle(hIn); // 关闭标准输入设备句柄 75 return 0; 76 77 default: 78 break; 79 } 80 81 // 打印字符 82 ch = keyRec.Event.KeyEvent.uChar.AsciiChar; 83 // 输出可以打印的字符(详参ASCII表) 84 if (ch > 0x20 && ch < 0x7e) 85 { 86 putchar(ch); 87 } 88 } 89 } 90 } 91 92 return 0; 93 } 94 95 void read_callback() 96 { 97 unsigned char buff[256]; 98 while (!_stoprequired) 99 { 100 int n = Read(buff, 256); 101 if (n > 0) 102 { 103 printhex(buff, n); 104 } 105 //cout << "detach test." << endl; 106 //this_thread::sleep_for(chrono::seconds(1)); 107 } 108 } 109 110 void printhex(unsigned char *data, int len) 111 { 112 for (int i = 0; i < len; i++) 113 { 114 printf("%2x ", data[i]); 115 } 116 printf("\n"); 117 }
表现现象是Read函数不单单是阻塞,还会让Write函数也阻塞,这种情况是是没有留意到的地方,此处作一个记录.
异步IO版本,异步代码上方的注释为同步IO版本
1 // Win32SerialPortLib.cpp : 定义 DLL 应用程序的导出函数。 2 // 3 4 #include "stdafx.h" 5 #include "Win32SerialPortLib.h" 6 #include <stdio.h> 7 8 HANDLE hcomm; 9 OVERLAPPED ovw; 10 OVERLAPPED ovr; 11 EXTERN_C WIN32SERIALPORTLIB_API HANDLE Open(char *com, int baud) 12 { 13 char szDCB[50]; 14 sprintf_s(szDCB, "baud=%d parity=%c data=%d stop=%d", baud, 'N', 8, 1); 15 //hcomm = CreateFileA(com, 16 // GENERIC_READ | GENERIC_WRITE, 17 // 0, 18 // NULL, 19 // OPEN_EXISTING, 20 // 0, 21 // 0); 22 hcomm = CreateFileA(com, 23 GENERIC_READ | GENERIC_WRITE, 24 0, 25 NULL, 26 OPEN_EXISTING, 27 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 28 0); 29 if (hcomm == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE; 30 // 设置缓冲大小 31 BOOL rt = SetupComm(hcomm, 4096, 4096); 32 if (!rt) return INVALID_HANDLE_VALUE; 33 // 设置超时 34 COMMTIMEOUTS commtimeouts; 35 commtimeouts.ReadIntervalTimeout = 0; 36 commtimeouts.ReadTotalTimeoutConstant = 0; 37 commtimeouts.ReadTotalTimeoutMultiplier = 0; 38 commtimeouts.WriteTotalTimeoutConstant = 0; 39 commtimeouts.WriteTotalTimeoutMultiplier = 0; 40 rt = SetCommTimeouts(hcomm, &commtimeouts); 41 if (!rt) return INVALID_HANDLE_VALUE; 42 // 设置DCB 43 DCB dcb; 44 #ifdef UNICODE 45 DWORD num = MultiByteToWideChar(CP_ACP, 0, szDCB, -1, NULL, 0); 46 wchar_t *pwStr = new wchar_t[num]; 47 MultiByteToWideChar(CP_ACP, 0, szDCB, -1, pwStr, num); 48 #else 49 DWORD num = strlen(szDCB) + 1; 50 char *pwStr = new char[num]; 51 strcpy(pwStr, szDCB); 52 #endif 53 rt = GetCommState(hcomm, &dcb); 54 if (!rt) return INVALID_HANDLE_VALUE; 55 rt = BuildCommDCB(pwStr, &dcb); 56 if (!rt) return INVALID_HANDLE_VALUE; 57 delete[] pwStr; 58 rt = SetCommState(hcomm, &dcb); 59 if (!rt) return INVALID_HANDLE_VALUE; 60 // 清空串口缓冲区 61 rt = PurgeComm(hcomm, PURGE_RXCLEAR | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_TXABORT); 62 if (!rt) return INVALID_HANDLE_VALUE; 63 64 return hcomm; 65 } 66 67 EXTERN_C WIN32SERIALPORTLIB_API int Read(unsigned char *data, DWORD len) 68 { 69 DWORD read_size = 0; 70 if (hcomm == INVALID_HANDLE_VALUE) return 0; 71 COMSTAT comstat; 72 DWORD error; 73 ClearCommError(hcomm, &error, &comstat); 74 // 串口有错误 75 if (error > 0) 76 { 77 PurgeComm(hcomm, PURGE_RXCLEAR | PURGE_RXABORT); 78 return read_size; 79 } 80 if (comstat.cbInQue > 0) 81 { 82 //BOOL rt = ReadFile(hcomm, data, comstat.cbInQue, &read_size, NULL); 83 //if (!rt) return 0; 84 BOOL rt = ReadFile(hcomm, data, comstat.cbInQue, &read_size, &ovr); 85 if (!rt) 86 { 87 rt = GetLastError(); 88 if (rt == ERROR_IO_PENDING) 89 { 90 // 此时是否需要对IO状态作处理看需求,处理与否不会影响IO 91 //rt = WaitForSingleObject(hcomm, 50); 92 //switch (rt) 93 //{ 94 //case WAIT_OBJECT_0: 95 // cout << "指定的对象处于有信号状态" << endl; 96 // if (GetOverlappedResult(hcomm, &ovr, &read_size, FALSE)) 97 // cout << "read " << read_size << " bytes" << endl; 98 // break; 99 //case WAIT_TIMEOUT: 100 // cout << "等待超时" << endl; 101 // break; 102 //case WAIT_FAILED: 103 // cout << "出现错误,CODE [" << GetLastError() << "]" << endl; 104 // break; 105 //case WAIT_ABANDONED: 106 // cout << "当Handle为mutex时,如果拥有mutex的线程在结束时没有释放核心对象会引发此返回值" << endl; 107 // break; 108 //} 109 } 110 else 111 { 112 CancelIo(hcomm); 113 return 0; 114 } 115 } 116 } 117 118 return read_size; 119 } 120 121 EXTERN_C WIN32SERIALPORTLIB_API void Write(unsigned char *data, DWORD len) 122 { 123 DWORD write_size; 124 if (hcomm == INVALID_HANDLE_VALUE) return; 125 COMSTAT comstat; 126 DWORD error; 127 BOOL rt = ClearCommError(hcomm, &error, &comstat); 128 if (!rt) return; 129 // 串口有错误 130 if (error > 0) 131 { 132 PurgeComm(hcomm, PURGE_TXCLEAR | PURGE_TXABORT); 133 return; 134 } 135 136 WriteFile(hcomm, data, len, &write_size, NULL); 137 rt = WriteFile(hcomm, data, len, &write_size, &ovw); 138 if (!rt) 139 { 140 rt = GetLastError(); 141 if (rt == ERROR_IO_PENDING) 142 { 143 // 此时是否需要对IO状态作处理看需求,处理与否不会影响IO 144 //rt = WaitForSingleObject(hcomm, 50); 145 //switch (rt) 146 //{ 147 //case WAIT_OBJECT_0: 148 // cout << "指定的对象处于有信号状态" << endl; 149 // if (GetOverlappedResult(hcomm, &ovr, &write_size, FALSE)) 150 // cout << "write " << write_size << " bytes" << endl; 151 // break; 152 //case WAIT_TIMEOUT: 153 // cout << "等待超时" << endl; 154 // break; 155 //case WAIT_FAILED: 156 // cout << "出现错误,CODE [" << GetLastError() << "]" << endl; 157 // break; 158 //case WAIT_ABANDONED: 159 // cout << "当Handle为mutex时,如果拥有mutex的线程在结束时没有释放核心对象会引发此返回值" << endl; 160 // break; 161 //} 162 } 163 else 164 { 165 CancelIo(hcomm); 166 return; 167 } 168 } 169 } 170 171 EXTERN_C WIN32SERIALPORTLIB_API void Close() 172 { 173 if (hcomm != INVALID_HANDLE_VALUE) 174 { 175 CancelIo(hcomm); 176 CloseHandle(hcomm); 177 hcomm = INVALID_HANDLE_VALUE; 178 } 179 }