当前位置: 首页 > news >正文

[转]Windows CE下的串口通信编程(by fllsoft)

现在大多数的笔记本电脑都没有外置串口,这不奇怪,因为有更快更稳定的接口代替了串口。不过基于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; 字符串结尾
  创建新线程处理数据
  AfxBeginThread(ProcessData, pTmp); 在ProcessData函数中处理数据。别忘了delete[] pTmp;
  iCounter = 0; 清空计数器
 } ///end while
 return 0;
}


  在ReadThread的代码中,对接收数据后的处理采用另外一个线程来执行,这种解决方法不适合所有需要,只适合读取终端设备返回来的少量数据。软件开发者要编写串口读线程代码应该具体问题具体分析。另外如果想在任意时刻停止读串口线程,应该按如下形式调用函数:


SetCommMask(hPort,EV_RXCHAR|EV_TXEMPTY);


  EV_TXEMPTY表示当输出字符都发送出去作为一个事件。那么我们可以发送任意一个字符,这个字符发送出去后WaitCommEvent就返回。

  付林林:

  2001年毕业,一直从事Windows CE下操作系统内核定制和应用软件开发工作,参与中国第一台Auto PC(车载电脑)的软件研发工作,希望和CE平台开发者交流、探讨。

  联系作者请邮至:fllsoft@sina.com或windowsce@tom.com;MSN:Messenger:windowsce@tom.com

转载于:https://www.cnblogs.com/harber/archive/2007/12/07/987256.html

相关文章:

  • 路由汇总
  • 完成PIX525的NAT映射
  • Community Server专题六:Delegates Events [转]
  • 微软,IT界老A
  • asp.net生成高质量缩略图通用函数(c#代码),支持多种生成方式
  • [翻译]简单谈谈事件与委托
  • WCF学习(一)
  • 新年第一天开通我的博客
  • 使用fail2ban保护系统一例(ssh)
  • javascript 中的xml dom
  • .NET中统一的存储过程调用方法(收藏)
  • Sun全球媒体高峰论坛开幕 CEO称重回快速增长轨道
  • 电脑总是丢失文件
  • ATL7.0 中 已经没有了CComModule
  • C++编程规范
  • 【跃迁之路】【444天】程序员高效学习方法论探索系列(实验阶段201-2018.04.25)...
  • 0基础学习移动端适配
  • flask接收请求并推入栈
  • iOS小技巧之UIImagePickerController实现头像选择
  • leetcode讲解--894. All Possible Full Binary Trees
  • Phpstorm怎样批量删除空行?
  • Redux 中间件分析
  • scala基础语法(二)
  • Spring声明式事务管理之一:五大属性分析
  • vue总结
  • 创建一种深思熟虑的文化
  • 看图轻松理解数据结构与算法系列(基于数组的栈)
  • 前端每日实战 2018 年 7 月份项目汇总(共 29 个项目)
  • 为视图添加丝滑的水波纹
  • JavaScript 新语法详解:Class 的私有属性与私有方法 ...
  • ​​​​​​​GitLab 之 GitLab-Runner 安装,配置与问题汇总
  • ​Z时代时尚SUV新宠:起亚赛图斯值不值得年轻人买?
  • !!java web学习笔记(一到五)
  • #数学建模# 线性规划问题的Matlab求解
  • (13)Latex:基于ΤΕΧ的自动排版系统——写论文必备
  • (附源码)计算机毕业设计SSM疫情社区管理系统
  • (九十四)函数和二维数组
  • (十一)手动添加用户和文件的特殊权限
  • (四)Android布局类型(线性布局LinearLayout)
  • (万字长文)Spring的核心知识尽揽其中
  • (转)LINQ之路
  • (转)自己动手搭建Nginx+memcache+xdebug+php运行环境绿色版 For windows版
  • (转载)微软数据挖掘算法:Microsoft 时序算法(5)
  • .net core webapi Startup 注入ConfigurePrimaryHttpMessageHandler
  • .NET Core 版本不支持的问题
  • .NET NPOI导出Excel详解
  • .NET开发人员必知的八个网站
  • @require_PUTNameError: name ‘require_PUT‘ is not defined 解决方法
  • @SentinelResource详解
  • @vue/cli脚手架
  • [145] 二叉树的后序遍历 js
  • [16/N]论得趣
  • [2021ICPC济南 L] Strange Series (Bell 数 多项式exp)
  • [28期] lamp兄弟连28期学员手册,请大家务必看一下
  • [ANT] 项目中应用ANT