WIN32控制台下的串口通信程序
Winodws平台下,文件、通信设备、命名管道、邮件槽、磁盘、控制台等都是以文件的形式存在,它们的创建于打开操作都是利用CreateFile()函数。在MSDN中CreateFile()的声明方式为:
[cpp] view plain copy
1. HANDLE WINAPI CreateFile(
2. _In_ LPCTSTR lpFileName, //文件名“COM1”,"COM2"等
3. _In_ DWORD dwDesiredAccess, //访问模式,读、写
4. _In_ DWORD dwShareMode, //共享模式,常为0表示独占方式
5. _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, //安全属性通常为NULL
6. _In_ DWORD dwCreationDisposition, //创建或打开方式
7. _In_ DWORD dwFlagsAndAttributes, //文件属性或标志
8. _In_opt_ HANDLE hTemplateFile //临时文件,又或者模板,通常为NULL
9. );
以下Win32控制台程序演示CreateFile()函数的调用方式,
[cpp] view plain copy
1. #include <iostream>
2. #include <cstdlib>
3. #include <Windows.h>
4.
5. using namespace std;
6.
7. bool OpenPort(); //打开串口
8. bool OpenPort()
9. {
10. HANDLE hComm;
11. hComm = CreateFile(L"COM3", //串口编号
12. GENERIC_READ | GENERIC_WRITE, //允许读写
13. 0, //通讯设备必须以独占方式打开
14. NULL,
15. OPEN_EXISTING, //通讯设备已存在
16. FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, //重叠方式
17. NULL); //通讯设备不能用模板打开
18. if (hComm == INVALID_HANDLE_VALUE)
19. {
20. CloseHandle(hComm);
21. return FALSE;
22. }
23. else
24. return TRUE;
25.
26. }
27. int main()
28. {
29. bool open;
30. open = OpenPort();
31. if (open)
32. cout << "Open serial port successfully!" << endl;
33. else
34. cout << "Open serial port unsuccessfully!" << endl;
35. return EXIT_SUCCESS;
36. }
我的主机只有一个串口COM1,使用虚拟串口软件额外创建了两个窗口COM2和COM3,修改COM口均能测试成功,如下所示:
在Windows串口通信中还会用到设备控制、超时控制、通信错误、通信状态、通信事件等操作,以下演示了对设备控制DCB和超时控制COMMTIMEOUTS的设置
[cpp] view plain copy
1. #include <iostream>
2. #include <cstdlib>
3. #include <Windows.h>
4.
5. bool OpenPort(); //打开串口
6. bool SetupDCB(int rate_arg); //设置DCB设备控制块
7. bool SetupTimeout(DWORD ReadInterval, DWORD ReadTotalMultiplier,
8. DWORD ReadTotalConstant, DWORD WriteTotalMultiplier, DWORD WriteTotalConstant); //超时设置
9. HANDLE hComm;
10. bool OpenPort()
11. {
12. //HANDLE hComm;
13. hComm = CreateFile(L"COM1", //指定串口
14. GENERIC_READ | GENERIC_WRITE, //允许读写
15. 0, //以独占方式打开
16. 0, //无安全属性
17. OPEN_EXISTING, //通讯设备已存在
18. 0, //同步I/O
19. 0); //不指定模式
20. if (hComm == INVALID_HANDLE_VALUE)
21. {
22. CloseHandle(hComm);
23. return FALSE;
24. }
25. else
26. return TRUE;
27. }
28.
29. bool SetupDCB(int rate_arg)
30. {
31. DCB dcb;
32. memset(&dcb, 0,sizeof(dcb));
33. if (!GetCommState(hComm, &dcb)) //获取当前DCB配置
34. return FALSE;
35. /* ---------- 串口设置 ------- */
36. dcb.DCBlength = sizeof(dcb); //DCB块大小
37. dcb.BaudRate = rate_arg; //波特率
38. dcb.Parity = NOPARITY; //奇偶校验0-4:分别表示不校验、奇校验,偶校验、标号、空格
39. dcb.fParity = 0; //不允许奇偶校验
40. dcb.StopBits = ONESTOPBIT; //停止位
41. dcb.ByteSize = 8; //数据位,以字节表示4-8
42. dcb.fOutxCtsFlow = 0; //CTS输出流控制
43. dcb.fOutxDsrFlow = 0; //DSR输出流控制
44. dcb.fDtrControl = DTR_CONTROL_DISABLE; //DTR流控制类型
45. dcb.fDsrSensitivity = 0; //对DSR信号线不敏感
46. dcb.fRtsControl = RTS_CONTROL_DISABLE; //RTS流控制
47. dcb.fOutX = 0; //XON/XOFF输出流控制
48. dcb.fInX = 0; //XON/XOFF输入流控制
49. /* ---------- 容错机制 ------- */
50. dcb.fErrorChar = 0; //允许错误替换
51. dcb.fBinary = 1; //二进制模式,不检测EOF
52. dcb.fNull = 0; //允许剥离,去掉NULL字符
53. dcb.fAbortOnError = 0; //有错误时终止读写操作
54. dcb.wReserved = 0; //
55. dcb.XonLim = 2; //XON发送字符之前缓冲区中允许接收的最小字节数
56. dcb.XoffLim = 4; //XON发送字符之前缓冲区中允许的最小可用字节数
57. dcb.XonChar = 0x13; //发送和接受XON字符
58. dcb.XoffChar = 0x19; //发送和接受XOFF字符
59. dcb.EvtChar = 0; //接收到的事件字符
60. if (!SetCommState(hComm, &dcb))
61. return FALSE;
62. else
63. return TRUE;
64. }
65.
66. bool SetupTimeout(DWORD ReadInterval, DWORD ReadTotalMultiplier,
67. DWORD ReadTotalConstant, DWORD WriteTotalMultiplier, DWORD WriteTotalConstant)
68. {
69. COMMTIMEOUTS time;
70. time.ReadIntervalTimeout = ReadInterval; //读时间超时
71. time.ReadTotalTimeoutConstant = ReadTotalConstant; //读时间常量
72. time.ReadTotalTimeoutMultiplier = ReadTotalMultiplier; //读时间系数
73. time.WriteTotalTimeoutConstant = WriteTotalConstant; //写时间常量
74. time.WriteTotalTimeoutMultiplier = WriteTotalMultiplier; //写时间系数
75. if (!SetCommTimeouts(hComm, &time))
76. return FALSE;
77. else
78. return TRUE;
79. }
80. int main()
81. {
82. if (OpenPort())
83. std::cout << "Open port success" << std::endl;
84. if (SetupDCB(9600))
85. std::cout << "Set DCB success" << std::endl;
86. if (SetupTimeout(0, 0, 0, 0, 0))
87. std::cout << "Set timeout success" << std::endl;
88. SetCommMask(hComm, EV_RXCHAR); //当有字符在inbuf中时产生这个事件
89. PurgeComm(hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
90.
91. return EXIT_SUCCESS;
92. }
在COM1口的测试结果如下,
串口的读写操作:
本例实现串口的读写,采用虚拟串口虚拟一对串口COM2和COM3,COM2用于接收,COM3用于发送。完整代码如下:
[cpp] view plain copy
1. #include <iostream>
2. #include <cstdlib>
3. #include <windows.h>
4.
5. HANDLE hComm;
6. OVERLAPPED OverLapped;
7. COMSTAT Comstat;
8. DWORD dwCommEvents;
9.
10. bool OpenPort(); //打开串口
11. bool SetupDCB(int rate_arg); //设置DCB
12. bool SetupTimeout(DWORD ReadInterval, DWORD ReadTotalMultiplier, DWORD
13. ReadTotalConstant, DWORD WriteTotalMultiplier, DWORD WriteTotalConstant); //设置超时
14. void ReciveChar(); //接收字符
15. bool WriteChar(char* szWriteBuffer, DWORD dwSend); //发送字符
16.
17. bool OpenPort()
18. {
19. hComm = CreateFile(L"COM2",
20. GENERIC_READ | GENERIC_WRITE,
21. 0,
22. 0,
23. OPEN_EXISTING,
24. FILE_FLAG_OVERLAPPED,
25. 0);
26. if (hComm == INVALID_HANDLE_VALUE)
27. return FALSE;
28. else
29. return true;
30. }
31.
32. bool SetupDCB(int rate_arg)
33. {
34. DCB dcb;
35. memset(&dcb, 0, sizeof(dcb));
36. if (!GetCommState(hComm, &dcb))//获取当前DCB配置
37. {
38. return FALSE;
39. }
40. dcb.DCBlength = sizeof(dcb);
41. /* ---------- Serial Port Config ------- */
42. dcb.BaudRate = rate_arg;
43. dcb.Parity = NOPARITY;
44. dcb.fParity = 0;
45. dcb.StopBits = ONESTOPBIT;
46. dcb.ByteSize = 8;
47. dcb.fOutxCtsFlow = 0;
48. dcb.fOutxDsrFlow = 0;
49. dcb.fDtrControl = DTR_CONTROL_DISABLE;
50. dcb.fDsrSensitivity = 0;
51. dcb.fRtsControl = RTS_CONTROL_DISABLE;
52. dcb.fOutX = 0;
53. dcb.fInX = 0;
54. dcb.fErrorChar = 0;
55. dcb.fBinary = 1;
56. dcb.fNull = 0;
57. dcb.fAbortOnError = 0;
58. dcb.wReserved = 0;
59. dcb.XonLim = 2;
60. dcb.XoffLim = 4;
61. dcb.XonChar = 0x13;
62. dcb.XoffChar = 0x19;
63. dcb.EvtChar = 0;
64. if (!SetCommState(hComm, &dcb))
65. {
66. return false;
67. }
68. else
69. return true;
70. }
71.
72. bool SetupTimeout(DWORD ReadInterval, DWORD ReadTotalMultiplier, DWORD
73. ReadTotalConstant, DWORD WriteTotalMultiplier, DWORD WriteTotalConstant)
74. {
75. COMMTIMEOUTS timeouts;
76. timeouts.ReadIntervalTimeout = ReadInterval;
77. timeouts.ReadTotalTimeoutConstant = ReadTotalConstant;
78. timeouts.ReadTotalTimeoutMultiplier = ReadTotalMultiplier;
79. timeouts.WriteTotalTimeoutConstant = WriteTotalConstant;
80. timeouts.WriteTotalTimeoutMultiplier = WriteTotalMultiplier;
81. if (!SetCommTimeouts(hComm, &timeouts))
82. {
83. return false;
84. }
85. else
86. return true;
87. }
88.
89. void ReciveChar()
90. {
91. bool bRead = TRUE;
92. bool bResult = TRUE;
93. DWORD dwError = 0;
94. DWORD BytesRead = 0;
95. char RXBuff;
96. for (;;)
97. {
98. bResult = ClearCommError(hComm, &dwError, &Comstat);
99. if (Comstat.cbInQue == 0)
100. continue;
101. if (bRead)
102. {
103. bResult = ReadFile(hComm, //通信设备(此处为串口)句柄,由CreateFile()返回值得到
104. &RXBuff, //指向接收缓冲区
105. 1, //指明要从串口中读取的字节数
106. &BytesRead, //
107. &OverLapped); //OVERLAPPED结构
108. std::cout << RXBuff << std::endl;
109. if (!bResult)
110. {
111. switch (dwError == GetLastError())
112. {
113. case ERROR_IO_PENDING:
114. bRead = FALSE;
115. break;
116. default:
117. break;
118. }
119. }
120. }
121. else
122. {
123. bRead = TRUE;
124. }
125. }
126. if (!bRead)
127. {
128. bRead = TRUE;
129. bResult = GetOverlappedResult(hComm,
130. &OverLapped,
131. &BytesRead,
132. TRUE);
133. }
134. }
135.
136. bool WriteChar(char* szWriteBuffer, DWORD dwSend)
137. {
138. bool bWrite = TRUE;
139. bool bResult = TRUE;
140. DWORD BytesSent = 0;
141. HANDLE hWriteEvent=NULL;
142. ResetEvent(hWriteEvent);
143. if (bWrite)
144. {
145. OverLapped.Offset = 0;
146. OverLapped.OffsetHigh = 0;
147. bResult = WriteFile(hComm, //通信设备句柄,CreateFile()返回值得到
148. szWriteBuffer, //指向写入数据缓冲区
149. dwSend, //设置要写的字节数
150. &BytesSent, //
151. &OverLapped); //指向异步I/O数据
152. if (!bResult)
153. {
154. DWORD dwError = GetLastError();
155. switch (dwError)
156. {
157. case ERROR_IO_PENDING:
158. BytesSent = 0;
159. bWrite = FALSE;
160. break;
161. default:
162. break;
163. }
164. }
165. }
166. if (!bWrite)
167. {
168. bWrite = TRUE;
169. bResult = GetOverlappedResult(hComm,
170. &OverLapped,
171. &BytesSent,
172. TRUE);
173. if (!bResult)
174. {
175. std::cout << "GetOverlappedResults() in WriteFile()" << std::endl;
176. }
177. }
178. if (BytesSent != dwSend)
179. {
180. std::cout << "WARNING: WriteFile() error.. Bytes Sent:" << BytesSent << "; Message Length: " << strlen((char*)szWriteBuffer) << std::endl;
181. }
182. return TRUE;
183. }
184. int main()
185. {
186. if (OpenPort())
187. std::cout << "Open port success" << std::endl;
188. if (SetupDCB(9600))
189. std::cout << "Set DCB success" << std::endl;
190. if (SetupTimeout(0, 0, 0, 0, 0))
191. std::cout << "Set timeout success" << std::endl;
192. PurgeComm(hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
193. WriteChar("Please send data:", 20);
194. std::cout << "Received data:";
195. ReciveChar();
196. return EXIT_SUCCESS;
197. }
首先编译运行如下结果:
此时打开串口调试助手,设置串口号为COM3,波特率9600,数据位8,停止位1,以ASCII码形式发送,然后在发送栏写入要发送的字符如“Hello world!”,点击发送,COM2口就可以成功接收发送来的数据,并且COM3口成功接收到了COM2发来的数据请求。图如所示,
全新视频:www.makeru.com.cn/?t=12 嵌入式学习交流群:561213221