《高级Internet》实验报告
题目: 串口通信
学生姓名: 班 级: 软件工程1202 学 号: 指导老师: 王文浪
西安邮电大学计算机学院
2015 年 6 月 10 日
一、 实验目的
1.了解串口通信的通信的原理
2.串口包的安装(Windows下)
3.解使用java进行串口通信的配置
二、 实验准备
1.首先准备一条串口通信线
2. 在设备上实现Java串口通信,需要用到javacomm20-win32.zip包.目前,常见的Java串口包有SUN在19xx年发布的串口通信API:comm2.0.jar(Windows下)、comm3.0.jar(Linux/Solaris);IBM的串口通信API以及一个开源的实现。鉴于在Windows下SUN的API比较常用以及IBM的实现和SUN的在API层面都是一样的,那个开源的实现又不像两家大厂的产品那样让人放心,这里就只介绍SUN的串口通信API在Windows平台下的使用。
3.到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相应的目录下就可以了.
三、 实验过程
1.串口API概览javax.comm.CommPort
这是用于描述一个被底层系统支持的端口的抽象类。它包含一些高层的IO控制方法,这些方法对于所有不同的通讯端口来说是通用的。SerialPort 和ParallelPort都是它的子类,前者用于控制串行端口而后者用于控这并口,二者对于各自底层的物理端口都有不同的控制方法。这里我只关心SerialPort。
2. javax.comm.CommPortIdentifier
这个类主要用于对串口进行管理和设置,是对串口进行访问控制的核心类。主要包括以下方法
(1)确定是否有可用的通信端口
(2)为IO操作打开通信端口
(3)决定端口的所有权
(4)处理端口所有权的争用
(5)管理端口所有权变化引发的事件(Event)
3. javax.comm.SerialPort
这个类用于描述一个RS-232串行通信端口的底层接口,它定义了串口通信所需的最小功能集。通过它,用户可以直接对串口进行读、写及设置工作 4 .串口通信实例
(1)要准备相应的设备:电脑,外设,通过数据线把他们连接起来。
(2)检验外设到底是用的那个COM口和电脑通讯的. 也就是说,他们有没有真确的连接上。可以通过下载串口通讯口测试软件,我用的是"SuperCommTool.exe"的绿色软件,进行测试的。这软件很适应,如果选中的某个COM已经被使用了,它会给你一个相应的提示(端口以被占用)。如果你不知道到底是使用的那个端口,那么你可以通过superCommTool软件一个一个的试,如果正常的话,那么你可以看到有数据显示在数据接收窗口。也许,有些主板的串口坏了,那么就要买一个转接卡,通过PCI插口转接。
(3)查看外设使用说明书知道外设的相关参数. 比如,波特率,数据位,停止位,校验位,等等。只有正确参数,才能显示正确的数据。当然,可以在通讯测试软件上调试这些参数的。比如:波特率 = 2400,数据位 = 8,停止位 = 2 ,校验位 = 1。
(4)准备开发环境:最基本的JDK了,使用自己的IDE,帮助开发。IDE可能自带了JDK,那么 你要把相应的javaComm20-win32放到运行时使用的JDK中。 下载JAVAcomm20-win32。必须把win32com.dll复制到java.home/bin下;把javax.comm.properties复制到java.home/lib下;把comm.jar添加到classPath下。前面两个都是非常重要的。
(5)获取SerialPort sPort对象的方法。
CommPortIdentifier portId = CommPortIdentifier .getPortIdentifier("COM4"); SerialPort sPort = (SerialPort)portId.open("ship ment",1000);
设置串行端口通讯参数:
sPort.setSerialPortParams(2400,SerialPort.DATABITS_8,SerialPort.STOPBITS_2,SerialPort.PARITY_NONE);
获取输入(出)流:
InputStream is = sPort.getInputStream();//从外 设获取数据
OutputStream os = sPort.getOutputStream();// 发送命令到外设
通过监听器就可以得到数据了:
sPort.notifyOnDataAvailable(true);
sPort.notifyOnBreakInterrupt(true);
sPort.enableReceiveTimeout(30);
StringBuffer linkWgt = new StringBuffer ();//存放获取的数据
sPort.addEventListener()
new SerialPortEventListener(){
public void serialEvent(SerialPortE vent e){
int newData = 0;
switch (e.getEventType()) {
case SerialPortEvent.DATA_ AVAILABLE:
while (newData != -1) {
try {
newData = is.read() ;
if (newData == -1) {
break; }
if ('\r' == (char)new Data) { }
else { linkWgt.append(( char)newData); } }
catch (IOException e x) {
System.err.println(e x);
return; }
}
try{
System.out.println("linkW gt
"+Double.valueOf(linkWgt.toString()));
}
catch(Exception ew){
ew.printStackTrace(); }
finally{
try{ //用完了,关闭端 口。
is.close(); sPort.close();
}catch(Exception c)
{
c.printStackTrace();}
} break; ---------|||||
case SerialPortEvent.BI: System.out.println("\n--- BREAK RECEIVED ---\n");
}
}
}
(6)枚举出系统所有的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
(7)主要程序
程序文件名称:SendComm.java * 功能:从串行口COM1中发送数据 import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import javax.comm.*;
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_Fram 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 javax.comm.*;
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进行串口通信需要的准备工作,在我的应用程序中,
我将收到的数据打包放到一个缓存中,然后启动另一个线程从缓存中获取并处理数据。两个线程以生产者—消费者模式协同工作。在处理线程中调用这个方法返回所需要的数据序列并处理之,这样不但没有丢失数据的现象行出现,也没有数据接收延迟了。这里唯一需要注意的就是当串口停止发送数据或没有数据的时候is.read()一直都返回-1,如果一旦在开始接收数据的时候发现-1就不要理它,继续接收,直到收到真正的数据为止.