热门标签 | HotTags
当前位置:  开发笔记 > 后端 > 正文

基于API函数的串口通信编程——理解(转)

用到的串口通信编程方法有:使用通信控件、在高级语言中嵌入汇编以及使用API函数。在这几种方法中,使用API函数编写的串口通信程序最为高效、灵活。串口通信编程将用到三种API函数——串口通信相关API函
用到的串口通信编程方法有:使用通信控件、在高级语言中嵌入汇编以及使用API函数。在这几种方法中,使用API函数编写的串口通信程序最为高效、灵活。串口通信编程将用到三种API函数——串口通信相关API函数、多线程API函数和实现消息机制的API函数,下面将分别介绍这几种API函数。
&nbs

p;  1 与串口通信有关的API函数
  Windows系统通信一般都以WOSA(Windows Open Services Architecture,即Windows开放式服务体系)模型为基础,在此模型中位于上层的应用程序通过调用各种通信API与位于下层的设备驱动程序进行数据交换。下面将按一般串口通信程序的流程顺序介绍这些API函数。
  1.1 打开串口
  Win32操作系统把串口看作一个文件,因此打开串口我们将要用到CreateFile函数。该函数第一个参数指明要打开的文件名称,对串口操作来说,就是COM1、COM2等。第二个参数为读写模式设置,因为要对串口进行读写,所以该参数应设为GENERIC_READ|GENERIC_WRITE。第三个参数值必须为0,表示不将串口与其他应用程序共享。第四个参数指向一个Security Attribute结构,通常设为NULL。第五个参数指定如何打开文件,在打开设备(串口是一种设备)时,此参数必须指定为OPEN_EXISTING。第六个参数指定文件属性及相关标  志,但是对于串行口,唯一有意义的设置是FILE_FLAG_OVERLAPPED或0;最后一个参数必须为NULL。若该函数打开串口成功,则返回创建的句柄,该句柄供随后对串行口的设置、读写等操作用;否则返回INVALID_HANDLE_VALUE。
  1.2 设置串口
  串口打开后,即可进行一系列初始化设置。最基本的初始化设置将通过GetCommState和SetCommState函数来实现。先调用GetCommState函数获取当前串口配置填充设备控制块(DCB),然后将DCB结构中几个重要参数如波特率、数据位、停止位、校验位改成符合实际设计要求的值,最后用SetCommState将刚刚所做的改动重新设置串口。串口I/O缓冲区的大小用SetupComm函数设置。通信速率越高,缓冲区应设置得越大,但不能超出设备驱动程序所能 处理的范围。另一个很重要的设置是串口超时设置。通信中因未知原因将出现不可预测的事件,譬如:接收数据过程中突然被中断,或者发送数据突然停止等。如果不认真对待,这些情况可能会引起I/O线程挂起或者线程被无限阻塞。Windows对于这类问题提供了安全措施,它可通过超时设置来决定通信是否异常并作相应处理,因此超时设置在串行通信中显得尤为重要。超时设置过程分为两步,首先设置Commtimeouts结构中的五个成员,然后调用SetCommTimeouts函数设置超时值。Commtimeouts结构的五个成员分别是:读串口间隔超时、读串口总超时乘数、读串口总超时常数(ms)、写串口总超时乘数、写串口总超时常数(ms)。
  1.3 读写串口
  设置工作完成后,即可用ReadFile和WriteFile对串口进行读写操作。在调用读写操作函数之前,应先用ClearCommError函数清除错误标志和获取当前串口状态。读写操作分为同步和重叠I/O(异步)。同步执行时,函数直到操作完成后才返回;重叠I/O操作时,即使操作尚未完成,调用的函数也会立即返回,费时的I/O操作在后台进行。可见,同步操作线程被阻塞,效率低,只能用在对通信要求比较低的场合,我们一般用到的都是效率较高的重叠I/O操作。前面提到的CreateFile函数第六个参数设置为FILE_FLAG_OVERLAPPED即可指定Read File和WriteFile函数为重叠I/O执行。使用重叠I/O还需为读写函数指定一个Overlapped结构,该结构有五个数据成员,对串口通信来说,其中的Offset和hEvent成员是很重要的。Offset指示文件指针偏移量,在重叠I/O操作时系统不能自动维护文件指针,所以要靠Offset在程序中手动调整文件指针。而hEvent标志读写操作是否完成。若操作完成,

 

  则将hEvent置为信号态;否则即置为非信号态。最后要说明的是,在重叠I/O操作时,读写函数返回值是FALSE并不能说明操作失败,应该调用GetLastError函数分析返回结果。如果 此时GetLastError函数返回值是ERROR_IO_PENDING,则说明操作未完成(并不是操作失败)。我们将用等待函数来等待操作的完成。典型的两个等待函数有WaitForSingleObject和GetOverlappedResult。函数的相同之处为都是等待读写操作指定的Overlapped结构hEvent成员置为信号态(即代表操作完成);不同之处是WaitForSingleObject是可设置超时,但无法得到重叠I/O操作的结果,GetOverlappedResult用来得到重叠I/O操作的结果,但无法设置超时。因此,我们经常两者结合起来使用,在用WaitForSingleObject等待操作结束后,用GetOverlap-pedResult得到操作结果。
  1.4 关闭串口
  串行口是非共享资源,某应用程序打开串行口后,即独占该资源,使其它应用程序无法再访问,直到该应用程序释放串口。所以对串口操作完成后,一定要关闭串口。关闭串口使用CloseHandle函数,该函数唯一参数即为用CreateFile打开串口时所创建的句柄。
  2 多线程API函数
  Windows是多线程(multi-threaded)、抢先多任务的(preemptible )。Windows中,一个可执行程序的运行时刻实例称为进程(process)。一个进程可以有多个线程(thread),Windows是按照线程分配CPU时间片的,而分配的机制就是抢先多任务方式。
  对于读写串口这种耗时的工作,使用多线程技术,创建辅助线程来管理串口是一个常用的方案。这样在进行串口读写的同时,能对读入的数据进行处理。如果使用单线程,就需要等待串口读写操作完成,整个进程都被阻塞。而使用多线程就可以避免这种情况。
  多线程也会带来一些新的问题,其中的一个问题就是线程的同步,如果同步问题解决不好,程序的稳定性会受到很大的影响。通常用到的几种线程同步的方法有互斥体对象(Mutex)、利用信号(Semaphore)、利用事件对象(Event)和设置临界区(Critical Section)。笔者在实际应用中使用的是事件对象结合Windows消息机制使线程同步,收到了很好的效果。
  创建线程函数为CreateThread,用SuspendThread和ResumeThread函数来挂起和唤醒线程。创建事件函数为CreateEvent,用SetEvent和ResetEvent函数来将事件置为信号态和非信号态,以此来同步线程。串口通信的辅助线程管理经常还要用到SetCommMask和WaitCommEvent函数。SetCommMask用来指定一系

  列事件监视串口,比如监视串口是否有数据收到;WaitCommEvent则用来等待指定的事件发生。笔者在实际应用中,就是在辅助线程中用SetCommMask指定串口监视接收数据事件,然后用WaitCommEvent等到串口真的接收到数据时,用PostMessage发出消息通知主线程,由主线程处理接收到的数据。
  3 实现消息机制的API函数
  Windows是一个消息驱动操作系统,简单的说消息就是指通过输入设备向程序发出的指令以要求

  其执行某个操作。具体的操作由消息处理函数实现。用户可以自定义消息在线程之间传递。把WM_USER(它的值等于0×0400)当作基数,然后顺序地去加序号,譬如:
  WM_COMMNOTIFY equ WM_USER+100h(小于WM_USER的值是Windows系统的保留值,大于该值留给用户来使用)。
  前一节已经提到在串口通信编程中对消息机制的利用,这里将继续说明怎样实现消息机制。由于笔者使用的编程工具是Borland公司的C++ Builder(BCB),因此对于消息机制的实现有其特殊之处。在BCB中实现消息的方法有三种:使用消息映射Message Map 重载TObject的Dispatch虚成员函数;重载TControl的WndProc方法;重载Appli

  cation的OnMessage方法。其中以第三种方法最快,因为一般情况下,BCB会为每个程序自动生成一个TApplication类的实例,消息到达BCB程序时,最先得到它们的就是TApplication对象。经由TApplication之后,才传递给Form的。前两种方法都是重载TForm的方法,显然比直接重载Application的OnMessage方法要晚一些收到消息。我们要做的只是定义好自己的消息处理函数:
  void __fastcall TForm1MyOnMessagetagMSG &Msg
bool &Handled 
  
  TMessage Message
  switch(Msg.message)
  
  case WM_COMMNOTIFY
  Message.Msg=Msg.message;
  Message.WParam=Msg.wParam;
  Message.LParam=Msg.lParam;
  //此处添加处理该消息的代码
  Handled=true;
  break;
     
  
  然后在窗口创建时用自定义的消息处理函数重载Application的OnMessage方法:
  void __fastcall TForm1FormCreate(TObject Sender)
  
  Application->OnMessage = MyOnMessage;
  
  这样就可以在程序中收到自定义的消息并作出相应处理。
  值得注意的是,使用Application->OnMessage并不能捕获非队列消息,它无法捕获使用SendMessage直接发送给窗口的消息,这是因为其不通过消息队列。但可以使用另一个发送消息的API函数——PostMessage,该函数发出的消息是队列消息。
  4 结束语
  利用多线程、消息机制和重叠I/O的API函数进行串口编程的方法,可实现串口通信的实时高效,为开发Windows系统下串口驱动程序提供了有益参考。&
  参考文献
  1 范逸之.江文贤.陈立元.C++ Builder与RS-232串行通信控制[M].清华大学出版社,2002
  2 黄军等.Delphi串口通信编程[M].人民邮电出版社,2001
3   张志明.李蓉艳.王磊.WIN32环境下串行通信编程技术研究[J].《计算机应用研究》 2002;9


推荐阅读
  • 第二章:Kafka基础入门与核心概念解析
    本章节主要介绍了Kafka的基本概念及其核心特性。Kafka是一种分布式消息发布和订阅系统,以其卓越的性能和高吞吐量而著称。最初,Kafka被设计用于LinkedIn的活动流和运营数据处理,旨在高效地管理和传输大规模的数据流。这些数据主要包括用户活动记录、系统日志和其他实时信息。通过深入解析Kafka的设计原理和应用场景,读者将能够更好地理解其在现代大数据架构中的重要地位。 ... [详细]
  • Node.js 配置文件管理方法详解与最佳实践
    本文详细介绍了 Node.js 中配置文件管理的方法与最佳实践,涵盖常见的配置文件格式及其优缺点,并提供了多种实用技巧和示例代码,帮助开发者高效地管理和维护项目配置,具有较高的参考价值。 ... [详细]
  • 在Python网络编程中,多线程技术的应用与优化是提升系统性能的关键。线程作为操作系统调度的基本单位,其主要功能是在进程内共享内存空间和资源,实现并行处理任务。当一个进程启动时,操作系统会为其分配内存空间,加载必要的资源和数据,并调度CPU进行执行。每个进程都拥有独立的地址空间,而线程则在此基础上进一步细化了任务的并行处理能力。通过合理设计和优化多线程程序,可以显著提高网络应用的响应速度和处理效率。 ... [详细]
  • 深入解析Spring Boot启动过程中Netty异步架构的工作原理与应用
    深入解析Spring Boot启动过程中Netty异步架构的工作原理与应用 ... [详细]
  • Java集合框架特性详解与开发实践笔记
    Java集合框架特性详解与开发实践笔记 ... [详细]
  • 【并发编程】全面解析 Java 内存模型,一篇文章带你彻底掌握
    本文深入解析了 Java 内存模型(JMM),从基础概念到高级特性进行全面讲解,帮助读者彻底掌握 JMM 的核心原理和应用技巧。通过详细分析内存可见性、原子性和有序性等问题,结合实际代码示例,使开发者能够更好地理解和优化多线程并发程序。 ... [详细]
  • 本书详细介绍了在最新Linux 4.0内核环境下进行Java与Linux设备驱动开发的全面指南。内容涵盖设备驱动的基本概念、开发环境的搭建、操作系统对设备驱动的影响以及具体开发步骤和技巧。通过丰富的实例和深入的技术解析,帮助读者掌握设备驱动开发的核心技术和最佳实践。 ... [详细]
  • 在前文探讨了Spring如何为特定的bean选择合适的通知器后,本文将进一步深入分析Spring AOP框架中代理对象的生成机制。具体而言,我们将详细解析如何通过代理技术将通知器(Advisor)中包含的通知(Advice)应用到目标bean上,以实现切面编程的核心功能。 ... [详细]
  • 本文详细介绍了如何安全地手动卸载Exchange Server 2003,以确保系统的稳定性和数据的完整性。根据微软官方支持文档(https://support.microsoft.com/kb833396/zh-cn),在进行卸载操作前,需要特别注意备份重要数据,并遵循一系列严格的步骤,以避免对现有网络环境造成不利影响。此外,文章还提供了详细的故障排除指南,帮助管理员在遇到问题时能够迅速解决,确保整个卸载过程顺利进行。 ... [详细]
  • 蜘蛛表格:全面解析与应用指南
    蜘蛛表格:全面解析与应用指南 ... [详细]
  • 本文探讨了利用Python编程语言开发自动化脚本来实现文件的全量和增量备份方法。通过详细分析不同备份策略的特点,文章介绍了如何使用Python标准库中的os和shutil模块来高效地管理和执行备份任务。此外,还提供了示例代码和最佳实践,帮助读者快速掌握自动化备份技术,确保数据的安全性和完整性。 ... [详细]
  • 本文深入解析了Elasticsearch写入与查询的底层机制。在数据写入过程中,首先会将数据暂存至内存缓冲区,在此阶段数据尚不可被搜索。同时,为了保证数据的持久性和可靠性,系统会将这些数据同步记录到事务日志(translog)中。当内存缓冲区接近满载时,系统会触发刷新操作,将缓冲区中的数据写入到磁盘上的段文件中,从而使其可被搜索。此外,文章还探讨了查询过程中涉及的索引分片、倒排索引等关键技术,为读者提供了全面的技术理解。 ... [详细]
  • 互斥锁(Mutual Exclusion,简称Mutex)是多线程编程中的一种同步机制,用于确保同一时间只有一个线程能够访问共享资源,从而避免数据竞争和不一致的问题。信号量(Semaphore)则是一种更为通用的同步工具,不仅能够控制对资源的访问,还能管理资源的数量。本文将深入探讨这两种机制的工作原理、应用场景以及它们在实际开发中的优缺点,帮助读者更好地理解和使用这些重要的并发控制手段。 ... [详细]
  • 面向对象编程的核心概念包括类与对象、封装技术、继承机制、多线程处理及线程同步、异常管理。类是现实世界对象的抽象表示,包含属性(特征)和方法(行为)。对象则是类的具体实例,用于实现抽象数据类型。通过封装技术,可以隐藏对象的内部细节,保护数据的安全性。继承机制允许子类继承父类的属性和方法,提高代码的复用性和可维护性。多线程处理和线程同步技术则用于实现并发操作,提升程序的执行效率。异常管理机制则确保程序在遇到错误时能够优雅地处理,提高系统的健壮性。 ... [详细]
  • 字节跳动深圳研发中心安全业务团队正在火热招募人才! ... [详细]
author-avatar
广佛笑嘻嘻_229
这个家伙很懒,什么也没留下!