Win32串口编程

Win32串口编程  
   
  一、基本知识    
   
       Win32下串口通信与16位串口通信有很大的区别。在Win32下,可以使用两种编程方式实现串口通信,其一是调用的Windows的API函数,其二 是使用ActiveX控件。使用API   调用,可以清楚地掌握串口通信的机制,熟悉各种配置和自由灵活采用不同的流控进行串口通信。下面介绍串口操作的基本知识。    
   
    打开串口:使用CreateFile()函数,可以打开串口。有两种方法可以打开串口,一种是同步方式(NonOverlapped),另外一种异步方式(Overlapped)。使用Overlapped打开时,适当的方法是:    
   
      HANDLE   hComm;  
      hComm   =   CreateFile(   gszPort,    
      GENERIC_READ   |   GENERIC_WRITE,    
      0,    
      0,    
      OPEN_EXISTING,  
      FILE_FLAG_OVERLAPPED,  
      0);  
      if   (hComm   ==   INVALID_HANDLE_VALUE)  
      //   error   opening   port;   abort  
    配置串口:    
   
    1.DCB配置    
   
       DCB(Device   Control   Block)结构定义了串口通信设备的控制设置。许多重要设置都是在DCB结构中设置的,有三种方式可以初始化DCB。    
   
    (1)通过GetCommState()函数得DCB的初始值,其使用方式为:    
   
  DCB   dcb   =   {0};  
  if   (!GetCommState(hComm,   &dcb))  
  //   Error   getting   current   DCB   settings  
  else  
  //   DCB   is   ready   for   use.  
   
    (2)用BuildCommDCB()函数初始化DCB结构,该函数填充   DCB的波特率、奇偶校验类型、数据位、停止位。对于流控成员函数设置了缺省值。其用法是:    
   
  DCB   dcb;  
  FillMemory(&dcb,   sizeof(dcb),   0);  
  dcb.DCBlength   =   sizeof(dcb);  
  if   (!BuildCommDCB(“9600,n,8,1",   &dcb))   {    
  //   Couldn't   build   the   DCB.   Usually   a   problem  
  //   with   the   communications   specification   string.  
  return   FALSE;  
  }  
  else  
  //   DCB   is   ready   for   use.  
   
    (3)用SetCommState()函数手动设置DCB初值。用法如下:    
   
  DCB   dcb;  
  FillMemory(&dcb,   sizeof(dcb),   0);  
  if   (!GetCommState(hComm,   &dcb))   //   get   current   DCB  
  //   Error   in   GetCommState  
  return   FALSE;  
  //   Update   DCB   rate.  
  dcb.BaudRate   =   CBR_9600   ;  
  //   Set   new   state.  
  if   (!SetCommState(hComm,   &dcb))  
  //   Error   in   SetCommState.    
      Possibly   a   problem   with   the   communications    
  //   port   handle   or   a   problem   with   the   DCB   structure   itself.  
   
    手动设置DCB值时,DCB的结构的各成员的含义,可以参看MSDN帮助。    
   
       2.流控设置    
   
    硬件流控:串口通信中的硬件流控有两种,DTE/DSR方式和RTS/CTS方式,这与DCB结构的初始化有关系,DCB结构中的 OutxCtsFlow、   fOutxDsrFlow、fDsrSensitivity、fRtsControl、fDtrControl几个成员的初始值很关键,不同的值代表不同 流控,也可以自己设置流控,但建议采用标准流行的流控方式。采用硬件流控时,DTE、DSR、RTS、CTS的逻辑位直接影响到数据的读写及收发数据的缓 冲区控制。    
   
       软件流控:串口通信中采用特殊字符XON和XOFF作为控制串口数据的收发。与此相关的DCB成员是:fOut、fInX、XoffChar、XonChar、   XoffLim和XonLim。具体含义参见MSDN帮助。    
   
       串口读写操作:串口读写有两种方式:同步方式(NonOverlapped)和异步方式(Overlapped)。同步方式是指必须完成了读写操作,函数 才返回,这可能造成程序死掉,因为如果在读写时发生了错误,永远不返回就会出错,可能线程将永远等待在那儿。而异步方式则灵活得多,一旦读写不成功,就将 读写挂起,函数直接返回,可以通过GetLastError函数得知读写未成功的原因,所以常常采用异步方式操作。    
   
       读操作:ReadFile()函数用于完成读操作。异步方式的读操作为:    
   
      DWORD   dwRead;  
      BOOL   fWaitingOnRead   =   FALSE;  
      OVERLAPPED   osReader   =   {0};  
      //   Create   the   overlapped   event.   Must   be   closed   before   exiting  
      //   to   avoid   a   handle   leak.  
      osReader.hEvent   =   CreateEvent  
      (NULL,   TRUE,   FALSE,   NULL);  
      if   (osReader.hEvent   ==   NULL)  
      //   Error   creating   overlapped   event;   abort.  
      if   (!fWaitingOnRead)   {  
      //   Issue   read   operation.  
      if   (!ReadFile(hComm,   lpBuf,   READ_BUF_SIZE,  
        &dwRead,   &osReader))   {  
      if   (GetLastError()   !=   ERROR_IO_PENDING)  
          //   read   not   delayed?  
      //   Error   in   communications;   report   it.  
      else  
      fWaitingOnRead   =   TRUE;  
      }  
      else   {    
      //   read   completed   immediately  
      HandleASuccessfulRead(lpBuf,   dwRead);  
      }  
      }  
   
       如果读操作被挂起,可以调用WaitForSingleObject()函数或WaitForMuntilpleObjects()函数等待读操作完成或者超时发生,再调用   GetOverlappedResult()得到想要的信息。    
   
       写操作:与读操作相似,故不详述,调用的API函数是:   WriteFile函数。    
   
       串口状态:    
   
    (1)通信事件:用SetCommMask()函数设置想要得到的通信事件的掩码,再调用WaitCommEvent()函数检测通信事件的发生。可 设置的通信事件标志(即SetCommMask()函数所设置的掩码)可以有EV_BREAK、EV_CTS、EV_DSR、   EV_ERR、EV_RING、EV_RLSD、EV_RXCHAR、EV_RXFLAG、EV_TXEMPTY。    
   
       注意:1对于EV_RING标志的设置,WIN95是不会返回EV_RING事件的,因为WIN95不检测该事件。2设置EV_RXCHAR,可以检测到 字符到达,但是在绑定此事件和ReadFile()函数一起读取串口接收数据时,可能会出现错误,造成少读字节数,具体原因查看MSDN帮助。可以采用循 环读的办法,另外一个比较好的解决办法是调用ClearCommError()函数,确定在一次读操作中在缓冲区中等待被读的字节数。    
   
    (2)错误处理和通信状态:在串口通信中,可能会产生很多的错误,使用ClearCommError()函数可以检测错误并且清除错误条件。    
   
       (3)Modem状态:用SetcommMask()可以包含很多事件标志,但是这些事件标志只指示在串口线路上的电压变化情况。而调用   GetCommModemStatus()函数可以获得线路上真正的电压状态。    
   
       扩展函数:如果应用程序想用自己的流控,可以使用   EscapeCommFunction()函数设置DTR和RTS线路的电平。    
   
       通信超时:在通信中,超时是个很重要的考虑因素,因为如果在数据接收过程中由于某种原因突然中断或停止,如果不采取超时控制机制,将会使得I/O线程被挂 起或无限阻塞。串口通信中的超时设置分为两步,首先设置   COMMTIMEOUTS结构的五个变量,然后调用SetcommTimeouts()设置超时值。对于使用异步方式读写的操作,如果操作挂起后,异步成 功完成了读写,WaitForSingleObject()或   WaitForMultipleObjects()函数将返回WAIT_OBJECT_0,GetOverlappedResult()返回TRUE。其 实还可以用GetCommTimeouts()得到系统初始值。    
   
       关闭串口:程序结束或需要释放串口资源时,应该正确关闭串口,关闭串口比较简单,使用API调用CloseHandle()关闭串口的句柄就可以了。    
   
    调用方法为:CloseHandle(hComm);    
   
       但是值得注意的是在关闭串口之前必须保证读写串口线程已经退出,否则会引起误操作,一般采用的办法是使用事件驱动机制,启动一事件,通知串口读写线程强制退出,在线程退出之前,通知主线程可以关闭串口。    
   
  二、实现  
    1.程序设计思路    
       对于不同的应用程序,虽然界面不同,但是如果采用串口与主机之间的通信,对串口的处理方式大致相似,无非就是通过串口收发数据,对于通过串口接收到的数 据,交给上层软件处理显示,对于上层要发给串口的数据,进行转发。但在实际编程中,由于采用的通信方式和流控不同,串口设置也不同,这就涉及到   DCB的初始化问题和读写串口等细节问题。串口通信应用程序设计的总体思路(即操作过程)是:首先,确定要打开的串口名、波特率、奇偶校验方式、数据位、 停止位,传递给CreateFile()函数打开特定串口;其次,为了保护系统对串口的初始设置,调用   GetCommTimeouts()得到串口的原始超时设置;然后,初始化DCB对象,调用SetCommState()   设置DCB,调用SetCommTimeouts()设置串口超时控制;再次,调用SetupComm()设置串口接收发送数据的缓冲区大小,串口的设置 就基本完成,之后就可以启动读写线程了。    
   
    一般来说,串口的读写由串口读写线程完成,这样可以避免读写阻塞时主程序死锁。对于全双工的串口读写,应该分别开启读线程和写线程;对于半双工和单工 的,建议只需开启一个线程即可。在线程中,按照预定好的通信握手方式,正确检测串口状态,读取发送串口数据。    
   
    2.实现细节    
   
    在半双工的情况下,首先完成必要的串口配置,成功打开串口、DCB设置、超时设置;然后开启线程,如:   CwinThread   hSerialThread   =   (CWinThread*)   AfxBeginThread(SerialOperation,hWnd,THREAD_PRIORITY_NORMAL);   其中开启之线程为SerialOperation,优先级为普通。    
   
       全双工情况下的串口编程,与单工差不多,区别仅仅在于启动双线程,分别为读线程和写线程,读线程根据不同的事件或消息,通过不断查询串口所收到的有效数据,完成读操作;写线程通过接收主线程的发送数据事件和要发送的数据,向串口发送。
内容概要:本文系统介绍了算术优化算法(AOA)的基本原理、核心思想及Python实现方法,并通过图像分割的实际案例展示了其应用价值。AOA是一种基于种群的元启发式算法,其核心思想来源于四则运算,利用乘除运算进行全局勘探,加减运算进行局部开发,通过数学优化器加速函数(MOA)和数学优化概率(MOP)动态控制搜索过程,在全局探索与局部开发之间实现平衡。文章详细解析了算法的初始化、勘探与开发阶段的更新策略,并提供了完整的Python代码实现,结合Rastrigin函数进行测试验证。进一步地,以Flask框架搭建前后端分离系统,将AOA应用于图像分割任务,展示了其在实际工程中的可行性与高效性。最后,通过收敛速度、寻优精度等指标评估算法性能,并提出自适应参数调整、模型优化和并行计算等改进策略。; 适合人群:具备一定Python编程基础和优化算法基础知识的高校学生、科研人员及工程技术人员,尤其适合从事人工智能、图像处理、智能优化等领域的从业者;; 使用场景及目标:①理解元启发式算法的设计思想与实现机制;②掌握AOA在函数优化、图像分割等实际问题中的建模与求解方法;③学习如何将优化算法集成到Web系统中实现工程化应用;④为算法性能评估与改进提供实践参考; 阅读建议:建议读者结合代码逐行调试,深入理解算法流程中MOA与MOP的作用机制,尝试在不同测试函数上运行算法以观察性能差异,并可进一步扩展图像分割模块,引入更复杂的预处理或后处理技术以提升分割效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值