| « | October 2026 | » | | 日 | 一 | 二 | 三 | 四 | 五 | 六 | | | | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | |
|
| 统计 |
blog名称: 日志总数:122 评论数量:12 留言数量:0 访问次数:320689 建立时间:2009年1月7日 |
| 
|
本站首页 管理页面 写新日志 退出
[编程]Windows CE下串口通信 |
Windows CE下串口通信作者:付林林
网上已经有CE下串口通信的文章了。我之所以发表同样内容的文章是因为我的文章是一系列的,不会因为别人写过我就不写了。另外我对串口通信有着自己的观点。 现在大多数的笔记本电脑都没有外置串口,这不奇怪,因为有更快更稳定的接口代替了串口。不过基于 Windows CE 的设备仍然保留着串口,而且目前看来串口的地位暂时不会动摇。目前流行的基于CE的设备很多都具有像导航、打电话等功能,而GPS、GSM/GPRS模块都是外置串口的终端设备,你想不用串口都不行。 上面我说了我有着自己的观点,我的观点就是不要把串口通信封装成类。我不明白为什么有些人总要把串口封装成类呢。把一个事物封装成类,那这个事物就一定是不易改变的,如果每次编写都要修改,那封装成类就一点意义都没有了。设想如果MFC类总要改变的话,那我们用MFC编的程序也要修改同样次数了。如果编写超级终端一类的程序倒是可以将串口封装成类,因为超级终端只管输入命令和显示输出数据,不对输出数据进行处理,那读串口的函数就可以一直使用而不必更改。但事实上串口通信大多数用来与终端设备进行通信,需要对终端设备返回的数据进行处理。而返回的数据在什么时间返回、数据量的大小不是确定的,非要封装成类难度很大。 正如CE的帮助文档所说,串口通信是最简单的通信之一。稍麻烦的是在读数据方面。一、打开串口 hSerial = CreateFile(L"COM1:",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL);
if(m_hSerial == NULL)
{
///L"串口打开失败";
return;
}
///配置串口
DCB PortDCB;
PortDCB.DCBlength = sizeof(DCB);
// 默认串口参数
GetCommState(hSerial, &PortDCB);
PortDCB.BaudRate = 115200; // baud
PortDCB.ByteSize = 8; // Number of bits/byte, 4-8
PortDCB.Parity = NOPARITY;
PortDCB.StopBits = ONESTOPBIT;
if (! SetCommState(hSerial, &PortDCB))
{
///L"配置串口失败";
return;
}
////配置超时值
COMMTIMEOUTS CommTimeouts;
GetCommTimeouts(m_hSerial, &CommTimeouts);
CommTimeouts.ReadIntervalTimeout = MAXDWORD;
CommTimeouts.ReadTotalTimeoutMultiplier = 10;
CommTimeouts.ReadTotalTimeoutConstant = 10;
CommTimeouts.WriteTotalTimeoutMultiplier = 50;
CommTimeouts.WriteTotalTimeoutConstant = 100;
if (!SetCommTimeouts(hSerial, &CommTimeouts))
{
///L"不能设置超时参数";
return;
} CE的串口驱动不支持重叠,这个大家都知道的。这样的话收和发就要分开。要接收串口数据就必须创建一个线程专门用于接收数据。串口的配置不需要设置很多参数,默认的配置大部分是不需要修改的。一般改动就是波特率、位数、奇偶校检等几项。超时值是需要改动的。ReadIntervalTimeout是指两个字符传送之间的超时时间。一次写操作的超时时间等于WriteTotalTimeoutMultiplier 乘以 要发送的字符数 加上WriteTotalTimeoutConstant。 单位是毫秒。读操作的超时和写类似。所以设置超时是一个关键。设置太小可能丢失数据。二、关闭串口关闭串口用关闭句柄函数。 if(hSerial != NULL)
{
CloseHandle(hSerial);
hSerial = NULL;
}
三、向串口发送数据 WriteFile (hSerial, // 句柄
&Byte, // 数据缓冲区地址
nByte, // 数据大小
&dwNumBytes, // 返回发送出去的字节数
NULL // 不支持重叠
);
向串口发送数据一般都会成功。需要注意的是如果终端设备需要一定处理时间或者称反应时间的话,那么两个写操作之间一定要注意时间间隔不能太小。具体的时间由终端设备的反应时间和缓冲区大小有关。四、读取串口数据 串口麻烦就麻烦在读取数据上。除了考虑及时的读取数据外,还要解决接收到的数据的处理工作。如果在读取串口数据的线程中安置数据处理工作,那么可能会丢失数据(终端设备发送数据但是没收到),也有可能不会丢失(终端设备发送的数据的时间、大小都是确定的)。如果肯定接收的数据在处理工作结束后终端设备才发送数据,那么完全可以将数据处理工作放在读取串口的线程中。对于及时的读取数据,下面提供了一种解决办法: *** 假设接收的都是字符 ***
UINT ReadThread(LPVOID pParam) ?////接收串口数据线程
{
HANDLE hPort = *(HANDLE*)pParam;
BYTE Byte;
int iCounter = 0;
DWORD dwBytes;
char ReceiveBuf[1000]; ///缓冲区的大小
SetCommMask (hPort, EV_RXCHAR); ///只接收字符
while (hPort != INVALID_HANDLE_VALUE)
{
DWORD dwCommStatus;
WaitCommEvent(hPort, &dwCommStatus, 0);
SetCommMask (hPort, EV_RXCHAR); ///重新设置要等待的信号
//// 接收数据
do
{
ReadFile(hPort, &Byte, 1, &dwBytes, 0);
if(dwBytes == 1)
{
ReceiveBuf[iCounter++] = Byte;
if(iCounter == 1000)
{
///L"接收缓冲区已满";
return -1;
}
}
} while (dwBytes == 1);
if(iCounter == 0) ?////没接到数据
{
continue;
}
//////保存数据
char* pTmp = new char[iCounter + 1];
if(pTmp == NULL)
{
///L"内存不足,接收串口数据线程关闭";
return -1;
}
memcpy(pTmp, ReceiveBuf, iCounter);
pTmp[iCounter] = NULL; ////字符串结尾
////////创建新线程处理数据
////在ProcessData函数中处理数据。别忘了delete[] pTmp;
AfxBeginThread(ProcessData, pTmp);
iCounter = 0; ////清空计数器
} ///////end while
return 0;
}
写作时间:2004-08-22 未经本文作者同意,不准擅自转载本篇文章。联系作者请邮至 fllsoft@sina.com 或windowsce@tom.comMSN Messenger:windowsce@tom.com
500)this.width=500'>最新评论 [发表评论] [文章投稿]
500)this.width=500'> 查看所有评论 500)this.width=500'> 推荐给好友 500)this.width=500'> 打印
500)this.width=500'> 我现在是天极网嵌入式开发论坛的版主。大家有问题可以到这个论坛看看。 ( fllsoft 发表于 2005-1-6 14:38:00) 500)this.width=500'> 多谢fllsoft,终于明白了! ( liu_sir 发表于 2004-8-31 8:55:00) 500)this.width=500'> 如果终止读线程后程序就退出,那么可以用TerminateThead。如果终止读线程后程序不退出,那么就要破坏WaitCommEvent。可以按下列方法:设置等待事件掩码时加一项,如SetCommMask(hPort,EV_RXCHAR|EV_TXEMPTY),表示当输出字符都发送出去作为一个事件。那么我们可以发送任意一个字符,这个字符发送出去后WaitCommEvent就返回。还有一种方法,如果WaitCommEvent执行时,这时调用SetCommMask,那么WaitCommEvent会立即返回。大家可以试一试。 ( fllsoft 发表于 2004-8-27 20:26:00) 500)this.width=500'> SetCommMask (hPort, EV_RXCHAR); ///只接收字符WaitCommEvent(hPort, &dwCommStatus, 0);SetCommMask (hPort, EV_RXCHAR); ///重新设置要等待的信号--------------------------------------------------感觉这样用WaitCommEvent监听com口事件是对的,可是有一点不明白,WaitCommEvent会同步等待,如果关闭串口的时候,如果解除等待,使线程返回? ( liu_sir 发表于 2004-8-27 17:27:00) 500)this.width=500'> 另外这个网友所说的很有道理。他说用一个线程C专门处理接收的数据。而接收线程B在接收数据后把数据交由C处理。我强调我的方法不是最好的,只是一种方法。 ( fllsoft 发表于 2004-8-25 11:53:00) 500)this.width=500'> 可能是我的错!在此声明:我写的这个例子不是通用的。也不存在通用的。具体问题具体分析。关于SetCommMask请去询问微软。微软提供的文档中的例子中重新设置了SetCommMask。 ( fllsoft 发表于 2004-8-25 11:48:00) 500)this.width=500'> 作者的观点:我的观点就是不要把串口通信封装成类。我不明白为什么有些人总要把串口封装成类呢我是坚决不同意的。数据的处理根本是不在这一层的。另外我不知到为什么要每次SetCommMask (hPort, EV_RXCHAR); ///重新设置要等待的信号请告之。 ( sander 发表于 2004-8-25 10:03:00) .......................................................More...
版权所有 © 1999 - 2008 VC知识库
|
阅读全文(3849) | 回复(0) | 编辑 | 精华 |
|