通讯资料

来源:互联网 发布:大数据 mobi 编辑:程序博客网 时间:2024/04/30 22:24

 

 

<!-- /* Font Definitions */ @font-face{font-family:宋体;panose-1:2 1 6 0 3 1 1 1 1 1;mso-font-alt:SimSun;mso-font-charset:134;mso-generic-font-family:auto;mso-font-pitch:variable;mso-font-signature:3 135135232 16 0 262145 0;}@font-face{font-family:"/@宋体";panose-1:2 1 6 0 3 1 1 1 1 1;mso-font-charset:134;mso-generic-font-family:auto;mso-font-pitch:variable;mso-font-signature:3 135135232 16 0 262145 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal{mso-style-parent:"";margin:0cm;margin-bottom:.0001pt;text-align:justify;text-justify:inter-ideograph;mso-pagination:none;font-size:10.5pt;mso-bidi-font-size:12.0pt;font-family:"Times New Roman";mso-fareast-font-family:宋体;mso-font-kerning:1.0pt;}a:link, span.MsoHyperlink{color:blue;text-decoration:underline;text-underline:single;}a:visited, span.MsoHyperlinkFollowed{color:purple;text-decoration:underline;text-underline:single;} /* Page Definitions */ @page{mso-page-border-surround-header:no;mso-page-border-surround-footer:no;}@page Section1{size:595.3pt 841.9pt;margin:72.0pt 90.0pt 72.0pt 90.0pt;mso-header-margin:42.55pt;mso-footer-margin:49.6pt;mso-paper-source:0;layout-grid:15.6pt;}div.Section1{page:Section1;}-->

Java串口通信详解

2008/09/02 16:20

 

 

说到开源,恐怕很少有人不挑大 指称赞。学生通过开源代码学到了知识,程序员通过开源类库获得了别人的成功经验及能够按时完成手头的工程,商家通过开源软件赚到了钱……,总之是皆大欢 喜。然而开源软件或类库的首要缺点就是大多缺乏详细的说明文档和使用的例子,或者就是软件代码随便你用,就是文档,例子和后期服务收钱。这也难怪,毕竟就像某个著名NBA球员说的那样:我还要养家,所以千万美元以下的合同别找我谈,否则我宁可待业。是啊,支持开源的人也要养家,收点钱也不过分。要想既 不花钱又学到知识就只能借助网络和了,我只是想抛砖引玉,为开源事业做出点微薄共献,能为你的工程解决哪怕一个小问题,也就足够了。
      
虽然我的这个系列介绍的东西不 是什么Web框架,也不是什么开源服务器,但是我相信,作为一个程序员,什么样的问题都会遇到。有时候越是简单的问题反而越棘手;越是小的地方就越是找不到称手的家伙。只要你不是整天只与架构构件框架打交道的话,相信我所说的东西你一定会用到。


     
嵌入式系统或传感器网络的很多 应用和测试都需要通过PC机与嵌入式设备或传感器节点进行通信。其中,最常用的接口就是RS-232串口和并口(鉴于USB接口的复杂性以及不需要很大的 数据传输量,USB接口用在这里还是显得过于奢侈,况且目前除了SUN有一个支持USB的包之外,我还没有看到其他直接支持USBJava类库)。 SUNCommAPI分别提供了对常用的RS232串行端口和IEEE1284并行端口通讯的支持。RS-232-C(又称EIA RS-232-C,以下简称RS232)是在1970年由美国电子工业协会(EIA)联合贝尔系统、调制解调器厂家及计算机终端生产厂家共同制定的用于串行通讯的标准。RS232是一个全双工的通讯协议,它可以同时进行数据接收和发送的工作。
1.1 常见的Java串口包
目前,常见的Java串口包有SUN1998年发布的串口通信 APIcomm2.0.jarWindows下)、comm3.0.jarLinux/Solaris);IBM的串口通信API以及一个开源的实 现。鉴于在WindowsSUNAPI比较常用以及IBM的实现和SUN的在API层面都是一样的,那个开源的实现又不像两家大厂的产品那样让人放心,这里就只介绍SUN的串口通信APIWindows平台下的使用。
1.2 串口包的安装(Windows下)
SUN的网站下载javacomm20-win32.zip,包含的东西如下所示:



按照其使用说明 (Readme.html)的说法,要想使用串口包进行串口通信,除了设置好环境变量之外,还要将win32com.dll复制 到<JDK>/bin目录下;将comm.jar复制到<JDK>/lib;把javax.comm.properties也同 样拷贝到<JDK>/lib目录下。然而在真正运行使用串口包的时候,仅作这些是不够的。因为通常当运行“java MyApp”的时候,是由JRE下的虚拟机启动MyApp的。而我们只复制上述文件到JDK相应目录下,所以应用程序将会提示找不到串口。解决这个问题的方法很简单,我们只须将上面提到的文件放到JRE相应的目录下就可以了。
     
值得注意的是,在网络应用程序中使用串口API的时候,还会遇到其他更复杂问题。有兴趣的话,你可以查看CSDN社区中
关于网页上Appletjavacomm20读取客户端串口的问题的帖子。
2
     串口API概览
2.1      javax.comm.CommPort

这是用于描述一个被底层系 统支持的端口的抽象类。它包含一些高层的IO控制方法,这些方法对于所有不同的通讯端口来说是通用的。SerialPort ParallelPort都是它的子类,前者用于控制串行端口而后者用于控这并口,二者对于各自底层的物理端口都有不同的控制方法。这里我们只关心 SerialPort

2.2      javax.comm.CommPortIdentifier

这个类主要用于对串口进行管理和设置,是对串口进行访问控制的核心类。主要包括以下方法

l         确定是否有可用的通信端口

l         IO操作打开通信端口

l         决定端口的所有权

l         处理端口所有权的争用

l         管理端口所有权变化引发的事件(Event

2.3        javax.comm.SerialPort

这个类用于描述一个RS-232串行通信端口的底层接口,它定义了串口通信所需的最小功能集。通过它,用户可以直接对串口进行读、写及设置工作。

2.4 串口API实例

大段的文字怎么也不如一个小例子来的清晰,下面我们就一起看一下串口包自带的例子---SerialDemo中的一小段代码来加深对串口API核心类的使用方法的认识。

2.4.1   列举出本机所有可用串口

voidlistPortChoices() {

           CommPortIdentifier portId;

           Enumeration en = CommPortIdentifier.getPortIdentifiers();

           // iterate through the ports.

           while (en.hasMoreElements()) {

               portId = (CommPortIdentifier) en.nextElement();

               if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {

                   System.out.println(portId.getName());

               }

           }

           portChoice.select(parameters.getPortName());

       }

以上代码可以列举出当前系统所有可用的串口名称,我的机器上输出的结果是COM1COM3

2.4.2   串口参数的配置

串口一般有如下参数可以在该串口打开以前配置进行配置:

包括波特率,输入/输出流控制,数据位数,停止位和齐偶校验。

SerialPort sPort;

try {

           sPort.setSerialPortParams(BaudRate,Databits,Stopbits,Parity);

                    //设置输入/输出控制流

                    sPort.setFlowControlMode(FlowControlIn | FlowControlOut);

       } catch (UnsupportedCommOperationException e) {}

2.4.3   串口的读写

对串口读写之前需要先打开一个串口:

CommPortIdentifierportId = CommPortIdentifier.getPortIdentifier(PortName);

try {

           SerialPort sPort = (SerialPort) portId.open("串口所有者名称", 超时等待时间);

       } catch (PortInUseException e) {//如果端口被占用就抛出这个异常

           throw new SerialConnectionException(e.getMessage());

       }

//用于对串口写数据

OutputStream os =new BufferedOutputStream(sPort.getOutputStream());

os.write(intdata);

//用于从串口读数据

InputStream is =new BufferedInputStream(sPort.getInputStream());

int receivedData =is.read();

读出来的是int型,你可以把它转换成需要的其他类型。

这里要注意的是,由于Java语言没有无符号类型,即所有的类型都是带符号的,在由byteint的时候应该尤其注意。因为如果byte的最高位是1,则转成int类型时将用1来占位。这样, 原本是10000000byte类型的数变成int型就成了1111111110000000,这是很严重的问题,应该注意避免。

3    串口通信的通用模式及其问题

终于唠叨完我最讨厌的基础知识 了,下面开始我们本次的重点--串口应用的研究。由于向串口写数据很简单,所以这里我们只关注于从串口读数据的情况。通常,串口通信应用程序有两种模式, 一种是实现SerialPortEventListener接口,监听各种串口事件并作相应处理;另一种就是建立一个独立的接收线程专门负责数据的接收。由于这两种方法在某些情况下存在很严重的问题(至于什么问题这里先卖个关子J),所以我的实现是采用第三种方法来解决这个问题。

3.1 事件监听模型

现在我们来看看事件监听模型是如何运作的

l        首先需要在你的端口控制类(例如SManager)加上“implementsSerialPortEventListener”

l        在初始化时加入如下代码:

try {

           SerialPort sPort.addEventListener(SManager);

       } catch (TooManyListenersException e) {

           sPort.close();

            throw new SerialConnectionException("too many listenersadded");

       }

       sPort.notifyOnDataAvailable(true);

l        覆写public void serialEvent(SerialPortEvent e)方法,在其中对如下事件进行判断:

BI -通讯中断.

  CD -载波检测.

  CTS -清除发送.

  DATA_AVAILABLE-有数据到达.

  DSR -数据设备准备好.

  FE -帧错误.

  OE -溢位错误.

  OUTPUT_BUFFER_EMPTY-输出缓冲区已清空.

  PE -奇偶校验错.

RI - 振铃指示.

一般最常用的就是DATA_AVAILABLE--串口有数据到达事件。也就是说当串口有数据到达时,你可以在serialEvent中接收并处理所收到的数据。然而在我的实践中,遇到了一个十分严重的问题。

首先描述一下我的实验:我的应 用程序需要接收传感器节点从串口发回的查询数据,并将结果以图标的形式显示出来。串口设定的波特率是115200,川口每隔128毫秒返回一组数据(大约 是30字节左右),周期(即持续时间)为31秒。实测的时候在一个周期内应该返回4900多个字节,而用事件监听模型我最多只能收到不到1500字节,不 知道这些字节都跑哪里去了,也不清楚到底丢失的是那部分数据。值得注意的是,这是我将serialEvent()中所有处理代码都注掉,只剩下打印代码所 得的结果。数据丢失的如此严重是我所不能忍受的,于是我决定采用其他方法。

3.2 串口读数据的线程模型

这个模型顾名思义,就是将接收数据的操作写成一个线程的形式:

public voidstartReadingDataThread() {

       Thread readDataProcess = new Thread(new Runnable() {

           public void run() {

                           while (newData != -1) {

                   try {

                                         newData = is.read();

                       System.out.println(newData);

                                         //其他的处理过程

                                        ……….

                                  } catch (IOException ex) {

                       System.err.println(ex);

                       return;

                   }

                    }

             readDataProcess.start();

}

在我的应用程序中我将收到的数据打包放到一个缓存中然后启动另一个线程从缓存中获取并处理数据。两个线程以生产者消费者模式协同工作,数据的流向如下图所示:

这 样,我就圆满解决了丢数据问题。然而,没高兴多久我就又发现了一个同样严重的问题:虽然这回不再丢数据了,可是原本一个周期(31秒)之后,传感器节电已 经停止传送数据了,但我的串口线程依然在努力的执行读串口操作,在控制台也可以看见收到的数据仍在不断的打印。原来,由于传感器节点发送的数据过快,而我的接收线程处理不过来,所以InputStream就先把已到达却还没处理的字节缓存起来,于是就导致了明明传感器节点已经不再发数据了,而控制台却还能看见数据不断打印这一奇怪的现象。唯一值得庆幸的是最后收到数据确实是4900左右字节,没出现丢失现象。然而当处理完最后一个数据的时候已经快1分半钟 了,这个时间远远大于节点运行周期。这一延迟对于一个实时的显示系统来说简直是灾难!

后来我想,是不是由于两个线程 之间的同步和通信导致了数据接收缓慢呢?于是我在接收线程的代码中去掉了所有处理代码,仅保留打印收到数据的语句,结果依然如故。看来并不是线程间的通信阻碍了数据的接收速度,而是用线程模型导致了对于发送端数据发送速率过快的情况下的数据接收延迟。这里申明一点,就是对于数据发送速率不是如此快的情况下 前面者两种模型应该还是好用的,只是特殊情况还是应该特殊处理。

3.3 第三种方法

痛苦了许久(Boss天天催我L) 之后,偶然的机会,我听说TinyOS中(又是开源的)有一部分是和我的应用程序类似的串口通信部分,于是我下载了它的1.x版的Java代码部分,参考 了它的处理方法。解决问题的方法说穿了其实很简单,就是从根源入手。根源不就是接收线程导致的吗,那好,我就干脆取消接收线程和作为中介的共享缓存,而直接在处理线程中调用串口读数据的方法来解决问题(什么,为什么不把处理线程也一并取消?----都取消应用程序界面不就锁死了吗?所以必须保留)于是程序变成了这样:

public byte[]getPack(){

      while (true) {

                       // PacketLength为数据包长度

                   byte[] msgPack = new byte[PacketLength];

                   for(int i = 0; i < PacketLength; i++){

                       if( (newData = is.read()) != -1){

                           msgPack[i] = (byte) newData;

                           System.out.println(msgPack[i]);

                       }

                   }

                   return msgPack;

                           }

}

在处理线程中调用这个方法返回 所需要的数据序列并处理之,这样不但没有丢失数据的现象行出现,也没有数据接收延迟了。这里唯一需要注意的就是当串口停止发送数据或没有数据的时候 is.read()一直都返回-1,如果一旦在开始接收数据的时候发现-1就不要理它,继续接收,直到收到真正的数据为止。


4    结束语

本文介绍了串口通信的基本知识,以及常用的几种模式。通过实践,提出了一些问题,并在最后加以解决。值得注意的是对于第一种方法,我曾将传感器发送的时间由128毫秒增加到512毫 秒,仍然有很严重的数据丢失现象发生,所以如果你的应用程序需要很精密的结果,传输数据的速率又很快的话,就最好不要用第一种方法。对于第二种方法,由于是线程导致的问题,所以对于不同的机器应该会有不同的表现,对于那些处理多线程比较好的机器来说,应该会好一些。但是我的机器是Inter奔四3.0双核CPU+512DDR内存,这样都延迟这么厉害,还得多强的CPU才行啊?所以对于数据量比较大的传输来说,还是用第三种方法吧。不过这个世界问题是很多的,而且未知的问题比已知的问题多的多,说不定还有什么其他问题存在,欢迎你通过下面的联系方式和我一起研究。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Java串口通信编程2

2008-09-12 12:20

 

 

1.2.2打开串口

示例代码如下:

代码:

try{

    // ポートのオープン

    port = (SerialPort)portID.open("portApp", 5000);

}catch(PortInUseException ex){

    ex.printStackTrace();

}

5000(毫秒)是超时时间。

1.2.3设置串行端口通讯参数

设置串口传输的波特率、数据位、停止位、奇偶校验等参数。

示例代码如下:

代码:

try {

    // 通信条件の設定

    // 通信速度 9600 baud

    // データビット 8bit

    // ストップビット 1bit

    // パリティ なし

    // フローコントロールの設定

    // 無制御を使用

    port.setSerialPortParams(9600,

        SerialPort.DATABITS_8,

        SerialPort.STOPBITS_1,

        SerialPort.PARITY_NONE);

    port.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);

} catch (UnsupportedCommOperationException ex) {

    ex.printStackTrace();

    System.exit(1);

}

1.2.4获取输入(出)流

示例代码如下:

代码:

//受信バッファ

protected BufferedReader comReader;

//送信バッファ

protected BufferedOutputStream comWriter;

try {

    // RS-232C 入力用の Reader を生成

    comReader = new BufferedReader(

            new InputStreamReader(port.getInputStream()));

    // RS-232C 出力用の Writer を生成

    comWriter=

new BufferedOutputStream(port.getOutputStream());

   

} catch (IOException ex){

    ex.printStackTrace();

}

 

 

 

 

 

原创】Windows平台如何配置Java实现串口通信

2008-07-15 19:02

 

本文将讨论如何在windows平台使用Java Communications API访问串口。

Sun提供了一个API扩展,用于并行和串行端口,特别是在SolarisWindows环境下。这个API可以在http://java.sun.com/products/javacomm找到。当然,在http://www.rxtx.org上也有包括Linux平台的第三方APIs和开源实现。

通过这个API,你能够为每一个端口创建对象(枚举),打开端口,在Java对象间解析内容,实现同步和异步的通信。

Windows环境下安装Java Communication API

为了使Java Communications API能够正确的与Windows系统交互,需要几个小的技巧。下面是三个很重要的文件,你可以从Sun的网站上下载得到

comm.jar

win32com.dll

javax.comm.properties

对于JVM来说,要正确的识别串口,将这几个文件放在系统中合适的位置使很重要的。

comm..jar应该放在以下目录中

%JAVA_HOME%/lib

%JAVA_HOME%/jre/lib/ext

win32com.dll应该放在以下目录中

%windir%system32

javax.com.properties应该放在以下目录中

%JAVA_HOME%/lib

%JAVA_HOME%/jre/lib

你可以通过编译和运行Sun的例程来验证串口是否可以使用了。

JBuilder中安装安装Java Communication API

(以下在JBuilder 2006中测试通过)

如果你使用JBuilder,那么还需要为JBuilder配置API

一般来说,根据你的JBuilder配置,你也许需要将win32com.dlljavax.com.properties安装到相应的目录中,可以参照上述的目录。例如,如果你使用JBuilder附带的JVM的话,你也许需要将win32com.dlljavax.com.properties放到C:/Borland/JBuilder2006/jdk1.5的相应位置。

接下来,需要安装comm.jar包,这相对比较简单,访问Project菜单

 

打开Properties对话框

 

选择Add… comm.jar加入库中即可

 

到此,系统配置完成,可以使用串口了。

** 以上内容,请参考 http://dn.codegear.com/ 网站

 

________________________________________________

java串口通信(利用PDU方式进行短信群发)

2009-05-0316:522008-06-17 11:41相关的GSM AT指令

 

  我现在以实例来说明这些指令的使用方法:

 

  先用手机数据线将手机连接到电脑串口,并将串口的波特率设置为19200,可以开始了。

 

  1、首先测试你的连接及手机是否支持AT指令,请在你的串口调试程序中输入:

 

  AT<回车>

 

  屏幕上返回"OK"表明计算机与手机连接正常,那样我们就可以进行其它的AT指令测试了

 

  2、设置短信发送格式

 

  AT+CMGF=1<回车>

 

  屏幕上返回"OK"表明现在短信的发送方式为PDU方式,如果是设置为TEXT方式,则,AT+CMGF=0<回车>

 

  3 发送短信

 

  发送内容及手要号仍旧同上面在编码中的一样,编码后,得到要发送的数据如下

 

0891683108705505F011000D91683117352446F2000800124F60597D002C00480065006C006C006F0021

 

  我们用如下指令来发送

 

  AT+CMGS=33<回车>

 

  如果返回"",就把上面编码数据输入,并以CTRL+Z结尾,稍等一下,你就可以看到返回OK啦。

 

  说明一下,为什么AT+CMGS=33呢,是这样得来的:

 

11000D91683117352446F2000800124F60597D002C00480065006C006C006F0021

 

  这一段字符串的长度除以2得到的结果,上面的字符串,短信中心号加上短信内容得到的,怎么得到的,请回顾一下解码部份

 

  在我们前面的讨论中,一条完整的短信发送,只要执行三条AT指令,ATAT+CMGS=?、AT+CMGS=?就可以了。由于篇幅,我只能在这里提到这么多,大家要是想了解更多,可以向各手机厂商索取AT指令白皮书,里面很详细的。

 

原创粉丝点击