MySeries.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. /*-----------------------------------------
  2. * Copyright (c) 2008 Eric Wong
  3. * 本版紧供读者参考,不得用于任何商业行为
  4. *
  5. * 文件名称: CESeries.cpp
  6. * 文件标识:
  7. * 摘要:用于封装WINCE 串口通讯
  8. *
  9. * 当前版本: 1.0
  10. * 作者: 汪兵 Eric Wong
  11. * 完成日期: 2008年1月17日
  12. *
  13. * 取代版本:
  14. * 原作者:
  15. * 完成日期:
  16. ----------------------------------------*/
  17. #include "StdAfx.h"
  18. #include "MySeries.h"
  19. //构造函数
  20. CMyCESeries::CMyCESeries(UINT portNo, UINT baud, UINT parity, UINT databits, UINT stopbits)
  21. :m_portNo(portNo)
  22. ,m_baud(baud)
  23. ,m_parity(parity)
  24. ,m_databits(databits)
  25. ,m_stopbits(stopbits)
  26. ,m_hReadThread(NULL)
  27. ,m_hReadCloseEvent(NULL)
  28. {
  29. //m_portNo = portNo; //串口号使用串口1 CAN模块和GPS共用
  30. //m_baud = baud; //波特率
  31. //m_parity = parity; //奇偶校验,0-None,1-Odd,2-Even
  32. //m_databits = databits; //数据位
  33. //m_stopbits = stopbits; //停止位 0-停止位1,1-停止位1.5,2-停止位2
  34. //初始化内部变量
  35. m_hComm = INVALID_HANDLE_VALUE;
  36. m_OnSeriesRead = NULL;
  37. m_hReadCloseEvent = NULL;
  38. m_bOpened = false;
  39. bCanComRead = true;
  40. m_bSyncOrAsync = true;//使用同步方式或者异步方式(重叠方式),默认值为true:异步方式
  41. memset(&m_olWrite,0,sizeof(OVERLAPPED));
  42. }
  43. //析构函数
  44. CMyCESeries::~CMyCESeries()
  45. {
  46. if (m_bOpened)
  47. ClosePort();
  48. }
  49. //串口读线程函数
  50. DWORD CMyCESeries::ReadThreadFunc(LPVOID lparam)
  51. {
  52. CMyCESeries *ceSeries = (CMyCESeries*)lparam;
  53. BYTE * readBuf = NULL;//读取的字节
  54. DWORD actualReadLen=0;//实际读取的字节数
  55. DWORD willReadLen;
  56. DWORD dwReadErrors;
  57. COMSTAT cmState;
  58. //异步方式下将使用这些变量
  59. //LPOVERLAPPED
  60. OVERLAPPED olWait;
  61. //memset(&olWaite,0,sizeof(olWaite));
  62. memset(&olWait,0,sizeof(OVERLAPPED));
  63. olWait.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
  64. //同步方式,将使用该变量
  65. DWORD evtMask;
  66. // 清空缓冲,并检查串口是否打开。
  67. ASSERT(ceSeries->m_hComm !=INVALID_HANDLE_VALUE);
  68. //清空串口
  69. PurgeComm(ceSeries->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR );
  70. SetCommMask (ceSeries->m_hComm, EV_RXCHAR | EV_CTS | EV_DSR );//设置3个串口事件
  71. //可以查看串口设置了哪些事件
  72. //DWORD dwMask1;
  73. //GetCommMask(ceSeries->m_hComm,&dwMask1);
  74. OVERLAPPED olRead;
  75. memset(&olRead,0,sizeof(OVERLAPPED));
  76. olRead.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
  77. while(TRUE)
  78. {
  79. if(ceSeries->m_bSyncOrAsync)//异步方式下
  80. {
  81. DWORD dwCommStatus = 0;
  82. BOOL bWait1=0;
  83. bWait1= WaitCommEvent(ceSeries->m_hComm,&dwCommStatus,&olWait);//如果以上设置的3个事件中任何一个发生了,这里主要是监测接收缓冲区中有数据时
  84. //while(!bWait1);//等待WaitCommEvent返回为TRUE,再往下执行,这种方式行不通,重叠方式(异步方式)时,该函数一直返回为false,
  85. DWORD dwByte; //norains:It is only suitable for the GetOverlappedResult(),not undefined here.
  86. if(GetOverlappedResult(ceSeries->m_hComm,&olWait,&dwByte,TRUE) == FALSE)//正常清空下,会一直在该函数阻塞,直到olWaite中的hEvent事件发生了
  87. //则该函数不阻塞了,并且返回值为True
  88. {
  89. if(GetLastError() != ERROR_IO_PENDING)
  90. {
  91. return 0x30;
  92. }
  93. //Clear the error flag
  94. DWORD dwErrors;
  95. COMSTAT comStat;
  96. memset(&comStat,0,sizeof(comStat));
  97. ClearCommError(ceSeries->m_hComm,&dwErrors,&comStat);
  98. return 0x35;
  99. }
  100. //表示串口收到字符
  101. if (dwCommStatus & EV_RXCHAR)
  102. {
  103. ClearCommError(ceSeries->m_hComm,&dwReadErrors,&cmState);
  104. willReadLen = cmState.cbInQue ;
  105. if (willReadLen <= 0)
  106. {
  107. continue;
  108. }
  109. //异步方式
  110. //分配内存
  111. readBuf = new BYTE[willReadLen+1];
  112. ZeroMemory(readBuf,willReadLen+1);
  113. //读取串口数据
  114. //异步方式
  115. if(ReadFile(ceSeries->m_hComm, readBuf, willReadLen, &actualReadLen,&olRead)==FALSE)//异步操作,在读取完成以后返回TRUE
  116. {
  117. //释放内存
  118. delete[] readBuf;
  119. readBuf = NULL;
  120. if(GetLastError() != ERROR_IO_PENDING)
  121. return 0x40;
  122. if(GetOverlappedResult(ceSeries->m_hComm,&olRead,&actualReadLen,TRUE) == FALSE)
  123. return 0x45;
  124. if(actualReadLen == 0)
  125. return 0x50;
  126. }
  127. else//缓冲区中的数据读取完时
  128. {
  129. //如果读取的数据大于0,
  130. if (actualReadLen>0)
  131. {
  132. //触发读取回调函数
  133. if (ceSeries->m_OnSeriesRead)
  134. {
  135. ceSeries->m_OnSeriesRead(ceSeries->m_pOwner,readBuf,actualReadLen);
  136. }
  137. }
  138. //释放内存
  139. delete[] readBuf;
  140. readBuf = NULL;
  141. }
  142. }
  143. }
  144. else//同步方式下
  145. {
  146. if (WaitCommEvent(ceSeries->m_hComm,&evtMask,0))
  147. {
  148. SetCommMask (ceSeries->m_hComm, EV_RXCHAR | EV_CTS | EV_DSR );
  149. //表示串口收到字符
  150. if (evtMask & EV_RXCHAR)
  151. {
  152. ClearCommError(ceSeries->m_hComm,&dwReadErrors,&cmState);
  153. willReadLen = cmState.cbInQue ;
  154. if (willReadLen <= 0)
  155. {
  156. continue;
  157. }
  158. //分配内存
  159. readBuf = new BYTE[willReadLen];
  160. ZeroMemory(readBuf,willReadLen);
  161. //读取串口数据
  162. ReadFile(ceSeries->m_hComm, readBuf, willReadLen, &actualReadLen,0);
  163. //如果读取的数据大于0,
  164. if (actualReadLen>0)
  165. {
  166. //触发读取回调函数
  167. if (ceSeries->m_OnSeriesRead)
  168. {
  169. ceSeries->m_OnSeriesRead(ceSeries->m_pOwner,readBuf,actualReadLen);
  170. }
  171. }
  172. //释放内存
  173. delete[] readBuf;
  174. readBuf = NULL;
  175. }
  176. }
  177. }
  178. //每次读线程执行到该处时,都会等待(或者叫阻塞)10milseconds,看是否关闭串口事件是否发生
  179. //在ClosePort()函数体中调用了CloseHandle(m_hReadCloseEvent);来使得该事件有信号,我觉得可以用SetEvent(m_hReadCloseEvent)代替,该函数也是使得事件有信号
  180. //当执行了ClosePotr()函数后,说明该事件已经发生了,则当在读线程函数中执行到此处时,
  181. //将会跳出while(true),从而退出读线程,
  182. if (WaitForSingleObject(ceSeries->m_hReadCloseEvent, 10) == WAIT_OBJECT_0)//
  183. {
  184. TRACE("线程ReadThreadFunc退出\n");
  185. break;
  186. }
  187. }
  188. //关闭事件
  189. CloseHandle( olRead.hEvent );
  190. CloseHandle( olWait.hEvent );
  191. //释放内存
  192. if(readBuf)
  193. {
  194. delete[] readBuf;
  195. readBuf = NULL;
  196. }
  197. ExitThread(0);
  198. return 0;
  199. }
  200. //关闭读线程
  201. void CMyCESeries::CloseReadThread()
  202. {
  203. if( m_hComm != INVALID_HANDLE_VALUE )
  204. {
  205. // SetEvent(m_hReadCloseEvent);
  206. // CloseHandle(m_hReadCloseEvent);
  207. //WaitForSingleObject(m_hReadThread, 1000);
  208. // DWORD exitCode;
  209. // BOOL ret = GetExitCodeThread(m_hReadThread, &exitCode);
  210. //if(ret && exitCode != 0)
  211. {
  212. TRACE("强行关闭ReadThreadFunc线程\n");
  213. TerminateThread(m_hReadThread,0);
  214. }
  215. // CloseHandle(m_hReadThread);
  216. m_hReadThread = NULL;
  217. }
  218. /*
  219. if( m_hComm != INVALID_HANDLE_VALUE )
  220. {
  221. SetEvent(m_hReadCloseEvent);
  222. //设置所有事件无效
  223. SetCommMask(m_hComm, 0);
  224. //清空所有将要读的数据
  225. PurgeComm( m_hComm, PURGE_RXCLEAR );
  226. //等待4秒,如果读线程没有退出,则强制退出
  227. if (WaitForSingleObject(m_hReadThread,4000) == WAIT_TIMEOUT)
  228. {
  229. TerminateThread(m_hReadThread,0);
  230. }
  231. m_hReadThread = NULL;
  232. }
  233. //*/
  234. }
  235. //函数介绍:打开串口
  236. //入口参数:pPortOwner :使用此串口类的窗体句柄
  237. // portNo :串口号
  238. // baud :波特率
  239. // parity :奇偶校验
  240. // databits :数据位
  241. // stopbits :停止位
  242. //出口参数:(无)
  243. //返回值:TRUE:成功打开串口;FALSE:打开串口失败
  244. BOOL CMyCESeries::OpenPort(void * pOwner,
  245. UINT portNo , //串口号
  246. UINT baud , //波特率
  247. UINT parity , //奇偶校验
  248. UINT databits , //数据位
  249. UINT stopbits , //停止位
  250. bool isASync
  251. )
  252. {
  253. m_portNo = portNo; //串口号使用串口1 CAN模块和GPS共用
  254. m_baud = baud; //波特率
  255. m_parity = parity; //奇偶校验,0-None,1-Odd,2-Even
  256. m_databits = databits; //数据位
  257. m_stopbits = stopbits; //停止位 0-停止位1,1-停止位1.5,2-停止位2
  258. m_bSyncOrAsync = isASync;
  259. DCB commParam;
  260. TCHAR szPort[15];
  261. ASSERT(pOwner!=NULL);
  262. m_pOwner = pOwner;
  263. // 已经打开的话,直接返回
  264. if (m_hComm != INVALID_HANDLE_VALUE)
  265. {
  266. return TRUE;
  267. }
  268. //设置串口名
  269. //wsprintf(szPort, _T("\.\COM%d:"), portNo);
  270. wsprintf(szPort, _T("\\\\.\\COM%d"), portNo);
  271. //sprintf(szPort, "\\\\.\\COM%d", portNo);
  272. //打开串口
  273. if(m_bSyncOrAsync)//m_bSyncOrAsync值为0,则用同步方式创建,否则以异步方式创建、
  274. {
  275. m_hComm = CreateFile( //以异步方式创建
  276. szPort,
  277. GENERIC_READ | GENERIC_WRITE, //允许读和写
  278. 0, //独占方式(共享模式)
  279. NULL,
  280. OPEN_EXISTING, //打开而不是创建(创建方式)
  281. FILE_FLAG_OVERLAPPED,
  282. 0
  283. );
  284. }
  285. else
  286. {
  287. m_hComm = CreateFile( //以同步方式创建
  288. szPort,
  289. GENERIC_READ | GENERIC_WRITE, //允许读和写
  290. 0, //独占方式(共享模式)
  291. NULL,
  292. OPEN_EXISTING, //打开而不是创建(创建方式)
  293. 0,
  294. NULL
  295. );
  296. }
  297. if (m_hComm == INVALID_HANDLE_VALUE)
  298. {
  299. // 无效句柄,返回。
  300. TRACE(_T("CreateFile 返回无效句柄\n"));
  301. DWORD ErrNo = GetLastError();
  302. return FALSE;
  303. }
  304. // 得到打开串口的当前属性参数,修改后再重新设置串口。
  305. if (!GetCommState(m_hComm,&commParam))
  306. {
  307. //关闭串口
  308. CloseHandle (m_hComm);
  309. m_hComm = INVALID_HANDLE_VALUE;
  310. return FALSE;
  311. }
  312. if(m_bSyncOrAsync)//如果是异步方式,每创建一个串口对象,并且成功打开一个串口对象时后,才创建一个相应的发送事件
  313. {
  314. //异步写串口
  315. m_olWrite.hEvent = CreateEvent(NULL,TRUE,FALSE,_T("WriteData"));
  316. // DWORD dwError = GetLastError();
  317. // if(dwError !=0)
  318. // {
  319. // TRACE("异步方式创建串口对象时,为该串口对象所创建的写串口事件对象失败");
  320. // return FALSE;
  321. // }
  322. }
  323. //设置串口参数
  324. commParam.BaudRate = baud; // 设置波特率
  325. commParam.fBinary = TRUE; // 设置二进制模式,此处必须设置TRUE
  326. commParam.fParity = TRUE; // 支持奇偶校验
  327. commParam.ByteSize = databits; // 数据位,范围:4-8
  328. commParam.Parity = parity; // 校验模式
  329. commParam.StopBits = stopbits; // 停止位
  330. commParam.fOutxCtsFlow = FALSE; // No CTS output flow control
  331. commParam.fOutxDsrFlow = FALSE; // No DSR output flow control
  332. commParam.fDtrControl = DTR_CONTROL_ENABLE;
  333. // DTR flow control type
  334. commParam.fDsrSensitivity = FALSE; // DSR sensitivity
  335. commParam.fTXContinueOnXoff = TRUE; // XOFF continues Tx
  336. commParam.fOutX = FALSE; // No XON/XOFF out flow control
  337. commParam.fInX = FALSE; // No XON/XOFF in flow control
  338. commParam.fErrorChar = FALSE; // Disable error replacement
  339. commParam.fNull = FALSE; // Disable null stripping
  340. commParam.fRtsControl = RTS_CONTROL_ENABLE;
  341. // RTS flow control
  342. commParam.fAbortOnError = FALSE; // 当串口发生错误,并不终止串口读写
  343. //设置串口参数
  344. if (!SetCommState(m_hComm, &commParam))
  345. {
  346. TRACE(_T("SetCommState error\n"));
  347. DWORD dwError = GetLastError();
  348. //关闭串口
  349. CloseHandle (m_hComm);
  350. m_hComm = INVALID_HANDLE_VALUE;
  351. return FALSE;
  352. }
  353. //设置串口读写时间
  354. COMMTIMEOUTS CommTimeOuts;
  355. GetCommTimeouts (m_hComm, &CommTimeOuts);
  356. CommTimeOuts.ReadIntervalTimeout = MAXDWORD;
  357. CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
  358. CommTimeOuts.ReadTotalTimeoutConstant = 0;
  359. CommTimeOuts.WriteTotalTimeoutMultiplier = 10;
  360. CommTimeOuts.WriteTotalTimeoutConstant = 1000;
  361. if(!SetCommTimeouts( m_hComm, &CommTimeOuts ))
  362. {
  363. TRACE( _T("SetCommTimeouts 返回错误\n") );
  364. //关闭串口
  365. CloseHandle (m_hComm);
  366. m_hComm = INVALID_HANDLE_VALUE;
  367. return FALSE;
  368. }
  369. //指定端口监测的事件集
  370. SetCommMask (m_hComm, EV_RXCHAR);
  371. //分配串口设备缓冲区
  372. SetupComm(m_hComm,1024,1024);
  373. //SetupComm(m_hComm,512,512);
  374. //初始化缓冲区中的信息
  375. PurgeComm(m_hComm,PURGE_TXCLEAR|PURGE_RXCLEAR);
  376. CString strEvent;
  377. strEvent.Format(_T("Com_ReadCloseEvent%d"),portNo);
  378. m_hReadCloseEvent = CreateEvent(NULL,TRUE,FALSE,strEvent);
  379. //创建串口读数据监听线程
  380. if( bCanComRead == true )
  381. {
  382. m_hReadThread = CreateThread(NULL,0,ReadThreadFunc,this,0,&m_dwReadThreadID);//通过this指针将串口对象传递给读线程函数ReadThreadFunc的形参
  383. }
  384. TRACE(_T("串口%d打开成功\n"), portNo);
  385. m_bOpened = true;
  386. return TRUE;
  387. }
  388. //函数介绍:关闭串口
  389. //入口参数:(无)
  390. //出口参数:(无)
  391. //返回值: (无)
  392. void CMyCESeries::ClosePort()
  393. {
  394. //表示串口还没有打开
  395. if (m_hComm == INVALID_HANDLE_VALUE)
  396. return ;
  397. //关闭事件
  398. //TRACE("关闭串口读事件\n");
  399. Sleep(100);
  400. //关闭读线程
  401. CloseReadThread();
  402. //关闭串口
  403. TRACE("CMyCESeries关闭串口%d句柄\n", m_portNo);
  404. CloseHandle (m_hComm);
  405. //关闭事件
  406. CloseHandle(m_olWrite.hEvent);
  407. m_hComm = INVALID_HANDLE_VALUE;
  408. m_bOpened = false;
  409. }
  410. //函数介绍:往串口写入数据
  411. //入口参数:buf :待写入数据缓冲区
  412. // bufLen : 待写入缓冲区长度
  413. //出口参数:(无)
  414. //返回值:TRUE:设置成功;FALSE:设置失败
  415. BOOL CMyCESeries::WriteSyncPort(const BYTE*buf , DWORD bufLen)
  416. {
  417. if( false == m_bSyncOrAsync )//同步方式
  418. {
  419. DWORD dwNumBytesWritten;
  420. DWORD dwHaveNumWritten =0 ; //已经写入多少
  421. int iInc = 0; //如果3次写入不成功,返回FALSE
  422. ASSERT(m_hComm != INVALID_HANDLE_VALUE);
  423. do
  424. {
  425. if (WriteFile (m_hComm, //串口句柄
  426. buf+dwHaveNumWritten, //被写数据缓冲区
  427. bufLen - dwHaveNumWritten, //被写数据缓冲区大小
  428. &dwNumBytesWritten, //函数执行成功后,返回实际向串口写的个数
  429. NULL)) //此处必须设置NULL
  430. {
  431. dwHaveNumWritten = dwHaveNumWritten + dwNumBytesWritten;
  432. //写入完成
  433. if (dwHaveNumWritten == bufLen)
  434. {
  435. break;
  436. }
  437. iInc++;
  438. if (iInc >= 3)
  439. {
  440. TRACE("CMyCESeries::WriteSyncPort 同步写串口失败3次\n");
  441. return FALSE;
  442. }
  443. Sleep(10);
  444. }
  445. else
  446. {
  447. TRACE("CMyCESeries::WriteSyncPort 同步写串口失败\n");
  448. return FALSE;
  449. }
  450. }while (TRUE);
  451. return TRUE;
  452. }
  453. else//异步方式
  454. {
  455. DWORD dwNumBytesWritten;
  456. DWORD dwHaveNumWritten =0 ; //已经写入多少
  457. // OVERLAPPED olWrite;
  458. // memset(&olWrite,0,sizeof(OVERLAPPED));
  459. // m_olWrite.hEvent = CreateEvent(NULL,TRUE,FALSE,_T("WriteData"));
  460. //异步写串口
  461. if (WriteFile (m_hComm, //串口句柄
  462. buf+dwHaveNumWritten, //被写数据缓冲区
  463. bufLen - dwHaveNumWritten, //被写数据缓冲区大小
  464. &dwNumBytesWritten, //函数执行成功后,返回实际向串口写的个数
  465. &m_olWrite) == FALSE)//并不是表示写入失败,而是没有将所有数据写入到缓冲区中,而只是写入了部分而已
  466. {
  467. if(GetLastError() != ERROR_IO_PENDING)
  468. {
  469. TRACE("CMyCESeries::WriteSyncPort 异步写串口0x20\n");
  470. return 0x20;
  471. }
  472. if(GetOverlappedResult(m_hComm,&m_olWrite,&dwNumBytesWritten,TRUE) == FALSE)//这里将一直阻塞,直到将所有数据写入到缓冲区为止
  473. {
  474. TRACE("CMyCESeries::WriteSyncPort 异步写串口0x25\n");
  475. return 0x25;
  476. }
  477. }
  478. //else//写入成功
  479. {
  480. dwHaveNumWritten = dwHaveNumWritten + dwNumBytesWritten;
  481. //写入完成
  482. if (dwHaveNumWritten == bufLen)
  483. return TRUE;
  484. else
  485. {
  486. TRACE("CMyCESeries::WriteSyncPort 异步写串口%d失败\n", m_portNo);
  487. return FALSE;
  488. }
  489. }
  490. }
  491. }
  492. //函数介绍:设置串口读取、写入超时
  493. //入口参数:CommTimeOuts : 指向COMMTIMEOUTS结构
  494. //出口参数:(无)
  495. //返回值:TRUE:设置成功;FALSE:设置失败
  496. BOOL CMyCESeries::SetSeriesTimeouts(COMMTIMEOUTS CommTimeOuts)
  497. {
  498. ASSERT(m_hComm != INVALID_HANDLE_VALUE);
  499. return SetCommTimeouts(m_hComm,&CommTimeOuts);
  500. }
  501. //得到串口是否打开
  502. BOOL CMyCESeries::GetComOpened()
  503. {
  504. return m_bOpened;
  505. }