java串口编程(好

java串口编程(好
java串口编程(好

一、

java串口编程

2008-04-07 11:31:02| 分类:默认分类| 标签:|字号大中小订阅/******************************************

*程序文件名称:SendComm.java

* 功能:从串行口COM1中发送数据

******************************************/

import java.awt.*;

import java.awt.event.*;

import java.io.*;

import java.util.*;

import https://www.360docs.net/doc/f010939893.html,m.*; //可以使用开源的RXTX

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 https://www.360docs.net/doc/f010939893.html,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提供了CommunicationAPI(包含于https://www.360docs.net/doc/f010939893.html,m包中)用于通过与机器无关的方式,控制各种外部设备。Communications API,是标准的Java的扩展部分,它在JavaAPI中是没有附带的。因此,必须先在SUN公司网站的Java站点(https://www.360docs.net/doc/f010939893.html,)上下载这个扩展类库。

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类型的输入流

什么是串口通讯?

串行通讯协议有很多种,像RS232,RS485,RS422,甚至现今流行的USB等都是串行通讯协议。而串行通讯技术的应用无处不在。可能大家见的最多就是电脑的串口与Modem的通讯。在PC机刚开始在中国流行起来时(大约是在90年代前五年),那时甚至有人用一条串行线进行两台电脑之间的数据共享。除了这些,手机,PDA,USB鼠标、键盘等等都是以串行通讯的方式与电脑连接。还有像多串口卡,各种种类的具有串口通讯接口的检测与测量仪器,串口通讯的网络设备等也是通过串口与计算机连接的。

虽然串口通讯协议有多种,不过目前还是以RS232的通讯方式居多。

RS232通讯基础

RS-232-C(又称EIA RS-232-C,以下简称RS232)是在1970年由美国电子工业协会(EIA)联合贝尔系统、调制解调器厂家及计算机终端生

产厂家共同制定的用于串行通讯的标准。RS232是一个全双工的通讯协议,它可以同时进行数据接收和发送的工作。RS232的端口通常有两种:9针(DB9)和25针(DB25)。

DB9和DB25的常用针脚定义

9针串口(DB9)

25针串口(DB25)

针号

功能说明

缩写针号

功能说明

缩写

1

数据载波检测

DCD 8

数据载波检测

DCD

2

接收数据RXD 3

接收数据RXD

3

发送数据

TXD 2

发送数据

TXD

4

数据终端准备

DTR 20

数据终端准备

DTR

5

信号地

GND 7

信号地

GND

6

数据设备准备好 DSR 6

数据准备好

DSR

7

请求发送RTS 4 请求发送

RTS

8

清除发送

CTS 5

清除发送

CTS

9

振铃指示

RI 22

振铃指示

RI

常见的边线方式

常见的通讯方式是三线式,这种方式是将两个RS232设备的发送端(TXD)和接收端(RXD)及接地端(GND)互相连接,也是许多读者所知道的连接方式:

(9针)

2(RXD) ---------

3(TXD

3(TXD) ---------

2(TXD)

5(GND) ---------

5(GND)

(25针)

2(RXD) ---------

3(TXD

3(TXD) ---------

2(RXD)

7(GND) ---------

7(GND)

这种方式分别将两端的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)针一一对应就行了。

大致了解了RS232之后,我主要关心的是如何使用Java进行串口通讯。

Java Communications API

Sun的J2SE中并没有直接提供以上提到的任何一种串行通讯协议的开发包,而是以独立的jar包形式发布在https://www.360docs.net/doc/f010939893.html,网站上(从这里下载)----即comm.jar,称之为Javatm Communications API,它是J2SE 的标准扩展。comm.jar并不是最近才有,早在1998年时,sun就已经发布了这个开发包。comm.jar分别提供了对常用的RS232串行端口和IEEE1284并行端口通讯的支持。目前sun发布的comm.jar只有Windows和Solaris平台两个版本,如果你需要Linux平台下的,可以使用rxtx。

在使用comm.jar之前,必须知道如何安装它。这也是困扰许多初学java RS232通讯者的一个难题。如果我们电脑上安装了JDK, 它将同时为我们安装一份JRE(Java Runtime Entironment),通常我们运行程序时都是以JRE来运行的。所以以下的安装适用于JRE。如果你是用JDK来运行程序的,请将相应的改成

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

win32com.dll和https://www.360docs.net/doc/f010939893.html,m.properties。comm.jar提供了通讯用的java API,而win32com.dll提供了供comm.jar调用的本地驱动接口。而https://www.360docs.net/doc/f010939893.html,m.properties是这个驱动的类配置文件。首先将comm.jar

复制到libext目录。再将win32com.dll复制到你的

RS232应用程序运行的目录,即user.dir。然后将

https://www.360docs.net/doc/f010939893.html,m.properties复制到lib目录。

Comm API基础

所有的comm API位于https://www.360docs.net/doc/f010939893.html,m包下面。从Comm API的javadoc 来看,它介绍给我们的只有区区以下13个类或接口:

https://www.360docs.net/doc/f010939893.html,mDriver

https://www.360docs.net/doc/f010939893.html,mPort

https://www.360docs.net/doc/f010939893.html,m.ParallelPort

https://www.360docs.net/doc/f010939893.html,m.SerialPort

https://www.360docs.net/doc/f010939893.html,mPortIdentifier

https://www.360docs.net/doc/f010939893.html,mPortOwnershipListener

https://www.360docs.net/doc/f010939893.html,m.ParallelPortEvent

https://www.360docs.net/doc/f010939893.html,m.SerialPortEvent

https://www.360docs.net/doc/f010939893.html,m.ParallelPortEventListener (extends

java.util.EventListener)

https://www.360docs.net/doc/f010939893.html,m.SerialPortEventListener (extends java.util.EventListener) https://www.360docs.net/doc/f010939893.html,m.NoSuchPortException

https://www.360docs.net/doc/f010939893.html,m.PortInUseException

https://www.360docs.net/doc/f010939893.html,m.UnsupportedCommOperationException

下面讲解一下几个主要类或接口。

1.枚举出系统所有的RS232端口

在开始使用RS232端口通讯之前,我们想知道系统有哪些端口是可用的,以下代码列出系统中所有可用的RS232端口:

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好打扮,这一打扮可能就是半小时甚至一小时以上。这时你就耐不住了,每两分钟就

相关主题
相关文档
最新文档