C++串口编程

原文链接:http://blog.sina.com.cn/s/blog_afb1793101016mq2.html

 

1. 本例子使用了比较规范的软件设计方法,类的设计具有比较好的可扩展性和移植性、代码的注释采用doxgen支持的javaDoc风格。
2. 为了能方便初学者更快地了解和入门,几乎每一行代码都加上了详细的注释,对于注释中如果依然有不清楚的概念,相信你通过百度和google一定能找到答案。
3. 本例子设计的串口操作类可以直接移植到其他的工程中去,大家也可以根据自己的需要添加其他的接口。
4. 本例子只实现了串口数据的基本收发功能,其实为了保证串口数据传输的正确性,往往需要设计一些串口通信协议,协议的设计有待你自己完成,如果以后有时间,我也会尝试提供一种比较基本的串口通信协议设计案例给大家学习。
5. 关于本程序的验证方法,可以使用虚拟串口软件VSPM和串口调试助手进行程序的测试与验证. 

下面即为例子工程的三个文件,SerialPort.h、SerialPort.cpp、maincpp

 

工程文件,需要使用vs2008打开。下载

http://ticktick.blog.51cto.com/ext/down_att.php?aid=23099&code=6280

 

SerialPort.h

 

 
  1. //

  2. /// COPYRIGHT NOTICE

  3. /// Copyright (c) 2009, 华中科技大学tickTick Group (版权声明)

  4. /// All rights reserved.

  5. ///

  6. /// @file SerialPort.h

  7. /// @brief 串口通信类头文件

  8. ///

  9. /// 本文件完成串口通信类的声明

  10. ///

  11. /// @version 1.0

  12. /// @author 卢俊

  13. /// @E-mail:lujun.hust@gmail.com

  14. /// @date 2010/03/19

  15. ///

  16. /// 修订说明:

  17. //

  18.  
  19. #ifndef SERIALPORT_H_

  20. #define SERIALPORT_H_

  21.  
  22. #include <Windows.h>

  23.  
  24. /** 串口通信类

  25. *

  26. * 本类实现了对串口的基本操作

  27. * 例如监听发到指定串口的数据、发送指定数据到串口

  28. */

  29. class CSerialPort

  30. {

  31. public:

  32. CSerialPort(void);

  33. ~CSerialPort(void);

  34.  
  35. public:

  36.  
  37. /** 初始化串口函数

  38. *

  39. * @param: UINT portNo 串口编号,默认值为1,即COM1,注意,尽量不要大于9

  40. * @param: UINT baud 波特率,默认为9600

  41. * @param: char parity 是否进行奇偶校验,'Y'表示需要奇偶校验,'N'表示不需要奇偶校验

  42. * @param: UINT databits 数据位的个数,默认值为8个数据位

  43. * @param: UINT stopsbits 停止位使用格式,默认值为1

  44. * @param: DWORD dwCommEvents 默认为EV_RXCHAR,即只要收发任意一个字符,则产生一个事件

  45. * @return: bool 初始化是否成功

  46. * @note: 在使用其他本类提供的函数前,请先调用本函数进行串口的初始化

  47. *      /n本函数提供了一些常用的串口参数设置,若需要自行设置详细的DCB参数,可使用重载函数

  48. * /n本串口类析构时会自动关闭串口,无需额外执行关闭串口

  49. * @see:

  50. */

  51. bool InitPort(UINT portNo = 1, UINT baud = CBR_9600, char parity = 'N', UINT databits = 8, UINT stopsbits = 1, DWORD dwCommEvents = EV_RXCHAR);

  52.  
  53. /** 串口初始化函数

  54. *

  55. * 本函数提供直接根据DCB参数设置串口参数

  56. * @param: UINT portNo

  57. * @param: const LPDCB & plDCB

  58. * @return: bool 初始化是否成功

  59. * @note: 本函数提供用户自定义地串口初始化参数

  60. * @see:

  61. */

  62. bool InitPort(UINT portNo, const LPDCB& plDCB);

  63.  
  64. /** 开启监听线程

  65. *

  66. * 本监听线程完成对串口数据的监听,并将接收到的数据打印到屏幕输出

  67. * @return: bool 操作是否成功

  68. * @note: 当线程已经处于开启状态时,返回flase

  69. * @see:

  70. */

  71. bool OpenListenThread();

  72.  
  73. /** 关闭监听线程

  74. *

  75. *

  76. * @return: bool 操作是否成功

  77. * @note: 调用本函数后,监听串口的线程将会被关闭

  78. * @see:

  79. */

  80. bool CloseListenTread();

  81.  
  82. /** 向串口写数据

  83. *

  84. * 将缓冲区中的数据写入到串口

  85. * @param: unsigned char * pData 指向需要写入串口的数据缓冲区

  86. * @param: unsigned int length 需要写入的数据长度

  87. * @return: bool 操作是否成功

  88. * @note: length不要大于pData所指向缓冲区的大小

  89. * @see:

  90. */

  91. bool WriteData(unsigned char* pData, unsigned int length);

  92.  
  93. /** 获取串口缓冲区中的字节数

  94. *

  95. *

  96. * @return: UINT 操作是否成功

  97. * @note: 当串口缓冲区中无数据时,返回0

  98. * @see:

  99. */

  100. UINT GetBytesInCOM();

  101.  
  102. /** 读取串口接收缓冲区中一个字节的数据

  103. *

  104. *

  105. * @param: char & cRecved 存放读取数据的字符变量

  106. * @return: bool 读取是否成功

  107. * @note:

  108. * @see:

  109. */

  110. bool ReadChar(char &cRecved);

  111.  
  112. private:

  113.  
  114. /** 打开串口

  115. *

  116. *

  117. * @param: UINT portNo 串口设备号

  118. * @return: bool 打开是否成功

  119. * @note:

  120. * @see:

  121. */

  122. bool openPort(UINT portNo);

  123.  
  124. /** 关闭串口

  125. *

  126. *

  127. * @return: void 操作是否成功

  128. * @note:

  129. * @see:

  130. */

  131. void ClosePort();

  132.  
  133. /** 串口监听线程

  134. *

  135. * 监听来自串口的数据和信息

  136. * @param: void * pParam 线程参数

  137. * @return: UINT WINAPI 线程返回值

  138. * @note:

  139. * @see:

  140. */

  141. static UINT WINAPI ListenThread(void* pParam);

  142.  
  143. private:

  144.  
  145. /** 串口句柄 */

  146. HANDLE m_hComm;

  147.  
  148. /** 线程退出标志变量 */

  149. static bool s_bExit;

  150.  
  151. /** 线程句柄 */

  152. volatile HANDLE m_hListenThread;

  153.  
  154. /** 同步互斥,临界区保护 */

  155. CRITICAL_SECTION m_csCommunicationSync; //!< 互斥操作串口

  156.  
  157. };

  158.  
  159. #endif //SERIALPORT_H_


 

SerialPort.cpp

 

 
  1. //

  2. /// COPYRIGHT NOTICE

  3. /// Copyright (c) 2009, 华中科技大学tickTick Group (版权声明)

  4. /// All rights reserved.

  5. ///

  6. /// @file SerialPort.cpp

  7. /// @brief 串口通信类的实现文件

  8. ///

  9. /// 本文件为串口通信类的实现代码

  10. ///

  11. /// @version 1.0

  12. /// @author 卢俊

  13. /// @E-mail:lujun.hust@gmail.com

  14. /// @date 2010/03/19

  15. ///

  16. ///

  17. /// 修订说明:

  18. //

  19.  
  20. #include "StdAfx.h"

  21. #include "SerialPort.h"

  22. #include <process.h>

  23. #include <iostream>

  24.  
  25. /** 线程退出标志 */

  26. bool CSerialPort::s_bExit = false;

  27. /** 当串口无数据时,sleep至下次查询间隔的时间,单位:秒 */

  28. const UINT SLEEP_TIME_INTERVAL = 5;

  29.  
  30. CSerialPort::CSerialPort(void)

  31. : m_hListenThread(INVALID_HANDLE_VALUE)

  32. {

  33. m_hComm = INVALID_HANDLE_VALUE;

  34. m_hListenThread = INVALID_HANDLE_VALUE;

  35.  
  36. InitializeCriticalSection(&m_csCommunicationSync);

  37.  
  38. }

  39.  
  40. CSerialPort::~CSerialPort(void)

  41. {

  42. CloseListenTread();

  43. ClosePort();

  44. DeleteCriticalSection(&m_csCommunicationSync);

  45. }

  46.  
  47. bool CSerialPort::InitPort(UINT portNo /*= 1*/, UINT baud /*= CBR_9600*/, char parity /*= 'N'*/,

  48. UINT databits /*= 8*/, UINT stopsbits /*= 1*/, DWORD dwCommEvents /*= EV_RXCHAR*/)

  49. {

  50.  
  51. /** 临时变量,将制定参数转化为字符串形式,以构造DCB结构 */

  52. char szDCBparam[50];

  53. sprintf_s(szDCBparam, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, stopsbits);

  54.  
  55. /** 打开指定串口,该函数内部已经有临界区保护,上面请不要加保护 */

  56. if (!openPort(portNo))

  57. {

  58. return false;

  59. }

  60.  
  61. /** 进入临界段 */

  62. EnterCriticalSection(&m_csCommunicationSync);

  63.  
  64. /** 是否有错误发生 */

  65. BOOL bIsSuccess = TRUE;

  66.  
  67. /** 在此可以设置输入输出的缓冲区大小,如果不设置,则系统会设置默认值.

  68. * 自己设置缓冲区大小时,要注意设置稍大一些,避免缓冲区溢出

  69. */

  70. /*if (bIsSuccess )

  71. {

  72. bIsSuccess = SetupComm(m_hComm,10,10);

  73. }*/

  74.  
  75. /** 设置串口的超时时间,均设为0,表示不使用超时限制 */

  76. COMMTIMEOUTS CommTimeouts;

  77. CommTimeouts.ReadIntervalTimeout = 0;

  78. CommTimeouts.ReadTotalTimeoutMultiplier = 0;

  79. CommTimeouts.ReadTotalTimeoutConstant = 0;

  80. CommTimeouts.WriteTotalTimeoutMultiplier = 0;

  81. CommTimeouts.WriteTotalTimeoutConstant = 0;

  82. if (bIsSuccess)

  83. {

  84. bIsSuccess = SetCommTimeouts(m_hComm, &CommTimeouts);

  85. }

  86.  
  87. DCB dcb;

  88. if (bIsSuccess)

  89. {

  90. // 将ANSI字符串转换为UNICODE字符串

  91. DWORD dwNum = MultiByteToWideChar(CP_ACP, 0, szDCBparam, -1, NULL, 0);

  92. wchar_t *pwText = new wchar_t[dwNum];

  93. if (!MultiByteToWideChar(CP_ACP, 0, szDCBparam, -1, pwText, dwNum))

  94. {

  95. bIsSuccess = TRUE;

  96. }

  97.  
  98. /** 获取当前串口配置参数,并且构造串口DCB参数 */

  99. bIsSuccess = GetCommState(m_hComm, &dcb) && BuildCommDCB(pwText, &dcb);

  100. /** 开启RTS flow控制 */

  101. dcb.fRtsControl = RTS_CONTROL_ENABLE;

  102.  
  103. /** 释放内存空间 */

  104. delete[] pwText;

  105. }

  106.  
  107. if (bIsSuccess)

  108. {

  109. /** 使用DCB参数配置串口状态 */

  110. bIsSuccess = SetCommState(m_hComm, &dcb);

  111. }

  112.  
  113. /** 清空串口缓冲区 */

  114. PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);

  115.  
  116. /** 离开临界段 */

  117. LeaveCriticalSection(&m_csCommunicationSync);

  118.  
  119. return bIsSuccess == TRUE;

  120. }

  121.  
  122. bool CSerialPort::InitPort(UINT portNo, const LPDCB& plDCB)

  123. {

  124. /** 打开指定串口,该函数内部已经有临界区保护,上面请不要加保护 */

  125. if (!openPort(portNo))

  126. {

  127. return false;

  128. }

  129.  
  130. /** 进入临界段 */

  131. EnterCriticalSection(&m_csCommunicationSync);

  132.  
  133. /** 配置串口参数 */

  134. if (!SetCommState(m_hComm, plDCB))

  135. {

  136. return false;

  137. }

  138.  
  139. /** 清空串口缓冲区 */

  140. PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);

  141.  
  142. /** 离开临界段 */

  143. LeaveCriticalSection(&m_csCommunicationSync);

  144.  
  145. return true;

  146. }

  147.  
  148. void CSerialPort::ClosePort()

  149. {

  150. /** 如果有串口被打开,关闭它 */

  151. if (m_hComm != INVALID_HANDLE_VALUE)

  152. {

  153. CloseHandle(m_hComm);

  154. m_hComm = INVALID_HANDLE_VALUE;

  155. }

  156. }

  157.  
  158. bool CSerialPort::openPort(UINT portNo)

  159. {

  160. /** 进入临界段 */

  161. EnterCriticalSection(&m_csCommunicationSync);

  162.  
  163. /** 把串口的编号转换为设备名 */

  164. char szPort[50];

  165. sprintf_s(szPort, "COM%d", portNo);

  166.  
  167. /** 打开指定的串口 */

  168. m_hComm = CreateFileA(szPort, /** 设备名,COM1,COM2等 */

  169. GENERIC_READ | GENERIC_WRITE, /** 访问模式,可同时读写 */

  170. 0, /** 共享模式,0表示不共享 */

  171. NULL, /** 安全性设置,一般使用NULL */

  172. OPEN_EXISTING, /** 该参数表示设备必须存在,否则创建失败 */

  173. 0,

  174. 0);

  175.  
  176. /** 如果打开失败,释放资源并返回 */

  177. if (m_hComm == INVALID_HANDLE_VALUE)

  178. {

  179. LeaveCriticalSection(&m_csCommunicationSync);

  180. return false;

  181. }

  182.  
  183. /** 退出临界区 */

  184. LeaveCriticalSection(&m_csCommunicationSync);

  185.  
  186. return true;

  187. }

  188.  
  189. bool CSerialPort::OpenListenThread()

  190. {

  191. /** 检测线程是否已经开启了 */

  192. if (m_hListenThread != INVALID_HANDLE_VALUE)

  193. {

  194. /** 线程已经开启 */

  195. return false;

  196. }

  197.  
  198. s_bExit = false;

  199. /** 线程ID */

  200. UINT threadId;

  201. /** 开启串口数据监听线程 */

  202. m_hListenThread = (HANDLE)_beginthreadex(NULL, 0, ListenThread, this, 0, &threadId);

  203. if (!m_hListenThread)

  204. {

  205. return false;

  206. }

  207. /** 设置线程的优先级,高于普通线程 */

  208. if (!SetThreadPriority(m_hListenThread, THREAD_PRIORITY_ABOVE_NORMAL))

  209. {

  210. return false;

  211. }

  212.  
  213. return true;

  214. }

  215.  
  216. bool CSerialPort::CloseListenTread()

  217. {

  218. if (m_hListenThread != INVALID_HANDLE_VALUE)

  219. {

  220. /** 通知线程退出 */

  221. s_bExit = true;

  222.  
  223. /** 等待线程退出 */

  224. Sleep(10);

  225.  
  226. /** 置线程句柄无效 */

  227. CloseHandle(m_hListenThread);

  228. m_hListenThread = INVALID_HANDLE_VALUE;

  229. }

  230. return true;

  231. }

  232.  
  233. UINT CSerialPort::GetBytesInCOM()

  234. {

  235. DWORD dwError = 0; /** 错误码 */

  236. COMSTAT comstat; /** COMSTAT结构体,记录通信设备的状态信息 */

  237. memset(&comstat, 0, sizeof(COMSTAT));

  238.  
  239. UINT BytesInQue = 0;

  240. /** 在调用ReadFile和WriteFile之前,通过本函数清除以前遗留的错误标志 */

  241. if (ClearCommError(m_hComm, &dwError, &comstat))

  242. {

  243. BytesInQue = comstat.cbInQue; /** 获取在输入缓冲区中的字节数 */

  244. }

  245.  
  246. return BytesInQue;

  247. }

  248.  
  249. UINT WINAPI CSerialPort::ListenThread(void* pParam)

  250. {

  251. /** 得到本类的指针 */

  252. CSerialPort *pSerialPort = reinterpret_cast<CSerialPort*>(pParam);

  253.  
  254. // 线程循环,轮询方式读取串口数据

  255. while (!pSerialPort->s_bExit)

  256. {

  257. UINT BytesInQue = pSerialPort->GetBytesInCOM();

  258. /** 如果串口输入缓冲区中无数据,则休息一会再查询 */

  259. if (BytesInQue == 0)

  260. {

  261. Sleep(SLEEP_TIME_INTERVAL);

  262. continue;

  263. }

  264.  
  265. /** 读取输入缓冲区中的数据并输出显示 */

  266. char cRecved = 0x00;

  267. do

  268. {

  269. cRecved = 0x00;

  270. if (pSerialPort->ReadChar(cRecved) == true)

  271. {

  272. std::cout << cRecved;

  273. continue;

  274. }

  275. } while (--BytesInQue);

  276. }

  277.  
  278. return 0;

  279. }

  280.  
  281. bool CSerialPort::ReadChar(char &cRecved)

  282. {

  283. BOOL bResult = TRUE;

  284. DWORD BytesRead = 0;

  285. if (m_hComm == INVALID_HANDLE_VALUE)

  286. {

  287. return false;

  288. }

  289.  
  290. /** 临界区保护 */

  291. EnterCriticalSection(&m_csCommunicationSync);

  292.  
  293. /** 从缓冲区读取一个字节的数据 */

  294. bResult = ReadFile(m_hComm, &cRecved, 1, &BytesRead, NULL);

  295. if ((!bResult))

  296. {

  297. /** 获取错误码,可以根据该错误码查出错误原因 */

  298. DWORD dwError = GetLastError();

  299.  
  300. /** 清空串口缓冲区 */

  301. PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_RXABORT);

  302. LeaveCriticalSection(&m_csCommunicationSync);

  303.  
  304. return false;

  305. }

  306.  
  307. /** 离开临界区 */

  308. LeaveCriticalSection(&m_csCommunicationSync);

  309.  
  310. return (BytesRead == 1);

  311.  
  312. }

  313.  
  314. bool CSerialPort::WriteData(unsigned char* pData, unsigned int length)

  315. {

  316. BOOL bResult = TRUE;

  317. DWORD BytesToSend = 0;

  318. if (m_hComm == INVALID_HANDLE_VALUE)

  319. {

  320. return false;

  321. }

  322.  
  323. /** 临界区保护 */

  324. EnterCriticalSection(&m_csCommunicationSync);

  325.  
  326. /** 向缓冲区写入指定量的数据 */

  327. bResult = WriteFile(m_hComm, pData, length, &BytesToSend, NULL);

  328. if (!bResult)

  329. {

  330. DWORD dwError = GetLastError();

  331. /** 清空串口缓冲区 */

  332. PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_RXABORT);

  333. LeaveCriticalSection(&m_csCommunicationSync);

  334.  
  335. return false;

  336. }

  337.  
  338. /** 离开临界区 */

  339. LeaveCriticalSection(&m_csCommunicationSync);

  340.  
  341. return true;

  342. }


 

main.cpp

 

 
  1. int _tmain(int argc, _TCHAR* argv[])

  2. {

  3.  
  4. CSerialPort mySerialPort;

  5.  
  6. if (!mySerialPort.InitPort(2))

  7. {

  8. std::cout << "initPort fail !" << std::endl;

  9. }

  10. else

  11. {

  12. std::cout << "initPort success !" << std::endl;

  13. }

  14.  
  15. if (!mySerialPort.OpenListenThread())

  16. {

  17. std::cout << "OpenListenThread fail !" << std::endl;

  18. }

  19. else

  20. {

  21. std::cout << "OpenListenThread success !" << std::endl;

  22. }

  23.  
  24. int temp;

  25. std::cin >> temp;

  26.  
  27. return 0;

  28. }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值