Linux系统下运用开源RXTX库实现JAVA串口通讯
时间:2006-06-23 来源:leo7766
leo wang
QQ 21575479 欢迎聊聊
web1 http://leo7766.cublog.cn
web2 http://shop.paipai.com/645582715
概述
一个嵌入式系统通常需要通过串口与其主控系统进行全双工通讯,譬如我们的环境在线监测系统下的各类监测仪器如流量计,PH计,电机状态检测仪器就需要定时的接受控制系统发送来的查询和控制信息,并将执行结果或查询结果发送回控制系统。
1.java的串口通讯扩展包javax.comm
Sun的J2SE中并没有直接提供任何一种串行通讯协议的开发包,而是以独立的jar包形式发布在java.sun.com网站上----即comm.jar,称之为Javatm Communications API,它是J2SE的标准扩展。comm.jar并不是最近才有,早在1998年时,sun就已经发布了这个开发包。comm.jar分别提供了对常用的RS232串行端口和IEEE1284并行端口通讯的支持。目前sun发布的comm.jar只有Windows和Solaris平台两个版本,如果需要Linux平台下的,可以在http://users.frii.com/jarvi/rxtx/index.html找到。
所有的comm API位于javax.comm包下面。其中比较重要的类和接口如下,
javax.comm.CommDriver
javax.comm.CommPort
javax.comm.ParallelPort
javax.comm.SerialPort
javax.comm.CommPortIdentifier
javax.comm.CommPortOwnershipListener
javax.comm.ParallelPortEvent
javax.comm.SerialPortEvent
javax.comm.ParallelPortEventListener (extends java.util.EventListener)
javax.comm.SerialPortEventListener (extends java.util.EventListener)
javax.comm.NoSuchPortException
javax.comm.PortInUseException
javax.comm.UnsupportedCommOperationException
通讯方式,CommPort的输入流的读取方式与文件的输入流有些不一样,那就是可能永远不知这个InputStream何时结束,除非对方的OutputStream发送了一个特定数据表示发送结束,收到这个特定字符后,再行关闭InputStream。而comm.jar提供了两种灵活的方式让我们读取数据:
1.轮询方式(Polling)
2.监听方式(listening)。Comm API支持标准的Java Bean型的事件模型。也就是说,可以使用类似AddXXXListener这样的方法为一个串口注册自己的监听器,以监听方式进行数据读取。
2.RXTX项目
RXTX是一个提供串口和并口通信的开源java类库,由该项目发布的文件均遵循LGPL协议。该项目的主页位于http://users.frii.com/jarvi/rxtx/index.html。
RXTX项目提供了Windows,Linux,Mac os X,Solaris操作系统下的兼容javax.comm串口通讯包API的实现,为其他开发人员在此类系统下开发串口应用提供了相当的方便。
针对x86体系结构的Linux操作系统平台,RXTX的部署包括下面几个文件:
* RXTXcomm.jar RXTX自己的javax.comm实现
* librxtxSerial.so 由RXTXcomm.jar调用的底层串口库文件
* librxtxParallel.so 由RXTXcomm.jar调用的底层并口库文件
* javax.comm.properties RXTX驱动的类配置文件,内容是Driver=gnu.io.RXTXCommDriver
3.RXTX的配置方法及部分源代码
为了使我们的程序使用RXTX作为串口通讯的底层API,需要配置它的环境。仍然以Linux系统平台为例:
1.复制librxtxSerial.so,librxtxParallel.so到$JAVA_HOME/lib/$(ARCH)/
2.复制RXTXcomm.jar到$JAVA_HOME/ext/,或在应用程序启动的CLASSPATH中包含RXTXcomm.jar
3.定义驱动类后将javax.comm.properties放在应用程序的根目录下
RXTX的使用上与sun提供的comm.jar基本相同,编程时最明显的不同是要包含的包名由javax.comm.*改成了gnu.io.*。下面是我们环境监测系统中封装的一个232串口驱动类部分源代码,使用RXTX作为串口通讯类库。
下面的driver232类是监测点子系统以开源的rxtx项目提供的串口通讯扩展包为基础自己封装的一个RS232串口驱动类库(部分代码),为系统的其它部分提供简易的访问本机串口的方法。类的前面部分是各个串口参数的声明,后面是读取,写入串口的具体方法实现。
引入 rxtx java 类库包RXTXcomm.jar
package com.kasumi.site.physicalservice; //import javax.comm.*; import gnu.io.*; import java.io.*; import com.kasumi.util.format.*; import java.awt.*; import java.awt.event.*; import org.apache.log4j.*; import com.kasumi.site.infrastructure.*; import com.kasumi.util.misc.*; |
实现已定义好的 ISerial 接口,提供 ISerial 规定的各个方法的实现。
/** * <p>该程序实现 ISerial 接口, 提供 RS-232C 的驱动 * @author lhh * @version 3.0 * @see com.kasumi.site.publicservice.measuredev */ public class driver232 implements ISitePlugin, IPoolable, ISiteConfigurable, ISerial { /** Clear all buffer data before lent out */ private boolean clearBufferData = true; |
类的前面部分是各个串口参数的声明,在本类中也提供了设置与获取这些参数的方法,运用Ioc技术在运行时根据配置文件plugin.conf的配置注入这些参数,以利于各个不同监测仪器的串口通讯参数设置。
/** Serial default setting */ private String portName = "COM1"; private int baudRate = 2400; private int flowControlIn = SerialPort.FLOWCONTROL_NONE; private int flowControlOut = SerialPort.FLOWCONTROL_NONE; private int databits = SerialPort.DATABITS_8; private int stopbits = SerialPort.STOPBITS_1; private int parity = SerialPort.PARITY_NONE; private ISiteContext siteContext = null; // private SerialParameters parameters; private OutputStream os; private InputStream is; private CommPortIdentifier portId; private SerialPort sPort; private boolean open; private String poolName; static Logger logger = Logger.getLogger(driver232.class); private String domainName=null; |
设置串口连接参数的方法
public void setConnectionParameters() throws IOException { // Set connection parameters, if set fails return parameters object // to original state. try { sPort.setSerialPortParams(baudRate,databits,stopbits,parity); } catch (UnsupportedCommOperationException e) { throw new IOException(getDomainName() + ":" + e.toString()); } // Set flow control. try { sPort.setFlowControlMode(flowControlIn|flowControlOut); } catch (UnsupportedCommOperationException e) { throw new IOException(getDomainName() + ":" + e.toString()); } } |
用已定义好的连接参数"打开"串口连接
/* * Open the port */ public void openConnection() throws IOException { try { portId = CommPortIdentifier.getPortIdentifier(portName); } catch (NoSuchPortException e) { throw new IOException(getDomainName() + ":" + e.toString()); } try { sPort = (SerialPort)portId.open("SerialCom", 30000); } catch (PortInUseException e) { throw new IOException(getDomainName() + ":" + e.toString()); } try { setConnectionParameters(); } catch (IOException e) { sPort.close(); throw e; } try { os = sPort.getOutputStream(); is = sPort.getInputStream(); } catch (IOException e) { sPort.close(); throw new IOException(getDomainName() + ":" + e.toString()); } sPort.notifyOnDataAvailable(true); open = true; } |
一次串口通讯结束后关闭当前的串口连接,以让系统其它监测仪器复用串口
/** * Close the port and clean up associated elements. * @param * @return void */ public void closeConnection() { // If port is alread closed just return. if (!open) { return; } if (sPort != null) { try { // close the i/o streams. os.close(); is.close(); } catch (IOException e) { logger.error(e.toString()); } // Close the port. sPort.close(); //portId.removePortOwnershipListener(this); } open = false; } |
读取串口的方法。读取与写入串口的方法在本类中提供了多个,因为系统中各个仪器的串口通讯要求不同,读取和写入的方法在实现上有一些差异,所以提供了多个方法以适应这类需求
/** * <p>在指定的时间内从串口读取指定长度的数据</p> * <pre>注意: 如果在指定的时间内未能读取指定长度的数据, * <p> 那么将返回实际读取的数据以及对应的长度</pre> * @param data 用于存放从串口读取的数据的缓冲区 * @param rdLen 指定要读取的数据的长度 * @param timeout 指定读取数据的时间长度 * @return 实际读取的数据的长度 * @throws IOException 如果读串口时低层 API 抛出异常 */ public int read(byte[] data, int rdLen, int timeout) throws IOException { try { if (!sPort.isReceiveTimeoutEnabled()){ sPort.enableReceiveTimeout(timeout); } } catch (UnsupportedCommOperationException e) { throw new IOException(e.toString()); } int offset = 0; int curRdLen = rdLen; long start = System.currentTimeMillis(); long end = start + timeout; while (offset < rdLen && System.currentTimeMillis() < end) { //while (offset < rdLen) { try { int len = is.read(data, offset, curRdLen); offset += len; curRdLen -= len; } catch (IOException e) { throw e; } } if (offset > 0) { HexDumper.dumpLogger(logger,"<< ", data, 0, offset); } return offset; } |
往串口写数据的方法
/** * 写串口数据 * * @param outdata 写出数据 * @param outoffset 开始写出偏移量 * @param outlen 写出长度 * @return 实际读入的数据长度 */ public int writeData(byte[] outdata, int outoffset, int outlen) throws IOException { HexDumper.dumpLogger(logger,">> ",outdata, outoffset, outlen); try { os.write(outdata, outoffset, outlen); os.flush(); } catch (IOException e) { throw new IOException(getDomainName() + ":" + e.toString()); } return outlen; } } |