串行通讯是目前计算机、通信和控制领域最基本的通信方式。但采用那种串口通讯类呢?一般的情况下是给你提供一个Pocket PC 2002的SDK例子程序。但到底SDK的程序和MFC的结构有很大的不同,对于想用MFC编写通信程序的人来说也不是很便利。
另一方面,由于Windows CE是一个基于Unicode的操作系统,并且Windows CE不支持Windows下常用的串行通信重叠I/O方式(OVERLAPPED),因此编写Windows CE下的串口通讯类有一些与桌面Windows不同的地方。
以下是我从SDK程序改写而来的MFC类,希望能和致力于WINCE开发的朋友多多交流,由于本人才疏学浅,程序中有许多不完善的地方,请大家指正。我的程序是基于“主动发送请求,被动接收响应”的假设,因此我只设置了一个接收数据的线程。如果有朋友能提供有独立发送数据和接收数据线程的类,我将十分感激。
头文件Serial.h
// Serial.h: interface for the CSerial class. // // #if !defined(AFX_SERIAL_H__59575586_AAA9_4FEF_B2A7_E089553698EF__INCLUDED_) #define AFX_SERIAL_H__59575586_AAA9_4FEF_B2A7_E089553698EF__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 DWORD WINAPI ReadPortThread(LPVOID lpvoid); //读数据线程 class CSerial { public: BOOL InitCommTimeouts(); //设置超时参数 BOOL InitDCB(); //配置串口 BOOL m_bConnected; BOOL ClosePort(HANDLE hCommPort); //关闭串口 DWORD WritePort(TCHAR *buf,DWORD dwBytesToWrite); //写数据 BOOL OpenPort(LPTSTR lpszPortName); //打开串口 CSerial(); HANDLE hReadThread; virtual ~CSerial(); }; #endif // !defined(AFX_SERIAL_H__59575586_AAA9_4FEF_B2A7_E089553698EF__INCLUDED_) 源文件:Serial.cpp // Serial.cpp: implementation of the CSerial class. // // #include "stdafx.h" #include "Serial.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif HANDLE hPort; CString strInChar; // // Construction/Destruction // CSerial::CSerial() { } CSerial::~CSerial() { if(hPort != INVALID_HANDLE_VALUE) ClosePort(hPort); } BOOL CSerial::OpenPort(LPTSTR lpszPortName) { DWORD dwError, dwThreadID; if(hPort) { return FALSE; } //打开串口 hPort = CreateFile (lpszPortName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,0, NULL); //如果打开端口出错, 返回FALSE if ( hPort == INVALID_HANDLE_VALUE ) { //不能打开端口 CString strError; strError.Format(_T("Unable to open %s, Error No.=%d"), lpszPortName, GetLastError()); MessageBox (NULL, strError, TEXT("Error"), MB_OK); return FALSE; } //指定端口监测的事件集 SetCommMask (hPort, EV_RXCHAR); //分配设备缓冲区 SetupComm(hPort,512,512); //初始化缓冲区中的信息 PurgeComm(hPort,PURGE_TXCLEAR|PURGE_RXCLEAR); //配置串行端口 if(!InitDCB()) return FALSE; //设置端口超时值 if(!InitCommTimeouts()) return FALSE; //设置端口上指定信号的状态 // SETDTR: 发送DTR (data-terminal-ready)信号 // SETRTS: 发送RTS (request-to-send)信号 EscapeCommFunction (hPort, SETDTR); EscapeCommFunction (hPort, SETRTS); //创建一个从串口读取数据的线程 if (hReadThread = CreateThread (NULL, 0, ReadPortThread, 0, 0,&dwThreadID)) { } else { //不能创建线程 MessageBox (NULL, TEXT("Unable to create the read thread"), TEXT("Error"), MB_OK); dwError = GetLastError (); return FALSE; } m_bConnected=TRUE; return TRUE; } DWORD CSerial::WritePort(TCHAR *buf,DWORD dwCharToWrite) { BOOL fWriteState; DWORD dwBytesWritten; //写入数据 fWriteState=WriteFile(hPort,buf,dwCharToWrite*sizeof(TCHAR),&dwBytesWritten,NULL); if(!fWriteState) { //不能写数据 MessageBox(NULL,TEXT("Can't Write String to Comm"),TEXT("Error"),MB_OK); dwBytesWritten=0; } return dwBytesWritten; } DWORD WINAPI ReadPortThread(LPVOID lpvoid) { BOOL fReadState; DWORD dwCommModemStatus; DWORD dwLength; COMSTAT ComStat; DWORD dwErrorFlags; while (hPort != INVALID_HANDLE_VALUE) { //等待串口的事件发生 WaitCommEvent (hPort, &dwCommModemStatus, 0); if (dwCommModemStatus & EV_RXCHAR) { ClearCommError(hPort,&dwErrorFlags,&ComStat); //cbInQue返回在串行驱动程序输入队列中的字符数 dwLength=ComStat.cbInQue; if(dwLength>0) { //从串口读取数据 TCHAR* buf=new TCHAR[256]; fReadState=ReadFile(hPort,buf,dwLength,&dwLength,NULL); if(!fReadState) { //不能从串口读取数据 MessageBox(NULL,TEXT("Error in read from serial port"),TEXT("Read Error"),MB_OK); } else { //把数据赋值给全局变量 strInChar=buf; } delete[] buf; } } GetCommModemStatus (hPort, &dwCommModemStatus); } return 0; } BOOL CSerial::ClosePort(HANDLE hCommPort) { if (hCommPort != INVALID_HANDLE_VALUE) { //设置连接属性为FALSE m_bConnected=FALSE; //结束线程中WaitCommEvent的等待 SetCommMask(hPort,0); //阻塞至线程停止 if(hReadThread) { TerminateThread(hReadThread,0); CloseHandle(hReadThread); } //清除端口上指定信号的状态 EscapeCommFunction(hPort,CLRDTR); EscapeCommFunction(hPort,CLRRTS); //清除驱动程序内部的发送和接收队列 PurgeComm(hPort,PURGE_TXCLEAR|PURGE_RXCLEAR); //关闭串口 CloseHandle (hCommPort); hCommPort = INVALID_HANDLE_VALUE; return TRUE; } else { return TRUE; } } BOOL CSerial::InitDCB() { DCB PortDCB; DWORD dwError; PortDCB.DCBlength = sizeof (DCB); //得到端口的默认设置信息 GetCommState (hPort, &PortDCB); //改变DCB结构设置 PortDCB.BaudRate = 19200; //波特率 PortDCB.fBinary = TRUE; //Win32不支持非二进制串行传输模式,必须为TRUE PortDCB.fParity = TRUE; //启用奇偶校验 PortDCB.fOutxCtsFlow = TRUE; //串行端口的输出由CTS线控制 PortDCB.fOutxDsrFlow = FALSE; //关闭串行端口的DSR流控制 PortDCB.fDtrControl = DTR_CONTROL_ENABLE; //启用DTR线 PortDCB.fDsrSensitivity = FALSE; //如果设为TRUE将忽略任何输入的字节,除非DSR线被启用 //PortDCB.fTXContinueOnXoff = TRUE; //当为TRUE时,如果接收缓冲区已满且驱动程序已传送XOFF字符,将使驱动程序停止传输字符 PortDCB.fTXContinueOnXoff = FALSE; PortDCB.fOutX = FALSE; //设为TRUE指定XON/XOFF控制被用于控制串行输出 PortDCB.fInX = FALSE; //设为TRUE指定XON/XOFF控制被用于控制串行输入 PortDCB.fErrorChar = FALSE; //WINCE串行驱动程序的默认执行将忽略这个字段 PortDCB.fNull = FALSE; //设为TRUE将使串行驱动程序忽略收到的空字节 PortDCB.fRtsControl = RTS_CONTROL_ENABLE; //启用RTS线 PortDCB.fAbortOnError = FALSE; //WINCE串行驱动程序的默认执行将忽略这个字段 PortDCB.ByteSize = 8; //每字节的位数 PortDCB.Parity = NOPARITY; //无奇偶校验 PortDCB.StopBits = ONESTOPBIT; //每字节一位停止位 //根据DCB结构配置端口 if (!SetCommState (hPort, &PortDCB)) { //不能配置串行端口 MessageBox (NULL, TEXT("Unable to configure the serial port"),TEXT("Error"), MB_OK); dwError = GetLastError (); return FALSE; } return TRUE; } BOOL CSerial::InitCommTimeouts() { COMMTIMEOUTS CommTimeouts; DWORD dwError; //得到超时参数 GetCommTimeouts (hPort, &CommTimeouts); //改变COMMTIMEOUTS结构设置 CommTimeouts.ReadIntervalTimeout = MAXDWORD; CommTimeouts.ReadTotalTimeoutMultiplier = 0; CommTimeouts.ReadTotalTimeoutConstant = 0; CommTimeouts.WriteTotalTimeoutMultiplier = 10; CommTimeouts.WriteTotalTimeoutConstant = 1000; //设置端口超时值 if (!SetCommTimeouts (hPort, &CommTimeouts)) { //不能设置超时值 MessageBox (NULL, TEXT("Unable to set the time-out parameters"), TEXT("Error"), MB_OK); dwError = GetLastError (); return FALSE; } return TRUE; } |
以上类代码在eMbedded Visual C++4.0和基于ARM9的三星S3C2410开发板(运行Windows CE.NET 4.1)上测试通过。