win 串口编程

1220阅读 0评论2015-08-29 zhanggf8220
分类:C/C++

在windows程序设计与开发过程中,特别是涉及到开发嵌入式软硬件系统时,往往会涉及到串口编程。

点击(此处)折叠或打开

  1. //////////////////////////////////////////////////////////////////////////
  2. /// COPYRIGHT NOTICE
  3. ///
  4. /// @file SerialPort.h
  5. /// @brief 串口通信类头文件
  6. ///
  7. /// 本文件完成串口通信类的声明
  8. ///
  9. /// 修订说明:
  10. //////////////////////////////////////////////////////////////////////////

  11. #ifndef SERIALPORT_H_
  12. #define SERIALPORT_H_

  13. #include <Windows.h>

  14. /** 串口通信类
  15.  *
  16.  * 本类实现了对串口的基本操作
  17.  * 例如监听发到指定串口的数据、发送指定数据到串口
  18.  */
  19. class CSerialPort
  20. {
  21. public:
  22.     CSerialPort(void);
  23.     ~CSerialPort(void);

  24. public:
  25.     
  26.     /** 初始化串口函数
  27.      *
  28.      * @param: UINT portNo 串口编号,默认值为1,即COM1,注意,尽量不要大于9
  29.      * @param: UINT baud 波特率,默认为9600
  30.      * @param: char parity 是否进行奇偶校验,'Y'表示需要奇偶校验,'N'表示不需要奇偶校验
  31.      * @param: UINT databits 数据位的个数,默认值为8个数据位
  32.      * @param: UINT stopsbits 停止位使用格式,默认值为1
  33.      * @param: DWORD dwCommEvents 默认为EV_RXCHAR,即只要收发任意一个字符,则产生一个事件
  34.      * @return: bool 初始化是否成功
  35.      * @note: 在使用其他本类提供的函数前,请先调用本函数进行串口的初始化
  36.      *      /n本函数提供了一些常用的串口参数设置,若需要自行设置详细的DCB参数,可使用重载函数
  37.      * /n本串口类析构时会自动关闭串口,无需额外执行关闭串口
  38.      * @see:
  39.      */
  40.     bool InitPort( UINT portNo = 1,UINT baud = CBR_9600,char parity = 'N',UINT databits = 8,
  41.          UINT stopsbits = 1,DWORD dwCommEvents = EV_RXCHAR);

  42.     /** 串口初始化函数
  43.      *
  44.      * 本函数提供直接根据DCB参数设置串口参数
  45.      * @param: UINT portNo
  46.      * @param: const LPDCB & plDCB
  47.      * @return: bool 初始化是否成功
  48.      * @note: 本函数提供用户自定义地串口初始化参数
  49.      * @see:
  50.      */
  51.     bool InitPort( UINT portNo ,const LPDCB& plDCB );

  52.     /** 开启监听线程
  53.      *
  54.      * 本监听线程完成对串口数据的监听,并将接收到的数据打印到屏幕输出
  55.      * @return: bool 操作是否成功
  56.      * @note: 当线程已经处于开启状态时,返回flase
  57.      * @see:
  58.      */
  59.     bool OpenListenThread();

  60.     /** 关闭监听线程
  61.      *
  62.      *
  63.      * @return: bool 操作是否成功
  64.      * @note: 调用本函数后,监听串口的线程将会被关闭
  65.      * @see:
  66.      */
  67.     bool CloseListenTread();

  68.     /** 向串口写数据
  69.      *
  70.      * 将缓冲区中的数据写入到串口
  71.      * @param: unsigned char * pData 指向需要写入串口的数据缓冲区
  72.      * @param: unsigned int length 需要写入的数据长度
  73.      * @return: bool 操作是否成功
  74.      * @note: length不要大于pData所指向缓冲区的大小
  75.      * @see:
  76.      */
  77.     bool WriteData(unsigned char* pData, unsigned int length);

  78.     /** 获取串口缓冲区中的字节数
  79.      *
  80.      *
  81.      * @return: UINT 操作是否成功
  82.      * @note: 当串口缓冲区中无数据时,返回0
  83.      * @see:
  84.      */
  85.     UINT GetBytesInCOM();

  86.     /** 读取串口接收缓冲区中一个字节的数据
  87.      *
  88.      *
  89.      * @param: char & cRecved 存放读取数据的字符变量
  90.      * @return: bool 读取是否成功
  91.      * @note:
  92.      * @see:
  93.      */
  94.     bool ReadChar(char &cRecved);

  95. private:

  96.     /** 打开串口
  97.      *
  98.      *
  99.      * @param: UINT portNo 串口设备号
  100.      * @return: bool 打开是否成功
  101.      * @note:
  102.      * @see:
  103.      */
  104.     bool openPort( UINT portNo );

  105.     /** 关闭串口
  106.      *
  107.      *
  108.      * @return: void 操作是否成功
  109.      * @note:
  110.      * @see:
  111.      */
  112.     void ClosePort();
  113.     
  114.     /** 串口监听线程
  115.      *
  116.      * 监听来自串口的数据和信息
  117.      * @param: void * pParam 线程参数
  118.      * @return: UINT WINAPI 线程返回值
  119.      * @note:
  120.      * @see:
  121.      */
  122.     static UINT WINAPI ListenThread(void* pParam);

  123. private:

  124.     /** 串口句柄 */
  125.     HANDLE m_hComm;

  126.     /** 线程退出标志变量 */
  127.     static bool s_bExit;

  128.     /** 线程句柄 */
  129.     volatile HANDLE m_hListenThread;

  130.     /** 同步互斥,临界区保护 */
  131.     CRITICAL_SECTION m_csCommunicationSync; //!< 互斥操作串口

  132. };

  133. #endif //SERIALPORT_H_

  134. //////////////////////////////////////////////////////////////////////////
  135. /// COPYRIGHT NOTICE
  136. ///
  137. /// @file SerialPort.cpp
  138. /// @brief 串口通信类的实现文件
  139. ///
  140. /// 本文件为串口通信类的实现代码
  141. ///
  142. ///
  143. /// 修订说明:
  144. //////////////////////////////////////////////////////////////////////////

  145. #include "StdAfx.h"
  146. #include "SerialPort.h"
  147. #include <process.h>
  148. #include <iostream>

  149. /** 线程退出标志 */
  150. bool CSerialPort::s_bExit = false;
  151. /** 当串口无数据时,sleep至下次查询间隔的时间,单位:*/
  152. const UINT SLEEP_TIME_INTERVAL = 5;

  153. CSerialPort::CSerialPort(void)
  154. : m_hListenThread(INVALID_HANDLE_VALUE)
  155. {
  156.     m_hComm = INVALID_HANDLE_VALUE;
  157.     m_hListenThread = INVALID_HANDLE_VALUE;

  158.     InitializeCriticalSection(&m_csCommunicationSync);

  159. }

  160. CSerialPort::~CSerialPort(void)
  161. {
  162.     CloseListenTread();
  163.     ClosePort();
  164.     DeleteCriticalSection(&m_csCommunicationSync);
  165. }

  166. bool CSerialPort::InitPort( UINT portNo /*= 1*/,UINT baud /*= CBR_9600*/,char parity /*= 'N'*/,
  167.                          UINT databits /*= 8*/, UINT stopsbits /*= 1*/,DWORD dwCommEvents /*= EV_RXCHAR*/ )
  168. {

  169.     /** 临时变量,将制定参数转化为字符串形式,以构造DCB结构 */
  170.     char szDCBparam[50];
  171.     sprintf_s(szDCBparam, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, stopsbits);

  172.     /** 打开指定串口,该函数内部已经有临界区保护,上面请不要加保护 */
  173.     if (!openPort(portNo))
  174.     {
  175.         return false;
  176.     }

  177.     /** 进入临界段 */
  178.     EnterCriticalSection(&m_csCommunicationSync);

  179.     /** 是否有错误发生 */
  180.     BOOL bIsSuccess = TRUE;

  181.     /** 在此可以设置输入输出的缓冲区大小,如果不设置,则系统会设置默认值.
  182.      * 自己设置缓冲区大小时,要注意设置稍大一些,避免缓冲区溢出
  183.      */
  184.     /*if (bIsSuccess )
  185.     {
  186.         bIsSuccess = SetupComm(m_hComm,10,10);
  187.     }*/

  188.     /** 设置串口的超时时间,均设为0,表示不使用超时限制 */
  189.     COMMTIMEOUTS CommTimeouts;
  190.     CommTimeouts.ReadIntervalTimeout = 0;
  191.     CommTimeouts.ReadTotalTimeoutMultiplier = 0;
  192.     CommTimeouts.ReadTotalTimeoutConstant = 0;
  193.     CommTimeouts.WriteTotalTimeoutMultiplier = 0;
  194.     CommTimeouts.WriteTotalTimeoutConstant = 0;
  195.     if ( bIsSuccess)
  196.     {
  197.         bIsSuccess = SetCommTimeouts(m_hComm, &CommTimeouts);
  198.     }

  199.     DCB dcb;
  200.     if ( bIsSuccess )
  201.     {
  202.         // 将ANSI字符串转换为UNICODE字符串
  203.         DWORD dwNum = MultiByteToWideChar (CP_ACP, 0, szDCBparam, -1, NULL, 0);
  204.         wchar_t *pwText = new wchar_t[dwNum] ;
  205.         if (!MultiByteToWideChar (CP_ACP, 0, szDCBparam, -1, pwText, dwNum))
  206.         {
  207.             bIsSuccess = TRUE;
  208.         }

  209.         /** 获取当前串口配置参数,并且构造串口DCB参数 */
  210.         bIsSuccess = GetCommState(m_hComm, &dcb) && BuildCommDCB(pwText, &dcb) ;
  211.         /** 开启RTS flow控制 */
  212.         dcb.fRtsControl = RTS_CONTROL_ENABLE;

  213.         /** 释放内存空间 */
  214.         delete [] pwText;
  215.     }

  216.     if ( bIsSuccess )
  217.     {
  218.         /** 使用DCB参数配置串口状态 */
  219.         bIsSuccess = SetCommState(m_hComm, &dcb);
  220.     }
  221.         
  222.     /** 清空串口缓冲区 */
  223.     PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);

  224.     /** 离开临界段 */
  225.     LeaveCriticalSection(&m_csCommunicationSync);

  226.     return bIsSuccess==TRUE;
  227. }

  228. bool CSerialPort::InitPort( UINT portNo ,const LPDCB& plDCB )
  229. {
  230.     /** 打开指定串口,该函数内部已经有临界区保护,上面请不要加保护 */
  231.     if (!openPort(portNo))
  232.     {
  233.         return false;
  234.     }
  235.     
  236.     /** 进入临界段 */
  237.     EnterCriticalSection(&m_csCommunicationSync);

  238.     /** 配置串口参数 */
  239.     if (!SetCommState(m_hComm, plDCB))
  240.     {
  241.         return false;
  242.     }

  243.     /** 清空串口缓冲区 */
  244.     PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);

  245.     /** 离开临界段 */
  246.     LeaveCriticalSection(&m_csCommunicationSync);

  247.     return true;
  248. }

  249. void CSerialPort::ClosePort()
  250. {
  251.     /** 如果有串口被打开,关闭它 */
  252.     if( m_hComm != INVALID_HANDLE_VALUE )
  253.     {
  254.         CloseHandle( m_hComm );
  255.         m_hComm = INVALID_HANDLE_VALUE;
  256.     }
  257. }

  258. bool CSerialPort::openPort( UINT portNo )
  259. {
  260.     /** 进入临界段 */
  261.     EnterCriticalSection(&m_csCommunicationSync);

  262.     /** 把串口的编号转换为设备名 */
  263.     char szPort[50];
  264.     sprintf_s(szPort, "COM%d", portNo);

  265.     /** 打开指定的串口 */
  266.     m_hComm = CreateFileA(szPort,         /** 设备名,COM1,COM2等 */
  267.                          GENERIC_READ | GENERIC_WRITE, /** 访问模式,可同时读写 */
  268.                          0, /** 共享模式,0表示不共享 */
  269.                      NULL,                            /** 安全性设置,一般使用NULL */
  270.                      OPEN_EXISTING,                    /** 该参数表示设备必须存在,否则创建失败 */
  271.                          0,
  272.                          0);

  273.     /** 如果打开失败,释放资源并返回 */
  274.     if (m_hComm == INVALID_HANDLE_VALUE)
  275.     {
  276.         LeaveCriticalSection(&m_csCommunicationSync);
  277.         return false;
  278.     }

  279.     /** 退出临界区 */
  280.     LeaveCriticalSection(&m_csCommunicationSync);

  281.     return true;
  282. }

  283. bool CSerialPort::OpenListenThread()
  284. {
  285.     /** 检测线程是否已经开启了 */
  286.     if (m_hListenThread != INVALID_HANDLE_VALUE)
  287.     {
  288.         /** 线程已经开启 */
  289.         return false;
  290.     }

  291.     s_bExit = false;
  292.     /** 线程ID */
  293.     UINT threadId;
  294.     /** 开启串口数据监听线程 */
  295.     m_hListenThread = (HANDLE)_beginthreadex(NULL, 0, ListenThread, this, 0, &threadId);
  296.     if (!m_hListenThread)
  297.     {
  298.         return false;
  299.     }
  300.     /** 设置线程的优先级,高于普通线程 */
  301.     if (!SetThreadPriority(m_hListenThread, THREAD_PRIORITY_ABOVE_NORMAL))
  302.     {
  303.         return false;
  304.     }

  305.     return true;
  306. }

  307. bool CSerialPort::CloseListenTread()
  308. {    
  309.     if (m_hListenThread != INVALID_HANDLE_VALUE)
  310.     {
  311.         /** 通知线程退出 */
  312.         s_bExit = true;

  313.         /** 等待线程退出 */
  314.         Sleep(10);

  315.         /** 置线程句柄无效 */
  316.         CloseHandle( m_hListenThread );
  317.         m_hListenThread = INVALID_HANDLE_VALUE;
  318.     }
  319.     return true;
  320. }

  321. UINT CSerialPort::GetBytesInCOM()
  322. {
  323.     DWORD dwError = 0;    /** 错误码 */
  324.     COMSTAT comstat; /** COMSTAT结构体,记录通信设备的状态信息 */
  325.     memset(&comstat, 0, sizeof(COMSTAT));

  326.     UINT BytesInQue = 0;
  327.     /** 在调用ReadFile和WriteFile之前,通过本函数清除以前遗留的错误标志 */
  328.     if ( ClearCommError(m_hComm, &dwError, &comstat) )
  329.     {
  330.         BytesInQue = comstat.cbInQue; /** 获取在输入缓冲区中的字节数 */
  331.     }

  332.     return BytesInQue;
  333. }

  334. UINT WINAPI CSerialPort::ListenThread( void* pParam )
  335. {
  336.     /** 得到本类的指针 */
  337.     CSerialPort *pSerialPort = reinterpret_cast<CSerialPort*>(pParam);

  338.     // 线程循环,轮询方式读取串口数据
  339.     while (!pSerialPort->s_bExit)
  340.     {
  341.         UINT BytesInQue = pSerialPort->GetBytesInCOM();
  342.         /** 如果串口输入缓冲区中无数据,则休息一会再查询 */
  343.         if ( BytesInQue == 0 )
  344.         {
  345.             Sleep(SLEEP_TIME_INTERVAL);
  346.             continue;
  347.         }

  348.         /** 读取输入缓冲区中的数据并输出显示 */
  349.         char cRecved = 0x00;
  350.         do
  351.         {
  352.             cRecved = 0x00;
  353.             if(pSerialPort->ReadChar(cRecved) == true)
  354.             {
  355.                 std::cout << cRecved ;
  356.                 continue;
  357.             }
  358.         }while(--BytesInQue);
  359.     }

  360.     return 0;
  361. }

  362. bool CSerialPort::ReadChar( char &cRecved )
  363. {
  364.     BOOL bResult = TRUE;
  365.     DWORD BytesRead = 0;
  366.     if(m_hComm == INVALID_HANDLE_VALUE)
  367.     {
  368.         return false;
  369.     }

  370.     /** 临界区保护 */
  371.     EnterCriticalSection(&m_csCommunicationSync);

  372.     /** 从缓冲区读取一个字节的数据 */
  373.     bResult = ReadFile(m_hComm, &cRecved, 1, &BytesRead, NULL);
  374.     if ((!bResult))
  375.     {
  376.         /** 获取错误码,可以根据该错误码查出错误原因 */
  377.         DWORD dwError = GetLastError();

  378.         /** 清空串口缓冲区 */
  379.         PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_RXABORT);
  380.         LeaveCriticalSection(&m_csCommunicationSync);

  381.         return false;
  382.     }

  383.     /** 离开临界区 */
  384.     LeaveCriticalSection(&m_csCommunicationSync);

  385.     return (BytesRead == 1);

  386. }

  387. bool CSerialPort::WriteData( unsigned char* pData, unsigned int length )
  388. {
  389.     BOOL bResult = TRUE;
  390.     DWORD BytesToSend = 0;
  391.     if(m_hComm == INVALID_HANDLE_VALUE)
  392.     {
  393.         return false;
  394.     }

  395.     /** 临界区保护 */
  396.     EnterCriticalSection(&m_csCommunicationSync);

  397.     /** 向缓冲区写入指定量的数据 */
  398.     bResult = WriteFile(m_hComm, pData, length, &BytesToSend, NULL);
  399.     if (!bResult)
  400.     {
  401.         DWORD dwError = GetLastError();
  402.         /** 清空串口缓冲区 */
  403.         PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_RXABORT);
  404.         LeaveCriticalSection(&m_csCommunicationSync);

  405.         return false;
  406.     }

  407.     /** 离开临界区 */
  408.     LeaveCriticalSection(&m_csCommunicationSync);

  409.     return true;
  410. }


  411. // mySerialport.cpp : Defines the entry point for the console application.
  412. //

  413. #include "stdafx.h"
  414. #include "SerialPort.h"
  415. #include <iostream>

  416. int _tmain(int argc, _TCHAR* argv[])
  417. {

  418.     CSerialPort mySerialPort;

  419.     if (!mySerialPort.InitPort(2))
  420.     {
  421.         std::cout << "initPort fail !" << std::endl;
  422.     }
  423.     else
  424.     {
  425.         std::cout << "initPort success !" << std::endl;
  426.     }

  427.     if (!mySerialPort.OpenListenThread())
  428.     {
  429.         std::cout << "OpenListenThread fail !" << std::endl;
  430.     }
  431.     else
  432.     {
  433.         std::cout << "OpenListenThread success !" << std::endl;
  434.     }

  435.     int temp;
  436.     std::cin >> temp;

  437.     return 0;
  438. }

上一篇:getsockopt的TCP层实现剖析
下一篇:linux c 定时器