java串口通信

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

java串口通信API说明
java串口通信
Java提供了 CommunicationAPI(包含于m包中)用于通过与机器无关的方式,控制各种外部设备。

Communications API,是标准的Java的扩展部分,它在JavaAPI中是没有附带的。

因此,必须先在SUN公司网站的Java站点()上下载这个扩展类库。

1.1Communications API 简介
Communications API 的核心是抽象的CommPort类及其两个子类:SerialPort类和ParallePort类。

其中,SerialPort类是用于串口通信的类,ParallePort类是用于并行口通信的类。

CommPort类还提供了常规的通信模式和方法,例如:getInputStream( )方法和getOutputStream( )方法,专用于与端口上的设备进行通信。

然而,这些类的构造方法都被有意的设置为非公有的(non-public)。

所以,不能直接构造对象,而是先通过静态的CommPortIdentifer.getPortIdentifiers()获得端口列表;再从这个端口列表中选择所需要的端口,并调用CommPortIdentifer对象的Open( )方法,这样,就能得到一个CommPort对象。

当然,还要将这个CommPort对象的类型转换为某个非抽象的子类,表明是特定的通讯设备。

该子类可以是SerialPort类和ParallePort类中的一个。

下面将分别对CommPort类,CommPortIdentifier类,串口类SerialPort进行详细的介绍。

1.2 CommPortIdentifier类
CommPortIdentifier类的方法如下:
方法说明
addPortName(String, int, CommDriver) 添加端口名到端口列表里addPortOwnershipListener(CommPortOwnershipListener) 添加端口拥有的监听器
removePortOwnershipListener(CommPortOwnershipListener) 移除端口拥有的监听器
getCurrentOwner() 得到当前占有端口的对象或应用程序
getName() 得到端口名称
getPortIdentifier(CommPort) 得到参数打开的端口的CommPortIdentifier类型对象
getPortIdentifier(String) 得到以参数命名的端口的CommPortIdentifier类型对象
getPortIdentifiers() 得到系统中的端口列表
getPortType() 得到端口的类型
isCurrentlyOwned() 判断当前端口是否被占用
open(FileDescriptor) 用文件描述的类型打开端口
open(String, int) 打开端口,两个参数:程序名称,延迟时间(毫秒数)
1.3 SerialPort类
SerialPort关于串口参数的静态成员变量
成员变量说明成员变量说明成员变量说明
DATABITS_5 数据位为5 STOPBITS_2 停止位为2 PARITY_ODD 奇检验DATABITS_6 数据位为6 STOPBITS_1 停止位为1 PARITY_MARK 标记检验
DATABITS_7 数据位为7 STOPBITS_1_5 停止为1.5 PARITY_NONE 空格检验
DATABITS_8 数据位为8 PARITY_EVEN 偶检验 PARITY_SPACE 无检验SerialPort对象的关于串口参数的函数
方法说明方法说明
getBaudRate() 得到波特率 getParity() 得到检验类型getDataBits() 得到数据位数 getStopBits() 得到停止位数setSerialPortParams(int, int, int, int) 设置串口参数依次为(波特率,数据位,停止位,奇偶检验)
SerialPort关于事件的静态成员变量
成员变量说明成员变量说明
BI Break interrupt中断 FE Framing error错误
CD Carrier detect载波侦听 OE Overrun error错误
CTS Clear to send清除以传送 PE Parity error奇偶检验错误DSR Data set ready数据备妥 RI Ring indicator响铃侦测
DATA_AVAILABLE 串口中的可用数据 OUTPUT_BUFFER_EMPTY 输出缓冲区空
SerialPort中关于事件的方法
方法说明方法说明方法说明
isCD() 是否有载波 isCTS() 是否清除以传送 isDSR() 数据是否备妥
isDTR() 是否数据端备妥 isRI() 是否响铃侦测 isRTS() 是否要求传送
addEventListener(SerialPortEventListener) 向SerialPort 对象中添加串口事件监听器
removeEventListener() 移除SerialPort对象中的串口事件监听器notifyOnBreakInterrupt(boolean) 设置中断事件true有效,false 无效
notifyOnCarrierDetect(boolean) 设置载波监听事件true有效,false无效
notifyOnCTS(boolean) 设置清除发送事件true有效,false无效notifyOnDataAvailable(boolean) 设置串口有数据的事件true有效,false无效
notifyOnDSR(boolean) 设置数据备妥事件true有效,false无效notifyOnFramingError(boolean) 设置发生错误事件true有效,false无效
notifyOnOutputEmpty(boolean) 设置发送缓冲区为空事件true有效,false无效
notifyOnParityError(boolean) 设置发生奇偶检验错误事件true 有效,false无效
notifyOnRingIndicator(boolean) 设置响铃侦测事件true有效,false无效
getEventType() 得到发生的事件类型返回值为int型
sendBreak(int) 设置中断过程的时间,参数为毫秒值
setRTS(boolean) 设置或清除RTS位
setDTR(boolean) 设置或清除DTR位
SerialPort中的其他常用方法
方法说明
close() 关闭串口
getOutputStream() 得到OutputStream类型的输出流getInputStream() 得到InputStream类型的输入流
一年半前在jbuilder2006下写过串口通信程序,最近做一个GPS-SMS 项目,用到串口通信,在JBUILDER 配置了comm.jar却发现找不到端口,经过几番查找才发现自己配置的路径错了,浪费了不少时间,有必要记下来,防止下次出错,此次还暴露了自己看文章一目十行,走马观花的毛病。

m是Sun公司提供的,用于开发平台独立的通讯应用程序的扩展API。

(ps:这里javax的x很准确地表明了它是一个扩展包,而不是核心包(core package),但由于历史原因,javax下的并不都是扩展包,比如swing包已经是Java核心架构的一部分了,不过为了与Java1.1编码兼容,仍使用javax.swing。

)m可以访问RS232接口(串口)及有限制地访问IEEE-1284(并口)。

下载
需要到其官方主页/products/javacomm/下载这个API,目前的最新版本是3.0。

不过可惜的是,Sun目前没有推出此API在Windows平台下的3.0版本,主页上列出的三个版本,分别是运行在x86和Sparc结构下的Solaris系统,以及x86下的Linux系统。

要下载Windows版本只能去寻找较老的版本了。

我所找到的2个网址是/projects/cricket/software/javaSeri al.zip(两个文件夹里面有所需的3个文件),/Jolt/javacomm20-win32.zip和(完整的2.0版本,还有examples)。

安装
这里的所谓安装就是把三个重要的文件放到指定的目录下。

将下载的文件解压缩后,在\javacomm20-win32\commapi目录下有必需的三个文件comm.jar,m. properties和win32comm.dll。

将文件comm.jar拷贝到%JAVA_HOME%\jre\lib\ext;文件m. properties拷贝到%JAVA_HOME%\jre\lib; 文件win32comm.dll拷贝到%JAVA_HOME%\bin。

注意%JAVA_HOME%是jdk的路径,而非jre。

API
在m下有13个类和接口,分别是
4个接口
CommDriver 可负载设备(the loadable device)驱动程序接口的一部分
CommPortOwnershipListener 传递各种通讯端口的所有权事件ParallelPortEventListener 传递并行端口事件SerialPortEventListener 传递串行端口事件
6个类
CommPort 通讯端口
CommPortIdentifier通讯端口管理
ParallelPort 并行通讯端口
ParallelPortEvent 并行端口事件
SerialPort RS-232串行通讯端口
SerialPortEvent 串行端口事件
3个异常类
NoSuchPortException 当驱动程序不能找到指定端口时抛出PortInUseException 当碰到指定端口正在使用中时抛出UnsupportedCommOperationException 驱动程序不允许指定操作时抛出
实例
同API一起下载的还有一个examples文件,里面有6个程序。

首先看最简单的读、写程序。

读串口的例程
import java.io.*;
import java.util.*;
import m.*;
public class SimpleRead implements Runnable, SerialPortEventListener {
static CommPortIdentifier portId;
static Enumeration portList;//枚举类
InputStream inputStream;
SerialPort serialPort;
Thread readThread;
public static void main(String[] args) {
portList = CommPortIdentifier.getPortIdentifiers();/*不带参数的getPortIdentifiers方
法获得一个枚举对象,该对象又包含了系统中管理每个端口的CommPortIdentifier对象。

注意这里的端口不仅仅是指串口,也包括并口。

这个方法还可以带参数。

getPortIdentifiers(CommPort)获得与已经被应用程序打开的端口相对应的CommPortIdentifier对象。

getPortIdentifier(String portName)获取指定端口名(比如“COM1”)的CommPortIdentifier对象。

*/
while (portList.hasMoreElements())
{
portId=(CommPortIdentifier) portList.nextElement();
if(portId.getPortType()==CommPortIdentifier.PORT_SERIAL)/*g etPortType方法返回端口类型*/
{
// if (portId.getName().equals("COM1"))/* 找Windows下的第一个串口*/
{
if (portId.getName().equals("/dev/term/a"))/*找Unix-like系统下的第一个串口*/
{
SimpleRead reader = new SimpleRead();
}
}
}
}
public SimpleRead() {
try {
serialPort=(SerialPort)portId.open("SimpleReadApp", 2000);/* open方法打开通讯端口,获得一个CommPort对象。

它使程序独占端口。

如果端口正被其他应用程序占用,将使用CommPortOwnershipListener事件机制,传递一个PORT_OWNERSHIP_REQUESTED事件。

每个端口都关联一个InputStream
何一个OutputStream。

如果端口是用open方法打开的,那么任何的getInputStream都将返回相同的数据流对象,除非有close被调用。

有两个参数,第一个为应用程序名;第二个参数是在端口打开时阻塞等待的毫秒数。

*/
} catch (PortInUseException e) {}
try {
inputStream = serialPort.getInputStream();/*获取端口的输入流对象*/
} catch (IOException e) {}
try {
serialPort.addEventListener(this);/*注册一个SerialPortEventListener事件来监听串
口事件*/
} catch (TooManyListenersException e) {}
serialPort.notifyOnDataAvailable(true);/*数据可用*/ try {
serialPort.setSerialPortParams(9600,
SerialPort.DATABITS_8,
SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);/*设置串口初始化参数,依次是波特率,数据位,停止位和校验*/
} catch (UnsupportedCommOperationException e) {}
readThread = new Thread(this);
&
本文主要给出一个实用的java 串口通信程序,供大家讨论学习./******************************************
* 程序文件名称:SendComm.java
* 功能:从串行口COM1中发送数据
******************************************/
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import m.*;
class S_Frame extends Frame implements
Runnable,ActionListener
{
/*检测系统中可用的通讯端口类 */
static CommPortIdentifier portId;
/*Enumeration 为枚举型类,在util中 */
static Enumeration portList;
OutputStream outputStream;
/*RS-232的串行口 */
SerialPort serialPort;
Thread readThread;
Panel p=new Panel();
TextField in_message=new TextField("打开COM1,波特率9600,数据位8,停止位1.");
TextArea out_message=new TextArea();
Button btnOpen=new Button("打开串口, 发送数据");
Button btnClose=new Button("关闭串口, 停止发送数据"); byte data[]=new byte[10240];
/*设置判断要是否关闭串口的标志*/
boolean mark;
/*安排窗体*/
S_Frame()
{ super("串口发送数据");
setSize(200,200);
setVisible(true);
add(out_message,"Center");
add(p,"North");
p.add(btnOpen);
p.add(btnClose);
add(in_message,"South");
btnOpen.addActionListener(this);
btnClose.addActionListener(this);
} //R_Frame() end
/*点击按扭打开串口.*/
public void actionPerformed(ActionEvent event) {
if (event.getSource()==btnClose){
serialPort.close(); //关闭串口
mark=true; //用于中止线程的run()方法
in_message.setText("串口COM1已经关闭,停止发送数据.");
}
else { mark=false;
/*从文本区按字节读取数据*/
data=out_message.getText().getBytes();
/*打开串口*/
start();
in_message.setText("串口COM1已经打开,正在每2秒钟发送一次数据.....");
}
} //actionPerformed() end
/*打开串口,并调用线程发送数据*/
public void start(){
/*获取系统中所有的通讯端口 */
portList=CommPortIdentifier.getPortIdentifiers();
/* 用循环结构找出串口 */
while (portList.hasMoreElements()){
/*强制转换为通讯端口类型*/
portId=(CommPortIdentifier)portList.nextElement();
if(portId.getPortType() == CommPortIdentifier.PORT_SERIAL){ if (portId.getName().equals("COM1")) {
/*打开串口 */
try {
serialPort = (SerialPort) portId.open("ReadComm", 2000);
}
catch (PortInUseException e) { }
/*设置串口输出流*/
try {
outputStream = serialPort.getOutputStream();
}
catch (IOException e) {}
} //if end
} //if end
} //while end
/*调用线程发送数据*/
try{
readThread = new Thread(this);
//线程负责每发送一次数据,休眠2秒钟
readThread.start();
}
catch (Exception e) { }
} //start() end
/*发送数据,休眠2秒钟后重发*/
public void run() {
/*设置串口通讯参数*/
try {
serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8,
SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
}
catch (UnsupportedCommOperationException e) { } /*发送数据流(将数组data[]中的数据发送出去)*/ try {
outputStream.write(data);
}
catch (IOException e) { }
/*发送数据后休眠2秒钟,然后再重发*/
try { Thread.sleep(2000);
if (mark)
{return; //结束run方法,导致线程死亡
}
start();
}
catch (InterruptedException e) { }
} //run() end
} //类S_Frame end
public class SendComm
{public static void main(String args[]) { S_Frame S_win=new S_Frame();
S_win.addWindowListener(new WindowAdapter() {public void windowClosing(WindowEvent e) {System.exit(0); }
});
S_win.pack();
}
}
/****************************************** * 程序文件名称:ReadComm.java
* 功能:从串行口COM1中接收数据
******************************************/ import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import m.*;
class R_Frame extends Frame implements
Runnable,ActionListener,SerialPortEventListener
{
/* 检测系统中可用的通讯端口类 */
static CommPortIdentifier portId;
/* Enumeration 为枚举型类,在java.util中 */
static Enumeration portList;
InputStream inputStream;
/* 声明RS-232串行端口的成员变量 */
SerialPort serialPort;
Thread readThread;
String str="";
TextField out_message=new TextField("上面文本框显示接收到的数据");
TextArea in_message=new TextArea();
Button btnOpen=new Button("打开串口");
/*建立窗体*/
R_Frame()
{
super("串口接收数据");
setSize(200,200);
setVisible(true);
btnOpen.addActionListener(this);
add(out_message,"South");
add(in_message,"Center");
add(btnOpen,"North");
} //R_Frame() end
/*点击按扭所触发的事件:打开串口,并监听串口. */
public void actionPerformed(ActionEvent event)
{
/*获取系统中所有的通讯端口 */
portList=CommPortIdentifier.getPortIdentifiers();
/* 用循环结构找出串口 */
while (portList.hasMoreElements()){
/*强制转换为通讯端口类型*/
portId=(CommPortIdentifier)portList.nextElement();
if(portId.getPortType() == CommPortIdentifier.PORT_SERIAL){ if (portId.getName().equals("COM1")) {
try {
serialPort = (SerialPort) portId.open("ReadComm", 2000);
out_message.setText("已打开端口COM1 ,正在接收数据..... "); }
catch (PortInUseException e) { }
/*设置串口监听器*/
try {
serialPort.addEventListener(this);
}
catch (TooManyListenersException e) { }
/* 侦听到串口有数据,触发串口事件*/
serialPort.notifyOnDataAvailable(true);
} //if end
} //if end
} //while end
readThread = new Thread(this);
readThread.start(); //线程负责每接收一次数据休眠20秒钟
} //actionPerformed() end
/*接收数据后休眠20秒钟*/
public void run() {
try {
Thread.sleep(20000);
}
catch (InterruptedException e) { }
} //run() end
/*串口监听器触发的事件,设置串口通讯参数,读取数据并写到文本区中*/
public void serialEvent(SerialPortEvent event) {
/*设置串口通讯参数:波特率、数据位、停止位、奇偶校验*/
try {
serialPort.setSerialPortParams(9600,
SerialPort.DATABITS_8,
SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
}
catch (UnsupportedCommOperationException e) { }
byte[] readBuffer = new byte[20];
try {
inputStream = serialPort.getInputStream();
}
catch (IOException e) {}
try {
/* 从线路上读取数据流 */
while (inputStream.available() > 0) {
int numBytes = inputStream.read(readBuffer);
} //while end
str=new String(readBuffer);
/*接收到的数据存放到文本区中*/
in_message.append(str+"\n");
}
catch (IOException e) { }
} //serialEvent() end
} //类R_Frame end
public class ReadComm
{
public static void main(String args[])
{
/* 实例化接收串口数据的窗体类 */
R_Frame R_win=new R_Frame();
/* 定义窗体适配器的关闭按钮功能 */
R_win.addWindowListener(new WindowAdapter() {public void windowClosing(WindowEvent e) {System.exit(0); }
});
R_win.pack();
}
}
Java串口通讯
串行通讯协议有很多种,像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)针一一对应就行了。

小技巧:如何辨别TXD和RXD端口?
搞电子的人手边应该常备一个电表,用来测测电压,电阻什么的会很有用。

你只要分别测一下RS232端口的2--5或3--5针脚之间的电压,通常TXD针脚与GND之间会有3~15V左右的负电压,表示它是TXD针脚。

安装Java Communications API
Sun的J2SE中并没有直接提供以上提到的任何一种串行通讯协议的开发包,而是以独立的jar包形式发布在网站上(从这里下载)----即comm.jar,称之为Javatm Communications API,它是J2SE的标准扩展。

comm.jar并不是最近才有,早在1998年时,sun就已经发布了这个开发包。

comm.jar分别提供了对常用的RS232串行端口和IEEE1284并行端口通讯的支持。

目前sun发布的comm.jar 只有Windows和Solaris平台两个版本,如果你需要Linux平台下的,可以在/~kevinh/linuxcomm.html找到。

在使用comm.jar之前,必须知道如何安装它。

这也是困扰许多初学java RS232通讯者的一个难题。

如果我们电脑上安装了JDK,它将同时为我们安装一份JRE(Java Runtime Entironment),通常我们
运行程序时都是以JRE来运行的。

所以以下的安装适用于JRE。

如果你是用JDK来运行程序的,请将相应的改成。

下载了comm.jar开发包后,与之一起的还有两个重要的文件,win32com.dll和m.properties。

comm.jar提供了通讯用的java API,而win32com.dll提供了供comm.jar调用的本地驱动接口。

而m.properties是这个驱动的类配置文件。

首先将comm.jar复制到\lib\ext目录。

再将win21com.dll复制到你的RS232应用程序运行的目录,即user.dir。

然后将m.properties 复制到\lib目录。

通讯前的准备
如果你手头上没有现成的提供了标准RS232串口的设备,你可以将自己的电脑模拟成两台不同的串口设备。

通常电脑主机后面的面板提供了两个9针的串口,请将这两个串口的2,3,5脚按前面介绍的方法连接。

电子市场都有现成的连接头卖,请不要买那种封装的严严实实的接头,而要买用螺丝封装可以拆开的连接头,这样可以方便自己根据需要连接各个针脚。

Comm API基础
我无意于在此详细描述Comm API每个类和接口的用法,但我会介绍Comm API的类结构和几个重要的API用法。

所有的comm API位于m包下面。

从Comm API的javadoc 来看,它介绍给我们的只有区区以下13个类或接口:
下面讲解一下几个主要类或接口。

1.枚举出系统所有的RS232端口
在开始使用RS232端口通讯之前,我们想知道系统有哪些端口是可用的,以下代码列出系统中所有可用的RS232端口:
在我的电脑上以上程序输出以下结果:
COM1
COM2
CommPortIdentifier类的getPortIdentifiers方法可以找到系统所有的串口,每个串口对应一个CommPortIdentifier类的实例。

2.打开端口
如果你使用端口,必须先打开它。

通过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中,最好的方式是使用线程,就像以下代码片断一样。

2.监听方式(listening)
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 - 振铃指示.
下面是一个监听器的示例:
这个监听器只是简单打印每个发生的事件名称。

而对于大多数应用程序来说,通常关心是DATA_AVAILABLE事件,当数据从外部设备传送到端口上来时将触发此事件。

此时就可以使用前面提到过的方法,serialPort.getInputStream()来从InputStream中读取数据了。

1 串口通信简介
嵌入式系统或传感器网络的很多应用和测试都需要通过PC机与
嵌入式设备或传感器节点进行通信。

其中,最常用的接口就是RS-23 2串口和并口(鉴于USB接口的复杂性以及不需要很大的数据传输量,USB接口用在这里还是显得过于奢侈,况且目前除了SUN有一个支持USB的包之外,我还没有看到其他直接支持USB的Java类库)。

SUN 的CommAPI分别提供了对常用的RS232串行端口和IEEE1284并行端口通讯的支持。

RS-232-C(又称EIA RS-232-C,以下简称RS232)是在1970年由美国电子工业协会(EIA)联合贝尔系统、调制解调器厂家及计算机终端生产厂家共同制定的用于串行通讯的标准。

RS232是一个全双工的通讯协议,它可以同时进行数据接收和发送的工作。

1.1 常见的Java串口包
目前,常见的Java串口包有SUN在1998年发布的串口通信API:co mm2.0.jar(Windows下)、comm3.0.jar(Linux/Solaris);IBM的串口通信API以及一个开源的实现。

鉴于在Windows下SUN的API比较常用以及IBM的实现和SUN的在API层面都是一样的,那个开源的实现又不像两家大厂的产品那样让人放心,这里就只介绍SUN的串口通信API在Windows平台下的使用。

1.2 串口包的安装(Windows下)
到SUN的网站下载javacomm20-win32.zip,包含的东西如下所示:
按照其使用说明(Readme.html)的说法,要想使用串口包进行串口通信,除了设置好环境变量之外,还要将win32com.dll复制到<J DK>\bin目录下;将comm.jar复制到<JDK>\lib;把m.pr operties也同样拷贝到<JDK>\lib目录下。

然而在真正运行使用串口包的时候,仅作这些是不够的。

因为通常当运行“java MyApp”的时候,是由JRE下的虚拟机启动MyApp的。

而我们只复制上述文件到J DK相应目录下,所以应用程序将会提示找不到串口。

解决这个问题的方法很简单,我们只须将上面提到的文件放到JRE相应的目录下就可以了。

值得注意的是,在网络应用程序中使用串口API的时候,还会遇到其他更复杂问题。

有兴趣的话,你可以查看CSDN社区中“关于网页上Applet用javacomm20读取客户端串口的问题”的帖子。

2 串口API概览
2.1 mPort
这是用于描述一个被底层系统支持的端口的抽象类。

它包含一些高层的IO控制方法,这些方法对于所有不同的通讯端口来说是通用的。

SerialPort 和ParallelPort都是它的子类,前者用于控制串行端口而后者用于控这并口,二者对于各自底层的物理端口都有不同的控制方法。

这里我们只关心SerialPort。

2.2 mPortIdentifier
这个类主要用于对串口进行管理和设置,是对串口进行访问控制的核心类。

主要包括以下方法
l 确定是否有可用的通信端口
l 为IO操作打开通信端口
l 决定端口的所有权
l 处理端口所有权的争用
l 管理端口所有权变化引发的事件(Event)
2.3 m.SerialPort
这个类用于描述一个RS-232串行通信端口的底层接口,它定义了串口通信所需的最小功能集。

通过它,用户可以直接对串口进行读、写及设置工作。

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

2.4.1 列举出本机所有可用串口
void listPortChoices() {
CommPortIdentifier portId;
Enumeration en = CommPortIdentifier.getPortI dentifiers();
// iterate through the ports.
while (en.hasMoreElements()) {
portId = (CommPortIdentifier) en.nextEle ment();
if (portId.getPortType() == CommPortIden tifier.PORT_SERIAL) {
System.out.println(portId.getName ());
}
}
portChoice.select(parameters.getPortName());
}
以上代码可以列举出当前系统所有可用的串口名称,我的机器上输出的结果是COM1和COM3。

2.4.2 串口参数的配置
串口一般有如下参数可以在该串口打开以前配置进行配置:
包括波特率,输入/输出流控制,数据位数,停止位和齐偶校验。

SerialPort sPort;
try {
sPort.setSerialPortParams(BaudRate,Databits, Stopbits,Parity);
//设置输入/输出控制流
sPort.setFlowControlMode(FlowContro lIn | FlowControlOut);
} catch (UnsupportedCommOperationException e) {} 2.4.3 串口的读写
对串口读写之前需要先打开一个串口:
CommPortIdentifier portId = CommPortIdentifier.getPortId entifier(PortName);
try {
SerialPort sPort = (SerialPort) portId.open ("串口所有者名称", 超时等待时间);
} catch (PortInUseException e) {//如果端口被占用就抛出这个异常
throw new SerialConnectionException(e.getMes sage());
}
//用于对串口写数据
OutputStream os = new BufferedOutputStream(sPort.getOutp utStream());
os.write(int data);
//用于从串口读数据
InputStream is = new BufferedInputStream(sPort.getInputS tream());
int receivedData = is.read();
读出来的是int型,你可以把它转换成需要的其他类型。

这里要注意的是,由于Java语言没有无符号类型,即所有的类型都是带符号的,在由byte到int的时候应该尤其注意。

因为如果by te的最高位是1,则转成int类型时将用1来占位。

这样,原本是1 0000000的byte类型的数变成int型就成了1111111110000000,这是很严重的问题,应该注意避免。

3 串口通信的通用模式及其问题
终于唠叨完我最讨厌的基础知识了,下面开始我们本次的重点--串口应用的研究。

由于向串口写数据很简单,所以这里我们只关注于从串口读数据的情况。

通常,串口通信应用程序有两种模式,一种是实现SerialPortEventListener接口,监听各种串口事件并作相应处理;另一种就是建立一个独立的接收线程专门负责数据的接收。

由于这两种方法在某些情况下存在很严重的问题(至于什么问题这里先卖个关子J),所以我的实现是采用第三种方法来解决这个问题。

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

l 首先需要在你的端口控制类(例如SManager)加上“impleme nts SerialPortEventListener”
l 在初始化时加入如下代码:
try {
SerialPort sPort.addEventListener(SManager);
} catch (TooManyListenersException e) {
sPort.close();
throw new SerialConnectionException("too man y listeners added");
}
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字节左。

相关文档
最新文档