/****************************************** * 程序文件名称:SendComm.java * 功能:从串行口COM1中发送数据 ******************************************/ import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.*; import javax.comm.*; //可以使用开源的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 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提供了 CommunicationAPI(包含于javax.comm包中)用于通过与机器无关的方式,控制各种外部设备。Communications API,是标准的Java的扩展部分,它在JavaAPI中是没有附带的。因此,必须先在SUN公司网站的Java站点(www.java.sun.com)上下载这个扩展类库。 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包形式发布在java.sun.com网站上(从这里下载)----即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来运行程序的,请将相应的<JRE_HOME>改成<JDK_HOME>。 下载了comm.jar开发包后,与之一起的还有两个重要的文件,win32com.dll和javax.comm.properties。 comm.jar提供了通讯用的java API,而win32com.dll提供了供comm.jar调用的本地驱动接口。而javax.comm.properties是这个驱动的类配置文件。首先将comm.jar复制到<JRE_HOME>libext目录。再将win32com.dll复制到你的RS232应用程序运行的目录,即user.dir。然后将javax.comm.properties复制到<JRE_HOME>lib目录。 Comm 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端口: 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) 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 { 这个监听器只是简单打印每个发生的事件名称。而对于大多数应用程序来说,通常关心是DATA_AVAILABLE事件,当数据从外部设备传送到端口上来时将触发此事件。此时就可以使用前面提到过的方法,serialPort.getInputStream()来从InputStream中读取数据了。 完整的程序 为节省篇幅,本文只提供了一些代码片断来帮助读者来理解Comm API的用法。你可以从Comm API的开发包中取得完整的可运行的演示程序。请先下载了comm API的开发包,解压之后有一个名为Sample的目录,里面有几个演示程序,分别是: 1) BlackBox: A Serial Port BlackBox application. 2) ParallelBlackBox: A Parallel Port BlackBox application 3) SerialDemo: A simpler SerialPort sample application 4) Simple: A very simple comm application 5) NullDriver: A template for driver writers. Can be used as the starting point to write a driver for the Comm API. 6) porting: A template CommPortIdentifier java file for people interested in porting the Comm API to a new platform. 其中,第1),3),4)是关于rs232通讯的演示程序。而其它的,2)是并行端口的演示程序。5)和6)是开发自己的端口驱动程序的模板程序,有兴趣的读者可以自行研究。 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; }} java串口通讯 最近在搞C的串口通讯,C里面没有线程的概念,所以C对串口的读写只能在一个进程里面,这样 如果串口的缓存有问题,就会导致报告丢失(正好是我们遇到的),我们来看看支持线程的java是如何来解决这个问题的。 本文介绍了一个简单的通过串口实现全双工通讯的Java类库,该类库大大的简化了对串口进行操作的过程。 本类库主要包括:SerialBean.java (与其他应用程序的接口), SerialBuffer.java (用来保存从串口所接收数据的缓冲区), ReadSerial.java (从串口读取数据的程序)。另外本类库还提供了一个例程SerialExample.java 作为示范。在下面的内容中将逐一对这几个部分进行详细介绍。 SerialBean SerialBean是本类库与其他应用程序的接口。该类库中定义了SerialBean的构造方法以及初始化串口,从串口读取数据,往串口写入数据以及关闭串口的函数。具体介绍如下: public SerialBean(int PortID) 本函数构造一个指向特定串口的SerialBean,该串口由参数PortID所指定。PortID = 1 表示COM1,PortID = 2 表示COM2,由此类推。 public int Initialize() 本函数初始化所指定的串口并返回初始化结果。如果初始化成功返回1,否则返回-1。初始化的结果是该串口被SerialBean独占性使用,其参数被设置为9600, N, 8, 1。如果串口被成功初始化,则打开一个进程读取从串口传入的数据并将其保存在缓冲区中。 public String ReadPort(int Length) 本函数从串口(缓冲区)中读取指定长度的一个字符串。参数Length指定所返回字符串的长度。 public void WritePort(String Msg) 本函数向串口发送一个字符串。参数Msg是需要发送的字符串。 public void ClosePort() 本函数停止串口检测进程并关闭串口。 SerialBean的源代码如下: package serial; import java.io.*; import java.util.*; import javax.comm.*; /** * * This bean provides some basic functions to implement full dulplex * information exchange through the srial port. * */ public class SerialBean { static String PortName; CommPortIdentifier portId; SerialPort serialPort; static OutputStream out; static InputStream in; SerialBuffer SB; ReadSerial RT; /** * * Constructor * * @param PortID the ID of the serial to be used. 1 for COM1, * 2 for COM2, etc. * */ public SerialBean(int PortID) { PortName = "COM" + PortID; } /** * * This function initialize the serial port for communication. It startss a * thread which consistently monitors the serial port. Any signal capturred * from the serial port is stored into a buffer area. * */ public int Initialize() { int InitSuccess = 1; int InitFail = -1; try { portId = CommPortIdentifier.getPortIdentifier(PortName); try { serialPort = (SerialPort) portId.open("Serial_Communication", 2000); } catch (PortInUseException e) { return InitFail; } //Use InputStream in to read from the serial port, and OutputStream //out to write to the serial port. try { in = serialPort.getInputStream(); out = serialPort.getOutputStream(); } catch (IOException e) { return InitFail; } //Initialize the communication parameters to 9600, 8, 1, none. try { serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); } catch (UnsupportedCommOperationException e) { return InitFail; } } catch (NoSuchPortException e) { return InitFail; } // when successfully open the serial port, create a new serial buffer, // then create a thread that consistently accepts incoming signals from // the serial port. Incoming signals are stored in the serial buffer. SB = new SerialBuffer(); RT = new ReadSerial(SB, in); RT.start(); // return success information return InitSuccess; } /** * * This function returns a string with a certain length from the incomin * messages. * * @param Length The length of the string to be returned. * */ public String ReadPort(int Length) { String Msg; Msg = SB.GetMsg(Length); return Msg; } /** * * This function sends a message through the serial port. * * @param Msg The string to be sent. * */ public void WritePort(String Msg) { int c; try { for (int i = 0; i < Msg.length(); i++) out.write(Msg.charAt(i)); } catch (IOException e) {} } /** * * This function closes the serial port in use. * */ public void ClosePort() { RT.stop(); serialPort.close(); } } SerialBuffer SerialBuffer是本类库中所定义的串口缓冲区,它定义了往该缓冲区中写入数据和从该缓冲区中读取数据所需要的函数。public synchronized String GetMsg(int Length) 本函数从串口(缓冲区)中读取指定长度的一个字符串。参数Length指定所返回字符串的长度。 public synchronized void PutChar(int c) 本函数望串口缓冲区中写入一个字符,参数c 是需要写入的字符。 在往缓冲区写入数据或者是从缓冲区读取数据的时候,必须保证数据的同步,因此GetMsg和PutChar函数均被声明为synchronized并在具体实现中采取措施实现的数据的同步。 SerialBuffer的源代码如下: package serial; /** * * This class implements the buffer area to store incoming data from the serial * port. * */ public class SerialBuffer { private String Content = ""; private String CurrentMsg, TempContent; private boolean available = false; private int LengthNeeded = 1; /** * * This function returns a string with a certain length from the incomin * messages. * * @param Length The length of the string to be returned. * */ public synchronized String GetMsg(int Length) { LengthNeeded = Length; notifyAll(); if (LengthNeeded > Content.length()) { available = false; while (available == false) { try { wait(); } catch (InterruptedException e) { } } } CurrentMsg = Content.substring(0, LengthNeeded); TempContent = Content.substring(LengthNeeded); Content = TempContent; LengthNeeded = 1; notifyAll(); return CurrentMsg; } /** * * This function stores a character captured from the serial port to the * buffer area. * * @param t The char value of the character to be stored. * */ public synchronized void PutChar(int c) { Character d = new Character((char) c); Content = Content.concat(d.toString()); if (LengthNeeded < Content.length()) { available = true; } notifyAll(); } } ReadSerial ReadSerial是一个进程,它不断的从指定的串口读取数据并将其存放到缓冲区中。public ReadSerial(SerialBuffer SB, InputStreamPort) 本函数构造一个ReadSerial进程,参数SB指定存放传入数据的缓冲区,参数Port指定从串口所接收的数据流。 public void run() ReadSerial进程的主函数,它不断的从指定的串口读取数据并将其存放到缓冲区中。 ReadSerial的源代码如下: package serial; import java.io.*; /** * * This class reads message from the specific serial port and save * the message to the serial buffer. * */ public class ReadSerial extends Thread { private SerialBuffer ComBuffer; private InputStream ComPort; /** * * Constructor * * @param SB The buffer to save the incoming messages. * @param Port The InputStream from the specific serial port. * */ public ReadSerial(SerialBuffer SB, InputStream Port) { ComBuffer = SB; ComPort = Port; } public void run() { int c; try { while (true) { c = ComPort.read(); ComBuffer.PutChar(c); } } catch (IOException e) {} } } SerialExample SerialExample是本类库所提供的一个例程。它所实现的功能是打开串口COM1,对其进行初始化,从串口读取信息对其进行处理后将处理结果发送到串口。 import serial.*; import java.io.*; /** * * This is an example of how to use the SerialBean. It opens COM1 and reads * six messages with different length form the serial port. * */ class SerialExample { public static void main(String[] args) { //TO DO: Add your JAVA codes here SerialBean SB = new SerialBean(1); String Msg; SB.Initialize(); for (int i = 5; i <= 10; i++) { Msg = SB.ReadPort(i); SB.WritePort("Reply: " + Msg); } SB.ClosePort(); } } 编译与调试 本类库中使用了Java Communication API (javax.comm)。这是一个Java扩展类库,并不包括在标准的Java SDK当中。如果你尚未安装这个扩展类库的话,你应该从Sun公司的Java站点下载这个类库并将其安装在你的系统上。在所下载的包里面包括一个安装说明,如果你没有正确安装这个类库及其运行环境的话,运行这个程序的时候你会找不到串口。 正确安装Java Communication API并将上述程序编译通过以后,你可以按如下方法测试这个程序。如果你只有一台机器,你可以利用一条RS-232电缆将COM1和COM2连接起来,在COM1上运行SerialExample,在COM2上运行Windows提供的超级终端程序。如果你有两台机器的话,你可以利用一条RS-232电缆将两台机器的COM1(或者是COM2)连接起来,在一端运行例程,另外一端运行Windows提供的超级终端程序。如果有必要的话,可以对SerialExample中所声明的串口进行相应改动。 一、概述 随着手机的逐渐普及,它的主要业务之一“短信”的使用量也水涨船高。但使用手机发短信还有一些不方便的地方,如输入汉字慢、功能有限、手机的存储容量有限等。因此,近几年开始兴起使用电脑向手机发送短信。使用电脑发送短信的方法很多,如通过126、新浪等短信平台通过注册自己的手机号,就可以通过电脑发短信了。但这样做有一些不足,如发短信时电脑必须联入Internet,而且一般使用电脑发短信的费用要比直接使用手机发短信的费用高一些。 当然,还有其它方法发短信。如象126那样租网通或移动的短信服务器,然后通过短信服务器发送短信。这种方式虽然很直接,但是价格昂贵,不是一般人可以承受的(只有象126、新浪这样的服务网站才能用得起)。 最省钱的方法就是到网上去找一个可以免费发短信的软件,我以前使用过一个叫“灵犀机器人”的软件,它们可以有限地免费发送短信,但好象现在也都收费了。这种软件现在越来越少了。 那么是否有折衷的方法,使发短信的费用和手机一样,而且又可以象电脑一样方便地输入、保存、修改和查询短信呢?答案是肯定的,那就是通过数据线将手机和电脑连在一起,使用电脑控制手机发短信。而且这样做电脑无需联入Internet。 二、如何通过数据线控制手机发短信 一般手机的数据线可以通过COM口或USB口和计算机进行通讯。在本文中我们采用带有COM口的数据线,因为控制COM口比控制USB口更容易、更简单。通过Java和COM口进行通讯有很多方法,可以在Java中直接调用系统API,也可以采用第三方的Java库(这些库在底层也是通过调用系统API实现的)。在本文中我们采用第二种方法,也就是通过第三方的Java库来和COM口进行通讯。在网上这种库很多,在本文中介绍了如何使用Sun的Java通讯API和手机进行通讯,可以在Sun的官方网站下载Java通讯API库 。 三、安装Sun的Java通讯API 安装Java通讯API可分为以下几步: 1. 将下载后的压缩文件zip压缩包解压,假设解压目录为C:commapi。并且保证你的机器中已经安装了Java开发包,假设Java开发包安装在了C盘的C:jdk1.5中。 2. 使用如下命令将win32com.dll复制到C:jdk1.5in中。 copy c:commapiwin32com.dll c:jdk1.5in 3.使用如下命令将comm.jar复制到c:jdk1.5lib目录中。 copy c:commapicomm.jar c:jdk1.5lib 4. 使用如下命令将Javax.comm.properties复制到c:jdk`1.5lib中。 copy c:commapiJavax.comm.properties c:jdk1.5lib 这个文件必须被安装在这,否则系统无法发现COM口。 5. 将comm.jar加入到classpath中