18. {
19. BYTE bt=*(char*)(rxdata+k); //字符型
20. strtemp.Format("%c",bt); //将字符送入临时变量strtemp存放
21. m_strRXData+=strtemp; //加入接收编辑框对应字符串
22. }
23. }
24.
25. //以下可根据需要处理接收到的数据
26. ....
27.}
由于用Mscomm控件的GetInput方法读到的串口缓冲区数据为VARIANT格式,需要用一个COleSafeArray类型来转换,具体步骤按上面的示例来就是了。
2、动态链接库dll读串口方法
不同的串口dll有不同的方法,应该根据其手册来,如VC串口通信技术网的VC串口通信技术资料集里的一个例子用到的串口dll-ThreadSerial.dll,它是通过查询成员变量m_LastIndex的值是否大于1来判断是否接串口缓冲区中数据的,VC串口通信技术资料集的一个例子串口调试助手EasySerialAssistant就是用的这个dll,
这个示例专门为读串口开了一个线程,在线程里不断地读m_LastIndex的值,如果大于代表串口缓冲区中有数据,然后将数据显示到显示区:
1.UINT CEasySerialAssistantDlg:
:
ProDispThread(LPVOID pParam)
2.{
3. CEasySerialAssistantDlg* pParlPro=(CEasySerialAssistantDlg*)pParam;
4. pParlPro->m_bThread=TRUE;
5. CString c="",d="";
6. char b[4];
7.
8. while
(1)
9. {
10. Sleep
(1);
11.
12. if(pParlPro->m_LastIndex > 0)
13. {
14. c.Format("RX:
%d t:
%d n:
%d",pParlPro->m_RecvCount,pParlPro->m_nCurLen,pParlPro->m_nRecvTimes);
15. (pParlPro->GetDlgItem(IDC_STATIC_RX))->SetWindowText(c);
16.
17. if(pParlPro->m_bDispRecv == FALSE)
18. {
19. pParlPro->m_LastIndex=0;
20. }
21. else
22. {
23. CEdit *pList =(CEdit *)(pParlPro->GetDlgItem(IDC_EDIT_RX_DISP));
24.
25. if(pParlPro->m_bAutoClearn == FALSE) c=pParlPro->m_StrBuf;
26. else c="";
27.
28. if(pParlPro->m_bHexDispMode == TRUE)
29. {
30. for(int i=0;i<(int)pParlPro->m_LastIndex;i++)
31. {
32. :
:
sprintf(b,"%2.2X",pParlPro->m_RecvBuf[i]);
33. c+=" ";
34. c+=b;
35. }
36. pParlPro->m_LastIndex=0;
37. }
38. else
39. {
40. pParlPro->m_RecvBuf[pParlPro->m_LastIndex+1]='\0';
41. d.Format("%s",pParlPro->m_RecvBuf);
42. if(pParlPro->m_bAutoChangRow == TRUE)
43. {
44. d.Insert(c.GetLength(),"\r\n");
45. }
46. c+=d;
47. pParlPro->m_LastIndex=0;
48. }
49.
50. if(pParlPro->m_bAutoClearn == FALSE) pParlPro->m_StrBuf=c;
51.
52. pList->SetWindowText(c);
53.
54. unsigned int nVScrollPos=pList->GetLineCount();
55. pList->LineScroll(nVScrollPos);
56. }
57. }
58. }
59.
60. pParlPro->m_bThread=false; //工作标志复位
61. return 0;
62.}
4、串口类读串口方法
同样不同串口类有不同方法和接口,这里只说说最常用的串口类CSerialPort的读串口方法。
启动串口通信监测线程函数StartMonitoring(),串口初始化成功后,就可以调用BOOLStartMonitoring()启动串口监测线程。
线程启动成功,返回TRUE。
串口收到的字符会向主线程或父窗口发送WM_COMM_RXCHAR消息,我们需要在主线程或父窗口中手动映射这个消息,手动映射消息的方法属于VC基础性内容,就不在这里讲解了,本站VC串口通信技术资料集有很多CSerialPort类的应用实例,大家可以参考,以下本资料中一个实例 串口调试助手SComAssistant的串口接收源代码,OnCommunication函数是WM_COMM_RXCHAR消息的映射函数:
1.LONG CSCOMMDlg:
:
OnCommunication(WPARAM ch, LPARAM port)
2.{
3. if (port <= 0 || port > 4)
4. return -1;
5. rxdatacount++; //接收的字节计数
6. CString strTemp;
7. strTemp.Format("%ld",rxdatacount);
8. strTemp="RX:
"+strTemp;
9. m_ctrlRXCOUNT.SetWindowText(strTemp); //显示接收计数
10.
11. if(m_bStopDispRXData) //如果选择了“停止显示”接收数据,则返回
12. return -1; //注意,这种情况下,计数仍在继续,只是不显示
13. //若设置了“自动清空”,则达到50行后,自动清空接收编辑框中显示的数据
14. if((m_ctrlAutoClear.GetCheck())&&(m_ctrlReceiveData.GetLineCount()>=50))
15. {
16. m_ReceiveData.Empty();
17. UpdateData(FALSE);
18. }
19. //如果没有“自动清空”,数据行达到400后,也自动清空
20. //因为数据过多,影响接收速度,显示是最费CPU时间的操作
21. if(m_ctrlReceiveData.GetLineCount()>400)
22. {
23. m_ReceiveData.Empty();
24. m_ReceiveData="***The Length of the Text is too long, Emptied Automaticly!
!
!
***\r\n";
25. UpdateData(FALSE);
26. }
27.
28. //如果选择了"十六进制显示",则显示十六进制值
29. CString str;
30. if(m_ctrlHexReceieve.GetCheck())
31. str.Format("%02X ",ch);
32. else
33. str.Format("%c",ch);
34. //以下是将接收的字符加在字符串的最后,这里费时很多
35. //但考虑到数据需要保存成文件,所以没有用List Control
36. int nLen=m_ctrlReceiveData.GetWindowTextLength();
37. m_ctrlReceiveData.SetSel(nLen, nLen);
38. m_ctrlReceiveData.ReplaceSel(str);
39. nLen+=str.GetLength();
40.
41. m_ReceiveData+=str;
42. return 0;
43.}
4、串口API如何读串口
以上各串口编程方法的实质都是调用了API的,只是被封装了而已,读串口的API函数是ReadFile,由于不知道何时串口收到数据,所以一般需要开一个线程不停地用ReadFile来读串口。
在用ReadFile读串口时,既可以同步执行,也可以重叠执行。
在同步执行时,函数直到操作完成后才返回。
这意味着同步执行时线程会被阻塞,从而导致效率下降。
在重叠执行时,即使操作还未完成,这个函数也会立即返回,费时的I/O操作在后台进行。
ReadFile函数是同步还是异步由CreateFile函数决定,如果在调用CreateFile创建句柄时指定了FILE_FLAG_OVERLAPPED标志,那么调用ReadFile对该句柄进行的操作就应该是重叠的;如果未指定重叠标志,则读写操作应该是同步的。
ReadFile和WriteFile函数的同步或者异步应该和CreateFile函数相一致。
ReadFile函数只要在串口输入缓冲区中读入指定数量的字符,就算完成操作。
如果操作成功,这个函数返回TRUE。
需要注意的是,当ReadFile返回FALSE时,不一定就是操作失败,线程应该调用GetLastError函数分析返回的结果。
例如,在重叠操作时如果操作还未完成函数就返回,那么函数就返回FALSE,而且GetLastError函数返回ERROR_IO_PENDING。
这说明重叠操作还未完成。
同步方式读写串口比较简单,下面先是同步方式读写串口的代码示例:
1.//同步读串口
2.char str[100];
3.DWORD wCount;//读取的字节数
4.BOOL bReadStat;
5.bReadStat=ReadFile(hCom,str,100,&wCount,NULL);
6.if(!
bReadStat)
7.{
8. AfxMessageBox("读串口失败!
");
9. return FALSE;
10.}
11.return TRUE;
12.
13.//同步写串口
14.
15. char lpOutBuffer[100];
16. DWORD dwBytesWrite=100;
17. COMSTAT ComStat;
18. DWORD dwErrorFlags;
19. BOOL bWriteStat;
20. ClearCommError(hCom,&dwErrorFlags,&ComStat);
21. bWriteStat=WriteFile(hCom,lpOutBuffer,dwBytesWrite,& dwBytesWrite,NULL);
22. if(!
bWriteStat)
23. {
24. AfxMessageBox("写串口失败!
");
25. }
26. PurgeComm(hCom, PURGE_TXABORT|
27. PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
在重叠操作时,操作还未完成函数就返回。
重叠I/O非常灵活,它也可以实现阻塞(例如我们可以设置一定要读取到一个数据才能进行到下一步操作)。
有两种方法可以等待操作完成:
一种方法是用象WaitForSingleObject这样的等待函数来等待OVERLAPPED结构的hEvent成员;另一种方法是调用GetOverlappedResult函数等待。
OVERLAPPED结构和GetOverlappedResult函数请查看MSDN,本站VC串口通信资料集中也有详细详述。
异步读串口的示例代码:
1.char lpInBuffer[1024];
2.DWORD dwBytesRead=1024;
3.COMSTAT ComStat;
4.DWORD dwErrorFlags;
5.OVERLAPPED m_osRead;
6.memset(&m_osRead,0,sizeof(OVERLAPPED));
7.m_osRead.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
8.
9.ClearCommError(hCom,&dwErrorFlags,&ComStat);
10.dwBytesRead=min(dwBytesRead,(DWORD)ComStat.cbInQue);
11.if(!
dwBytesRead)
12.return FALSE;
13.BOOL bReadStatus;
14.bReadStatus=ReadFile(hCom,lpInBuffer,
15. dwBytesRead,&dwBytesRead,&m_osRead);
16.
17.if(!
bReadStatus) //如果ReadFile函数返回FALSE
18.{
19. if(GetLastError()==ERROR_IO_PENDING)
20. //GetLastError()函数返回ERROR_IO_PENDING,表明串口正在进行读操作
21. {
22. WaitForSingleObject(m_osRead.hEvent,2000);
23. //使用WaitForSingleObject函数等待,直到读操作完成或延时已达到2秒钟
24. //当串口读操作进行完毕后,m_osRead的hEvent事件会变为有信号
25. PurgeComm(hCom, PURGE_TXABORT|
26. PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
27. return dwBytesRead;
28. }
29. return 0;
30.}
31.PurgeComm(hCom, PURGE_TXABORT|
32. PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
33.return dwBytesRead;
对以上代码再作简要说明:
在使用ReadFile函数进行读操作前,应先使用ClearCommError函数清除错误。
ClearCommError函数的原型如下:
1.BOOL ClearCommError(
2.
3. HANDLE hFile, // 串口句柄
4. LPDWORD lpErrors, // 指向接收错误码的变量
5. LPCOMSTAT lpStat // 指向通讯状态缓冲区
6. );
该函数获得通信错误并报告串口的当前状态,同时,该函数清除串口的错误标志以便继续输入、输出操作。
参数lpStat指向一个COMSTAT结构,该结构返回串口状态信息。
COMSTAT结构COMSTAT结构包含串口的信息,结构定义如下:
1.typedef struct _COMSTAT { // cst
2. DWORD fCtsHold :
1; // Tx waiting for CTS signal
3. DWORD fDsrHold :
1; // Tx waiting for DSR signal
4. DWORD fRlsdHold :
1; // Tx waiting for RLSD signal
5. DWO