Java串口通信编程教程hao.docx

上传人:b****7 文档编号:11034859 上传时间:2023-02-24 格式:DOCX 页数:27 大小:31.92KB
下载 相关 举报
Java串口通信编程教程hao.docx_第1页
第1页 / 共27页
Java串口通信编程教程hao.docx_第2页
第2页 / 共27页
Java串口通信编程教程hao.docx_第3页
第3页 / 共27页
Java串口通信编程教程hao.docx_第4页
第4页 / 共27页
Java串口通信编程教程hao.docx_第5页
第5页 / 共27页
点击查看更多>>
下载资源
资源描述

Java串口通信编程教程hao.docx

《Java串口通信编程教程hao.docx》由会员分享,可在线阅读,更多相关《Java串口通信编程教程hao.docx(27页珍藏版)》请在冰豆网上搜索。

Java串口通信编程教程hao.docx

Java串口通信编程教程hao

Win32 串口编程

(一)(2009-08-1616:

28:

13)

标签:

串口 重叠i/o 

分类:

Windows

翻译自:

ms-help:

//MS.MSDNQTR.v80.chs/MS.MSDN.v80/MS.WIN32COM.v10.en/dnfiles/html/msdn_serial.htm

老外写的文章,虽比较全面,但很啰嗦,不如看各个函数的文档来得快。

为方便以后查阅,列出本文涉及的主要函数如下:

CreateFile、ReadFile、WriteFile、GetOverlappedResult、WaitForSingleObject

SetCommMask、WaitCommEvent

ClearCommError、GetCommModemStatus、EscapeCommFunction

GetCommState、BuildCommDCB、SetCommState、SetCommTimeouts

此外,新浪的博客系统限制文章最大长度为40000字节,只好把这篇文章分成几部分了。

0简介

本文仅关注在WindowsNT和95间兼容的API。

Windows95支持TelephonyAPI(TAPI),但WindowsNT3.x不支持TAPI,所以本文不讨论它。

本文的示例程序MTTTY(MultithreadedTTY)使用了三个线程:

一个进行内存管理的界面线程;控制所有写入操作的写入者线程;读取数据和处理端口状态改变的读取/状态线程。

示例采用了一些不同的堆来进行内存管理;还大量使用了同步方法来进行线程间通信。

1打开端口

使用CreateFile函数打开端口。

打开端口时有两种方法:

重叠的和非重叠的。

下列代码片段以重叠方式打开端口:

 

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

 

 Win32 串口编程

(二)(2009-08-1616:

39:

32)

标签:

串口 重叠i/o it 

分类:

Windows

3串口状态

有两种获取通信端口状态的方法。

第一种方法是设置事件掩码,当指定事件发生时应用程序会收到通知。

SetCommMask函数用于设置事件掩码,WaitCommEvent用于等待指定的事件发生。

它们与16位Windows中的SetCommEventMask和EnableCommNotification类似,只是它们不发送WM_COMMNOTIFY消息。

第二种方法是不时地调用另一些状态函数来获取通信端口的状态。

当然,轮询是低效的,不建议使用。

3.1通信事件

通信事件在使用通信端口时可能随时发生。

接收通信事件需要两个步骤:

用SetCommMask设定需要接收通知的事件

用WaitCommEvent提交状态检查请求,请求可以是重叠的或者非重叠的,与读写操作一样。

下面是使用SetCommMask的示例:

DWORD dwStoredFlags;

dwStoredFlags = EV_BREAK | EV_CTS   | EV_DSR | EV_ERR | EV_RING |               

               EV_RLSD | EV_RXCHAR | EV_RXFLAG | EV_TXEMPTY ;

if (!

SetCommMask(hComm, dwStoredFlags))   

  // error setting communications mask

 

 

下表描述了每种事件类型。

 

事件标志

描述

EV_BREAK

检测到输入中的break

EV_CTS

CTS(ClearToSend)信号状态改变。

要取得CTS线路状态,应使用GetCommModemStatus函数。

EV_DSR

DSR(DataSetReady)信号状态改变。

要取得DSR线路状态,应使用GetCommModemStatus函数。

EV_ERR

某线路状态错误发生。

线路状态错误包括CE_FRAME、CE_OVERRUN和CE_RXPARITY。

要取得具体错误种类,需调用ClearCommError函数。

EV_RING

检测到振铃指示

EV_RLSD

RLSD(ReceiveLineSignalDetect)信号状态改变。

要取得RLSD线路状态,需调用GetCommModemStatus函数。

注意,RLSD通常被称作CD(carrierdetect)。

EV_RXCHAR

接收到一个字符并且已放入输入缓冲区。

请参考下面的“警告”节对此标志的详细讨论。

EV_RXFLAG

接收到一个事件字符并且已放入输入缓冲区。

事件字符由下文讨论的DCB结构EvtChar字段指定。

下面的“警告”节也讨论了这个标志。

EV_TXEMPTY

输出缓冲区中最后一个字符被发送到串口设备了。

如果使用硬件缓冲区,此标志仅表示所有数据已经发送到硬件了。

如果不与设备驱动交互,是无法确定硬件缓冲区空的。

 

指定事件掩码后,使用WaitCommEvent函数检测事件发生。

如果以非重叠方式打开端口,则WaitCommEvent不需要OVERLAPPED结构体,函数阻塞调用线程直到某事件发生。

如果没有事件发生,调用线程将无限阻塞。

下面的代码片段展示了如何在以非重叠方式打开的端口上等待EV_RING事件。

   DWORD dwCommEvent;

   if (!

SetCommMask(hComm, EV_RING))      

       // Error setting communications mask      

      return FALSE;

   if (!

WaitCommEvent(hComm, &dwCommEvent, NULL))

      // An error occurred waiting for the event.

       return FALSE;

  else

      // Event has occurred.

      return TRUE;

 

 

 

 

如果没有事件发生,上面的代码将无限阻塞调用线程。

解决方法是以重叠方式打开端口,用下面的方式等待状态事件:

 

  #define STATUS_CHECK_TIMEOUT      500   // Milliseconds

   DWORD      dwRes;

   DWORD      dwCommEvent;

   DWORD      dwStoredFlags;

   BOOL      fWaitingOnStat = FALSE;

   OVERLAPPED osStatus = {0};

   dwStoredFlags = EV_BREAK | EV_CTS | EV_DSR | EV_ERR | EV_RING |\

                  EV_RLSD | EV_RXCHAR | EV_RXFLAG | EV_TXEMPTY ;

   if (!

SetCommMask(comHandle, dwStoredFlags))

      // error setting communications mask; abort

      return 0;

   osStatus.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

   if (osStatus.hEvent == NULL)

      // error creating event; abort

      return 0;

   for ( ; ; ) {

      // Issue a status event check if one hasn't been issued already.

      if (!

fWaitingOnStat) {

         if (!

WaitCommEvent(hComm, &dwCommEvent, &osStatus)) {

            if (GetLastError() == ERROR_IO_PENDING)

               bWaitingOnStatusHandle = TRUE;

            else

               // error in WaitCommEvent; abort

               break;

         }

         else

            // WaitCommEvent returned immediately.

            // Deal with status event as appropriate.

            ReportStatusEvent(dwCommEvent); 

      }

      // Check on overlapped operation.

      if (fWaitingOnStat) {

         // Wait a little while for an event to occur.

         dwRes = WaitForSingleObject(osStatus.hEvent, STATUS_CHECK_TIMEOUT);

         switch(dwRes)

         {

             // Event occurred.

             case WAIT_OBJECT_0:

 

                 if (!

GetOverlappedResult(hComm, &osStatus, &dwOvRes, FALSE))

                    // An error occurred in the overlapped operation;

                    // call GetLastError to find out what it was

                    // and abort if it is fatal.

                 else

                    // Status event is stored in the event flag

                    // specified in the original WaitCommEvent call.

                    // Deal with the status event as appropriate.

                    ReportStatusEvent(dwCommEvent);

                 // Set fWaitingOnStat flag to indicate that a new

                 // WaitCommEvent is to be issued.

                 fWaitingOnStat = FALSE;

                 break;

             case WAIT_TIMEOUT:

                 // Operation isn't complete yet. fWaitingOnStatusHandle flag 

                 // isn't changed since I'll loop back around and I don't want

                 // to issue another WaitCommEvent until the first one finishes.

                 //

                 // This is a good time to do some background work.

                DoBackgroundWork();

                 break;                       

             default:

                 // Error in the WaitForSingleObject; abort

                 // This indicates a problem with the OVERLAPPED structure's

                 // event handle.

                CloseHandle(osStatus.hEvent);

                return 0;

         }

      }

   }

   CloseHandle(osStatus.hEvent);

上面的代码片段与重叠读取操作的代码非常相似。

实际上,MTTTY使用WaitForMultipleObjects在同一个线程中等待读取完成或者状态改变事件发生。

SetCommMask和WaitCommEvent有两种很有意思的边际效应。

第一,如果以非重叠方式打开通信端口,WaitCommEvent将阻塞直到某事件发生。

如果其他线程调用SetCommMask设置新的事件掩码,则线程将阻塞在SetCommMask调用上,原因是第一个线程的WaitCommEvent调用仍在执行中。

SetCommMask将一直阻塞调用线程,直到第一个线程的WaitCommEvent调用返回。

这种边际效应对于以非重叠方式打开的端口是通用的。

如果某线程阻塞在任何通信函数上,则第二个线程对任何通信函数的调用都将阻塞,直到第一个线程的函数调用返回。

第二种边际效应是关于以重叠方式打开的端口的。

如果使用SetCommMask设置新的事件掩码,则未决的WaitCommEvent调用将成功完成,导致调用完成的事件掩码将是NULL。

 

 

 

 

去掉参数中的FILE_FLAG_OVERLAPPED就是非重叠操作方式了。

用CreateFile打开通信端口时,有下列限制:

fdwShareMode必须是0。

通信端口不能像文件那样被共享。

要共享通信端口,需要使用句柄继承或者复制操作。

fdwCreate必须指定OPEN_EXISTING标志。

hTemplateFile参数必须是NULL。

端口名通常是COM1、COM2、COM3和COM4。

Win32API不提供确定系统中有哪些端口可用的机制。

WindowsNT和Windows95跟踪系统已安装端口的方法是不同的,所以不太可能提供兼容的确定可用端口的方法。

某些系统可能有多于4个端口,而传统的通信端口最大个数是4。

硬件厂商和串口驱动编写者可以自由地为端口命名。

所以,程序最好可以让用户指定要使用的端口名字。

如果端口不存在,则试图打开端口时会返回ERROR_FILE_NOT_FOUND错误,这时应该提示用户端口不可用。

2读写操作

通信端口的读写操作与文件I/O操作非常相似,它们使用同样的函数。

Win32的I/O操作可分为两种:

重叠(overlapped)的和非重叠的(nonoverlapped)。

平台SDK文档分别使用异步(asynchronous)和同步(synchronous)来表示这两种I/O方式。

很多开发者都熟悉非重叠I/O,因为它就是传统的I/O方式:

函数返回时,所请求的操作已经完成。

然而在重叠I/O的情况下,系统则可能在操作还没有完成的情形下立即返回,随后才通知调用者操作完成。

程序可以在发起I/O请求和请求被完成之间进行一些后台工作。

2.1非重叠I/O

非重叠I/O的工作方式很简单:

I/O操作进行时,调用线程被阻塞;操作完成后,函数返回,调用线程可以继续执行。

在多线程应用中,这种I/O方式很有用:

一个线程阻塞在某I/O操作上时,其他线程可以继续工作。

应用程序应该保证对端口的串行访问。

某个线程阻塞在等待某I/O操作上时,其他线程后续的通信API调用也都将阻塞。

比如说,一个线程在等待ReadFile调用返回时,另一个线程的WriteFile函数调用将阻塞。

在选择使用非重叠还是重叠方式时,可移植性是要考虑的因素之一。

有时候重叠操作并不是好的选择,因为很多操作系统不支持它;然而很多操作系统都支持某种形式的多线程。

所以从兼容性方面考虑,多线程非重叠I/O可能是最好的选择。

2.2重叠I/O

重叠I/O不像非重叠I/O那样简单易懂,但却灵活高效。

使用重叠方式打开的端口允许多个线程同时进行I/O操作,并且在操作进行期间可以进行其他的工作。

此外,重叠操作的行为方式还允许单个线程提交多个不同的请求,然后在操作进行期间进行其他后台工作。

在单线程和多线程应用中,都必须在提交I/O请求和处理操作结果间进行一些同步操作。

线程可能需要在操作结果可用前阻塞;当然也可以进行其他工作。

如果没有其他需要进行的工作,则重叠I/O的优点是更好的用户响应性能。

MTTTY使用了重叠I/O。

它创建用于读取数据和监测端口状态的线程,并且还定时进行一些后台工作;此外它还另外创建一个线程用于写入数据。

重叠I/O操作分为两个部分:

创建I/O操作和检测操作完成。

创建I/O操作涉及到建立OVERLAPPED结构体、创建用于同步的手动复位事件、调用恰当的函数(ReadFile或者WriteFile)。

I/O操作可能立即完成,也可能不能立即完成,不能认为一个重叠I/O操作请求总是生成一个重叠操作。

如果操作立即完成,程序应该可以继续进行通常的处理。

检测操作完成涉及到等待事件句柄、检查操作完成结果、处理数据。

与重叠I/O相关的工作更多的原因是有更多的失败点。

非重叠操作中,简单地通过函数返回值判断操作是否失败;而重叠操作中,则可能在创建操作请求时失败,或者操作阻塞期间失败,也可能是操作超时,或者是等待操作完成信号超时。

2.2.1读操作

下面的代码片段展示了提交重叠的读操作请求的方法。

注意,如果ReadFile返回TRUE,调用了一个函数处理数据。

代码定义了fWaitingOnRead标志,它表示是否有重叠的读取操作存在,用于阻止在一个操作进行中时提交另一个读取操作请求。

 

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);

    }

}

 

OVERLAPPED结构体的事件句柄被传递给WaitForSingleObject以等待事件授信,操作完成。

注意,事件受信表示操作完成,而不是操作成功完成。

应该用GetOverlappedResult来取得操作结果,它返回TRUE表示操作成功完成;FALSE表示有错误发生,用GetLastError可以取得具体的错误码。

也可以用GetOverlappedResult来检测操作完成:

GetOverlappedResult返回FALSE,GetLastError返回ERROR_IO_INCOMPLETE表示操作进行中。

如果对bWait参数传入TRUE,则效果就是重叠操作变成了非重叠的,直到操作完成,函数才返回。

下面的代码片段展示了一种检测重叠读取操作完成的方法。

注意fWaitingOnRead标志的使用,它是检测代码的控制入口,只有在某操作进行中时,才应该调用检测代码。

#define READ_TIMEOUT      500      // milliseconds

DWORD dwRes;

if (fWaitingOnRead) {

   dwRes = WaitForSingleObject(osReader.hEvent, READ_TIMEOUT);

   switch(dwRes)

   {

      // Read completed.

      case WAIT_OBJECT_0:

          if (!

GetOverlappedResult(hComm, &osReader, &dwRead, FALSE))

             // Error in communications; report it.

          else

             // Read completed successfully.

             HandleASuccessfulRead(lpBuf, dwRead);

          //  Reset flag so that another opertion can be issued.

          fWaitingOnRead = FALSE;

          break;

      case WAIT_TIMEOUT:

          // Operation isn't complete yet. fWaiti

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > PPT模板 > 简洁抽象

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1