《电子技术应用》
您所在的位置:首页 > 通信与网络 > 业界动态 > 多功能物流手持终端集群通信方式的设计与实现

多功能物流手持终端集群通信方式的设计与实现

《电子技术应用》2007年第2期
2008-01-04
作者:周晓光, 周晓雄

摘 要:为了提高物流行业的调度效率,提出了基于Windows CE.NET在多功能物流终端中实现模拟集群无线通信的方案。基于Windows CE.NET的多" title="的多">的多线程机制,讨论了该通信过程中线程间的数据同步、通信协议的解析以及数据的分离等问题,并在eMbedded Visual C++ 4.0环境下实现集群模块与多功能物流手持终端" title="手持终端">手持终端之间的通信。
关键词:多功能物流手持终端? 集群通信? 协议帧格式? 串口" title="串口">串口通信? 多线程通信

?

??? 在现代企业中降低商品成本的一个重要方法就是降低货物在流通中的成本,货物的流通速度直接影响到生产的各个环节,物流(Logistics)在现代经济的发展中起着越来越重要的作用。不论是企业的物流部门还是专门的物流公司都面临着调度效率的问题。提高调度效率的方法是在提高物流管理技术的同时实现物流技术的信息化,提高物流技术信息化的程度。虽然国内在物流行业里也引进了信息化,但信息化的程度与国外相比还是低得多。国内的物流终端主要集中在车载型且功能相对简单,这样调度的实时性比较差。
??? 模拟集群通信(Analog Trunking Communication)技术已经相对比较成熟,强大的调度功能、组呼功能和快速呼叫的特性使得集群通信深受大企业或集团公司的青睐。对于物流行业而言,集群通信技术也是其信息化的一个方向,通过无线调度实现快速、便捷、有效的货物投递、配送,实现有效的实时无线调度,提高其调度效率。这里的物流终端是集手机、数字集群、模拟集群、GPS定位、条码扫描、无线上网、快速打印等功能于一体的集成度很高的多功能手持终端,针对不同的用户可以方便快捷地进行不同的功能模块配置。
????以下将讨论在基于Windows CE.NET的多功能物流手持终端上如何用多线程编程实现其与集群模块通信,以及如何解析集群模块中发往手持终端的数据。
1 集群模块与手持终端通信协议帧格式
??? 集群通信中的协议帧格式如下:
?? ?SYNC + LENGTH + DIR + COMMAND + DATA +
??? CHECKSUM
??? SYNC:??0x96 (包头)
??? LENGTH:?DIR(1B)+COMMAND(1B)+DATA(nB) +
??? CHECKSUM(1B)
??? DIR:??0x80 或0x81?
??? COMMAND:?协议号
??? DATA:??协议数据
??? CHECKSUM:?SYNC ^ LENGTH ^ DIR ^ COMMAND ^
?????????? ??? DATA? 即全部字节的异或
??? 其中为了保证通信正常完成,LENGTH约定要不大于125字节;?DIR?表示发送方向: ?0x80表示 上位机>>下位机,?0x81表示下位机>>上位机;(上位机指手持终端,下位机指集群模块);DATA长度不大于122个字节;COMMAND表示协议命令包括0x10,0x11,0x12, 0x13,0x15,0x16等;CHECKSUM是数据结束校验位,判断数据报是否结束。
??? 在该协议中汉字采用国标码进行传输,其他字符或数字等都采用ASCII码进行显示或发送,在这个协议中字符都有特殊意义,例如:‘<’ 表示下翻, ‘>’ 表示上翻,‘C’表示清除等。
2 Windows CE.NET多线程机制
??? ?Windows CE.NET中线程类似于Windows XP中的线程,每个进程都有一个主线程" title="主线程">主线程,而且还可以创建任意多个子线程,但受系统存储器大小和堆栈大小的约束,实际上在一个进程中不能创建过多的线程。
  线程是在创建它的进程的地址空间中执行代码,并且在该进程的地址空间中进行数据操作。一个进程的地址空间中可以有多个线程来共享使用,这个进程的所有线程可以执行相同的代码,对相同的数据进行操作,而且这些线程还可以共享相同的内核对象句柄。多个线程既然可以执行相同的代码并对相同的数据进行操作,就会产生在一个程序里如何控制数据同步的问题。这也是多线程编程的一个难点。Windows CE.Net环境下控制线程同步的方法很多,但用起来也很容易出问题。常用的控制线程同步的方法有:
??? (1)事件对象(Event Object)机制。事件对象是一种在通知状态或不在通知状态的同步对象,线程可以通过捕获事件来达到同步。可以创建手动或者自动复位的事件对象,通过命名和共享事件对象来达到不同线程或进程之间的同步。
??? (2)信号量" title="信号量">信号量(Semaphores)。信号量是通过计数来控制进程或线程间同步的,只要信号量的计数值大于0,信号量就可以被使用。当计数计到0时该信号量就处于不可用状态,它所控制的资源就不可用,直到其他线程释放了该信号量使得计数值大于0才可用。适合多个线程间的通信。
??? (3)互斥量(Mutexes)。当一个线程获得该互斥量时,该互斥量就被锁定,其他线程就不能使用,直到该互斥量被释放。??

  当然还有其他的方法来控制线程的同步。多线程通过线程间的通信可以达到同步,节省系统资源,减少损耗。在接下来的串口通信中选择了事件触发机制来控制线程的运行。
3 集群模块与手持终端的通信以及数据的提取和分析
  集群模块负责接收调度中心或其他终端发出的信号,手持终端自身的CPU负责处理模块接收的信息。手持终端的CPU与集群模块之间通过串口进行通信,首先设定串口参数如下:
??? 波特率?????????????????? 9 600bps
??? 数据位?????????????????? 8bit
??? 校验位?????????????????? 无
??? 停止位?????????????????? 1 bit
????串口参数的设定以及串口操作在这里通过Windows CE.NET自带的API函数来完成,其步骤是:
????(1)打开串口
???? HANDLE? hComPort = CreatFileCreateFile(pszDevName,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING, 0, NULL);
??? 其中,pszDevName表示要打开的串口号,在Windows CE系统中其形式应该为TEXT(“COM1:”)或L“COM1:”,这与UNICODE相一致,当串口打开成功,hComPort应该返回打开的串口句柄,失败则返回INVALID_HANDLE_VALUE。
??? (2)按上述要求设定串口参数
????定义一个DCB结构变量dcb,然后依次设定该结构的参数:
?? ?dcb.DCBlength = sizeof (dcb);
??? GetCommState (hLocal, &dcb);???//获得当前串口的参数放到当前dcb中
??? dcb.BaudRate = CBR_9600; ?????? //设定串口波特率为要求的9600bps
??? dcb.fParity = FALSE;??? ???//设定校验位为无
??? dcb.fNull = FALSE;
??? dcb.StopBits = ONESTOPBIT;? ??//设定一位停止位
??? dcb.Parity = NOPARITY;? ?????? //低4位校验设置为否
??? dcb.ByteSize = 8;????? ???? //数据位设定为8位
??? SetCommState (hLocal, &dcb); ?
??????? //把设定的参数写入串口设定
?? ?设定串口时间常数:设定一个COMMTIME OUTS结构变量cto
??? cto.ReadIntervalTimeout = 0; ?
??????? //读取两个字符间隔时间设定
?? ?cto.ReadTotalTimeoutMultiplier = 0;???? //设定
??? cto.ReadTotalTimeoutConstant = 0;????//设定读超时时间为无限
??? cto.WriteTotalTimeoutMultiplier = 0;????//设定写超时时间为无限时
??? SetCommTimeouts (hLocal, &cto); //把时间设定写进串口
??? 至此,串口初始化编程设定完毕。
??? 由于这里涉及实时收发数据,为了提高主线程响应其他事件的速度,在主线程中创建一个独立的子线程来监听串口,通过触发事件EV_RXCHAR通知线程去读串口缓冲区来得到数据。主线程与子线程的调度关系如图1所示。

?

?


????主线程负责处理主窗口及其控件的消息以及子线程发送过来的消息,子线程主要负责处理串口消息及数据,大大减小了主线程响应事件的压力,提高了系统的响应速度。
????以下讨论如何操作子线程让其很好地收发并解析数据。
 ? 串口监听线程程序流程图如图2所示。从中可以看出线程中设定了事件EV_RXCH-AR,当线程守候到该消息后执行读串口事件,否则线程继续等待该消息,这样既节省系统资源,也省去不断地读串口,减少了资源消耗。

?


??? 其中,串口初始化部分已经在前面介绍了,其余程序的简单实现如下:
??? hRecvThread = CreateThread(NULL, 0, ReceiveProc, hWnd, 0, &dwStat); ???//创建接收线程监听串口
?? ?if (hRecvThread)
???? ??CloseHandle (hRecvThread);
???? 如果子线程创建成功,则线程启动转入线程处理函数(RecvProc)处理接收来的数据。
???? RecvProc实现对串口的监听,实时接收从集群模块发到串口的数据。为了实现这一实时功能,通过串口信号触发一个事件来通知程序去接收数据,这样既可以比较准确地接收数据,也可以减少处理器不断查询串口对系统资源的消耗。下面是简化的线程处理函数的实现:
??? DWORD WINAPI RecvProc(PVOID pArg) {
??? PurgeComm(hComPort, PURGE_RXCLEAR);
??????????? //清空串口的读缓冲区
??? SetCommMask (hComPort, EV_RXCHAR);
?? ?????????? //设定串口读事件EV_RXCHAR
??? while (TRUE)
??? {
?? ?if(WaitCommEvent(hComPort, &evtMask, 0)) {
? ????????????? // 等待串口EV_RXCHAR事件的发生
??? SetCommMask(hComPort, EV_RXCHAR);
 ??????? //如果事件发生则重新设定事件以便下次能再触发
??? if(evtMask&EV_RXCHAR)
??? {???? ?????????????????????? //串口有数据读入
??? ClearCommError(hComPort,&dwReadErrors, &cmState);
??? willreadlen = cmState.cbInQue;
???????????????? //查看串口读缓冲区里数据的实际长度
 ? if (willreadlen <= 0) {
    continue;
 ? }
 ? memset(recvText, '0', sizeof(recvText));
??? if(!ReadFile (hComPort, recvText, willreadlen, &cBytes, 0)) //读串口数据到
 ?{
   ?if(hComPort= =INVALID_HANDLE_VALUE)
   ?return 0;
?? ?}
??? …… //按照数据包的定义从串口数据中提取一个完整的包
   ???????? }
    }
 ?}
??? 程序至此完成从串口缓冲区读数据的过程,下一步程序设计的任务是如何分析分离数据。
 ? 根据数据协议格式可以知道,这里的数据包有相同的包头(0x96),通过包头和数据长度(LENGTH)就可以把数据分离成一个个数据包,再根据协议号(COMMAND)的不同可以完全解析出该数据的含义,从而对数据做出相应的决策(送上显示或报警等)。以下给出一段解析数据包的示例:假设数据包存放在数组RecvText中,则:
??? command = RecvText[3];
??? switch(command){? //由协议号来解析数据
 ? case 0x12: //数据送到上面界面显示
????if(RecvText[4] == 0x01){?
????????? //表示送到主窗口的第一行显示
 ? ??……
?? ?}
??? else{ ????? //表示送主窗口的第二行显示
    ……
  ??}
  ??break;
??? case 0x13: ?//提取手持机电池电量和集群信号强度,
???? 为判断手持机能否工作提供提示
??? ?if(RecvText[4] = = 0x01){
 ????????? //表示该数据包为显示信号强度
??????? …… //把RecvText[5](表信号强度值)进行处理
?????????? }
?????????? else{ ??????? //表示该数据包数据是电池电量
??? ?? …… ?????//发到界面显示电池电量
?????????? }
?????????? break;??????????????????????????????????????????
??? ?case 0x15: ??//进入或退出读出或设置集群模块内部具体地址空间的数据状态
   ???? ……
   ???? break;
???? case 0x16: ????? //在进入读取集群模块内部数据状态后,读取或设置具体地址空间的值
   ???? ……
   ???? break;
  ?}
  该程序内部具体的实现依据界面不同其内部函数实现也不同,这里不一一罗列了。
  至此程序完成了集群模块与手持终端间的通信功能,实现了对下位机发往上位机数据的分离与解析,为上层界面对数据的处理做好了充分的准备。
??? 以上讨论了集群模块与多功能物流手持终端之间通信的协议格式,Windows CE.NET的多线程机制,以及如何利用多线程实现集群模块与手持终端之间的通信,并对数据的分离解析以及串口的配置作了讨论。通过对应用程序的设计,可以完全实现集群模块与手持终端之间的通信以及数据分离解析,为上层界面提供完整的数据服务,使该终端能实现集群通信功能,满足物流行业方便快捷调度的需要。上述程序在eMbedded Visual C++ 4.0环境下编译,下载到Windows CE.NET 的手持终端上能正常稳定地运行,并且其功能与专门的对讲机相比具有更好的可视性、操作更灵活的特点。
参考文献
[1]? 汪 斌,李存斌,陈鹏等. EVC高级编程及其应用开发,北京:中国水利电力出版社,2005.
[2] ?BOLING D. Programming Microsoft Windows CE. NET,?Third Edition, Microsoft Press, 2003.
[3] ?周毓林. Windows CE.NET内核定制及应用开发.北京:?电子工业出版社,2005.

?

?

本站内容除特别声明的原创文章之外,转载内容只为传递更多信息,并不代表本网站赞同其观点。转载的所有的文章、图片、音/视频文件等资料的版权归版权所有权人所有。本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如涉及作品内容、版权和其它问题,请及时通过电子邮件或电话通知我们,以便迅速采取适当措施,避免给双方造成不必要的经济损失。联系电话:010-82306118;邮箱:aet@chinaaet.com。