Java串行端口通讯技能概述
副标题#e#
串行通讯概述
串行通讯协议有许多种,像RS232,RS485,RS422,甚至现今风行的USB等都是串行通讯协议。而串行通讯技能的应用无处不在。大概各人见的最多就是电脑的串口与Modem的通讯。记得在PC机刚开始在中国风行起来时(约莫是在90年月前五年),当时甚至有人用一条串行线举办两台电脑之间的数据共享。除了这些,手机,PDA,USB鼠标、键盘等等都是以串行通讯的方法与电脑毗连。而笔者事情性质的干系,所打仗到的就更多了,像多串口卡,各类种类的具有串口通讯接口的检测与丈量仪器,串口通讯的网络设备等。
固然串行通讯有许多种,但笔者所知的在整个电子通讯产物方面,以RS232的通讯方法最为多见。固然USB接口的电子产物也是层出不穷,但相识一下Java在串行通讯方面的技能尚有有须要的,说不定有哪位读者还想用此技能写一个PDA与电脑之间数据共享的措施呢。
本文主要以RS232为主来讲授Java的串行通讯技能。
RS232通讯基本
RS-232-C(又称EIA RS-232-C,以下简称RS232)是在1970年由美国电子家产协会(EIA)连系贝尔系统、调制解调器厂家及计较机终端出产厂家配合拟定的用于串行通讯的尺度。RS232是一个全双工的通讯协议,它可以同时举办数据吸收和发送的事情。RS232的端口凡是有两种:9针(DB9)和25针(DB25)。
DB9和DB25的常用针脚界说
图1
常见的边线方法
常见的通讯方法是三线式,这种方法是将两个RS232设备的发送端(TXD)和吸收端(RXD)及接地端(GND)相互毗连,也是很多读者所知道的毗连方法:
图2
这种方法别离将两头的RS232接口的2–3,3—2,5(7)—5(7)针脚毗连起来。个中2是数据吸收线(RXD),3是数据发送线(TXD),5(7)是接地(RND)。假如有一台式PC,和一部NoteBook电脑,就可以用这种方法连线了。用三线式可以将大大都的RS232设备毗连起来。但假如你认死了2–3,3–2,5(7)–5(7)对接这个理,会发此刻连某些RS232设备时并不奏效。这是因为有些设备在电路内部已将2和3线变更过来了,你只要2,3,5(7)针一一对应就行了。
#p#副标题#e#
小能力:如何分辨TXD和RXD端口?
搞电子的人手边应该常备一个电表,用来测测电压,电阻什么的会很有用。你只要别离测一下RS232端口的2–5或3–5针脚之间的电压,凡是TXD针脚与GND之间会有3~15V阁下的负电压,暗示它是TXD针脚。
安装Java Communications API
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://www.geeksville.com/~kevinh/linuxcomm.html找到。
在利用comm.jar之前,必需知道如何安装它。这也是困扰很多初学java RS232通讯者的一个困难。假如我们电脑上安装了JDK,它将同时为我们安装一份JRE(Java Runtime Entironment),凡是我们运行措施时都是以JRE来运行的。所以以下的安装合用于JRE。假如你是用JDK来运行措施的,请将相应的改成。
下载了comm.jar开拓包后,与之一起的尚有两个重要的文件,win32com.dll和javax.comm.properties。comm.jar提供了通讯用的java API,而win32com.dll提供了供comm.jar挪用的当地驱动接口。而javax.comm.properties是这个驱动的类设置文件。首先将comm.jar复制到\lib\ext目次。再将win21com.dll复制到你的RS232应用措施运行的目次,即user.dir。然后将javax.comm.properties复制到\lib目次。
通讯前的筹备
假如你手头上没有现成的提供了尺度RS232串口的设备,你可以将本身的电脑模仿成两台差异的串口设备。凡是电脑主机后头的面板提供了两个9针的串口,请将这两个串口的2,3,5脚按前面先容的要领毗连。电子市场都有现成的连讨论卖,请不要买那种封装的严严实实的讨论,而要买用螺丝封装可以拆开的连讨论,这样可以利便本身按照需要毗连各个针脚。
Comm API基本
我无意于在此具体描写Comm API每个类和接口的用法,但我会先容Comm API的类布局和几个重要的API用法。
所有的comm API位于javax.comm包下面。从Comm API的javadoc来看,它先容给我们的只有区区以下13个类或接口:
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
下面讲授一下几个主要类或接口。
1.列举出系统所有的RS232端口
在开始利用RS232端口通讯之前,我们想知道系统有哪些端口是可用的,以下代码列出系统中所有可用的RS232端口:
#p#分页标题#e#
Enumeration en = CommPortIdentifier.getPortIdentifiers();
CommPortIdentifier portId;
while (en.hasMoreElements())
{
portId = (CommPortIdentifier) en.nextElement();
/*假如端口范例是串口,则打印出其端口信息*/
if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL)
{
System.out.println(portId.getName());
}
}
在我的电脑上以上措施输出以下功效:
COM1
COM2
CommPortIdentifier类的getPortIdentifiers要领可以找到系统所有的串口,每个串口对应一个CommPortIdentifier类的实例。
2.打初步口
假如你利用端口,必需先打开它。
try{
CommPort serialPort = portId.open("My App", 60);
/*从端口中读取数据*/
InputStream input = serialPort.getInputStream();
input.read(...);
/*往端口中写数据*/
OutputStream output = serialPort.getOutputStream();
output.write(...)
...
}catch(PortInUseException ex)
{ ... }
通过CommPortIdentifier的open要领可以返回一个CommPort工具。open要领有两个参数,第一个是String,凡是配置为你的应用措施的名字。第二个参数是时间,即开启端口超时的毫秒数。当端口被别的的应用措施占用时,将抛出PortInUseException异常。
在这里CommPortIdentifier类和CommPort类有什么区别呢?其实它们两者是一一对应的干系。CommPortIdentifier主要认真端口的初始化和开启,以及打点它们的占有权。而CommPort则是跟实际的输入和输出成果有关的。通过CommPort的getInputStream()可以取得端口的输入流,它是java.io.InputStream接口的一个实例。我们可以用尺度的InputStream的操纵接口来读取流中的数据,就像通过FileInputSteam读取文件的内容一样。相应的,CommPort的getOutputStream可以得到端口的输出流,这样就可以往串口输出数据了。
3.封锁端口
利用完的端口,必需记得将其封锁,这样可以让其它的措施有时机利用它,否则其它措施利用该端口时大概会抛出端口正在利用中的错误。很奇怪的是,CommPortIdentifier类只提供了开启端口的要领,而要封锁端口,则要挪用CommPort类的close()要领。
CommPort的输入流的读取方法与文件的输入流有些纷歧样,那就是你大概永远不知这个InputStream何时竣事,除非对方的OutputStream向你发送了一个特定命据暗示发送竣事,你收到这个特定字符后,再行封锁你的InputStream。而comm.jar提供了两种机动的方法让你读取数据。
1.轮询方法(Polling)
举个例子,你同GF相约一起出门去看影戏,但你的GF好妆扮,这一妆扮大概就是半小时甚至一小时以上。这时你就耐不住了,每两分钟就催问一次“好了没?”,如此这样,直到你的GF说OK了才算完。这个就叫轮询(Polling)。
在措施中,轮询凡是设计成一个关闭的轮回,当满意某个条件时即竣事轮回。适才谁人例子中,你的GF说“OK了!”,这个就是竣事你轮询的条件。在单线程的措施中,当轮回一直执行某项任务而又无法预知它何时竣事时,此时你的措施看起来大概就像死机一样。在VB措施中,这个问题可以用在轮回布局中插入一个doEvent语句来办理。而Java中,最好的方法是利用线程,就像以下代码片段一样。
public TestPort extend Thread
{
...
InputStream input = serialPort.getInputStream();
StringBuffer buf = new StringBuffer();
boolean stopped = false;
...
public void run()
{
try {
while( !stopped )
int ch = input.read();
if ( ch=='q' || ch=='Q' )
{
/*竣事读取,封锁端口...*/
stopped = true;
...
}
else
{
buf.append((char)ch);
...
}
}catch (InterruptedException e) { }
}
}
2.监听方法(listening)
#p#分页标题#e#
Comm API支持尺度的Java Bean型的事件模子。也就是说,你可以利用雷同AddXXXListener这样的要领为一个串口注册本身的监听器,以监听方法举办数据读取。
如要对端口监听,你必需先取得CommPortIdentifier类的一个实例,
CommPort serialPort = portId.open("My App", 60);
从而取得SerialPort,再挪用它的addEventListener要领为它添加监听器,
serialPort.addEventListener(new MyPortListener());
SerialPort的监听器必需担任于SerialPortEventListener接口。当有任何SerialPort的事件产生时,将自动挪用监听器中的serialEvent要领。Serial Event有以下几种范例:
BI -通讯间断.
CD -载波检测.
CTS -排除发送.
DATA_AVAILABLE -有数据达到.
DSR -数据设备筹备好.
FE -帧错误.
OE -溢位错误.
OUTPUT_BUFFER_EMPTY -输出缓冲区已清空.
PE -奇偶校验错.
RI - 振铃指示.
下面是一个监听器的示例:
public void MyPortListener implements SerialPortEventListener
{
public void serialEvent(SerialPortEvent evt)
{
switch (evt.getEventType())
{
case SerialPortEvent.CTS :
System.out.println("CTS event occured.");
break;
case SerialPortEvent.CD :
System.out.println("CD event occured.");
break;
case SerialPortEvent.BI :
System.out.println("BI event occured.");
break;
case SerialPortEvent.DSR :
System.out.println("DSR event occured.");
break;
case SerialPortEvent.FE :
System.out.println("FE event occured.");
break;
case SerialPortEvent.OE :
System.out.println("OE event occured.");
break;
case SerialPortEvent.PE :
System.out.println("PE event occured.");
break;
case SerialPortEvent.RI :
System.out.println("RI event occured.");
break;
case SerialPortEvent.OUTPUT_BUFFER_EMPTY :
System.out.println("OUTPUT_BUFFER_EMPTY event occured.");
break;
case SerialPortEvent.DATA_AVAILABLE :
System.out.println("DATA_AVAILABLE event occured.");
int ch;
StringBuffer buf = new StringBuffer();
InputStream input = serialPort.getInputStream
try {
while ( (ch=input.read()) > 0) {
buf.append((char)ch);
}
System.out.print(buf);
} catch (IOException e) {}
break;
}
}
这个监听器只是简朴打印每个产生的事件名称。而对付大大都应用措施来说,凡是体贴是DATA_AVAILABLE事件,当数据从外部设备传送到端口上来时将触发此事件。此时就可以利用前面提到过的要领,serialPort.getInputStream()来从InputStream中读取数据了。