一年半前在jbuilder2006下写过串口通信程序,最近做一个GPS-SMS项目,用到串口通信,在JBUILDER 配置了comm.jar却发现找不到端口,经过几番查找才发现自己配置的路径错了,浪费了不少时间,有必要记下来,防止下次出错,此次还暴露了自己看文章一目十行,走马观花的毛病。
Javax.comm是Sun公司提供的,用于开发平台独立的通讯应用程序的扩展API。(ps:这里javax的x很准确地表明了它是一个扩展包,而不是核心包(core package),但由于历史原因,javax下的并不都是扩展包,比如swing包已经是Java核心架构的一部分了,不过为了与Java1.1编码兼容,仍使用javax.swing。)javax.comm可以访问RS232接口(串口)及有限制地访问IEEE-1284(并口)。
下载
需要到其官方主页http://java.sun.com/products/javacomm/下载这个API,目前的最新版本是3.0。不过可惜的是,Sun目前没有推出此API在Windows平台下的3.0版本,主页上列出的三个版本,分别是运行在x86和Sparc结构下的Solaris系统,以及x86下的Linux系统。要下载Windows版本只能去寻找较老的版本了。我所找到的2个网址是http://llk.media.mit.edu/projects/cricket/software/javaSerial.zip(两个文件夹里面有所需的3个文件),http://mdubuc.freeshell.org/Jolt/javacomm20-win32.zip和(完整的2.0版本,还有examples)。
安装
这里的所谓安装就是把三个重要的文件放到指定的目录下。
将下载的文件解压缩后,在/javacomm20-win32/commapi目录下有必需的三个文件comm.jar,javax.comm. properties和win32comm.dll。将文件comm.jar拷贝到%JAVA_HOME%/jre/lib/ext;文件 javax.comm. properties拷贝到%JAVA_HOME%/jre/lib; 文件win32comm.dll拷贝到%JAVA_HOME%/bin。注意%JAVA_HOME%是jdk的路径,而非jre。
API
在javax.comm下有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 javax.comm.*; 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(); while (portList.hasMoreElements()) { portId = (CommPortIdentifier) portList.nextElement(); if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) { // if (portId.getName().equals("COM1")) { if (portId.getName().equals("/dev/term/a")) { SimpleRead reader = new SimpleRead(); } } } } public SimpleRead() { try { serialPort = (SerialPort) portId.open("SimpleReadApp", 2000); } catch (PortInUseException e) {} try { inputStream = serialPort.getInputStream(); } catch (IOException e) {} try { serialPort.addEventListener(this); } 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); readThread.start(); } public void run() { try { Thread.sleep(20000); } catch (InterruptedException e) {} } //串口事件 public void serialEvent(SerialPortEvent event) { switch(event.getEventType()) { case SerialPortEvent.BI: case SerialPortEvent.OE: case SerialPortEvent.FE: case SerialPortEvent.PE: case SerialPortEvent.CD: case SerialPortEvent.CTS: case SerialPortEvent.DSR: case SerialPortEvent.RI: case SerialPortEvent.OUTPUT_BUFFER_EMPTY: break; case SerialPortEvent.DATA_AVAILABLE: byte[] readBuffer = new byte[20]; try { while (inputStream.available() > 0) { int numBytes = inputStream.read(readBuffer); } System.out.print(new String(readBuffer)); } catch (IOException e) {} break; } } }
(PS:不推荐Thread的这种用法,详见《Core Java VolumeII》)
写串口的例程
把字符串"Hello, world!/n"写到系统的第一个串口
import java.io.*; import java.util.*; import javax.comm.*; public class SimpleWrite { static Enumeration portList; static CommPortIdentifier portId; static String messageString = "Hello, world!/n"; static SerialPort serialPort; static OutputStream outputStream; public static void main(String[] args) { portList = CommPortIdentifier.getPortIdentifiers(); while (portList.hasMoreElements()) { portId = (CommPortIdentifier) portList.nextElement(); if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) { // if (portId.getName().equals("COM1")) { if (portId.getName().equals("/dev/term/a")) { try { serialPort = (SerialPort) portId.open("SimpleWriteApp", 2000); } catch (PortInUseException e) {} try { outputStream = serialPort.getOutputStream(); } catch (IOException e) {} try { serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); } catch (UnsupportedCommOperationException e) {} try { outputStream.write(messageString.getBytes()); } catch (IOException e) {} } } } } }
上面两个例程都经过了简化,在打开端口,并且传输结束后没有关闭数据流和串口。在例程中我们看
到CommPortIdentifier提供了打开通讯端口的方法open,但却没有相应关闭端口的方法,关闭端口需要调
用javax.comm.CommPort类的close()。CommPort是这个包中的一个高级抽象,它定义了端口可作的各种事
情:获取I/O数据流对象,控制缓冲区大小,调整输入的处理。
============================================================
public SerialReader(HashMap params) { serialParams = params; init(); } private void init() { try { // 参数初始化 int timeout = Integer.parseInt(serialParams.get(PARAMS_TIMEOUT).toString()); int rate = Integer.parseInt(serialParams.get(PARAMS_RATE).toString()); int dataBits = Integer.parseInt(serialParams.get(PARAMS_DATABITS).toString()); int stopBits = Integer.parseInt(serialParams.get(PARAMS_STOPBITS).toString()); int parity = Integer.parseInt(serialParams.get(PARAMS_PARITY).toString()); delayRead = Integer.parseInt(serialParams.get(PARAMS_DELAY).toString()); String port = serialParams.get(PARAMS_PORT).toString(); // 打开端口 portId = CommPortIdentifier.getPortIdentifier(port); serialPort = (SerialPort) portId.open("SerialReader", timeout); inputStream = serialPort.getInputStream(); serialPort.addEventListener(this); serialPort.notifyOnDataAvailable(true); serialPort.setSerialPortParams(rate, dataBits, stopBits, parity); } catch (PortInUseException e) { System.out.println("端口已经被占用!"); e.printStackTrace(); } catch (TooManyListenersException e) { System.out.println("端口监听者过多!"); e.printStackTrace(); } catch (UnsupportedCommOperationException e) { System.out.println("端口操作命令不支持!"); e.printStackTrace(); } catch (NoSuchPortException e) { System.out.println("端口不存在!"); e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } Thread readThread = new Thread(this); readThread.start(); } public void run() { try { Thread.sleep(100); } catch (InterruptedException e) { } } public void serialEvent(SerialPortEvent event) { try { // 等待1秒钟让串口把数据全部接收后在处理 Thread.sleep(delayRead); System.out.print("serialEvent[" + event.getEventType() + "] "); } catch (InterruptedException e) { e.printStackTrace(); } switch (event.getEventType()) { case SerialPortEvent.BI: // 10 case SerialPortEvent.OE: // 7 case SerialPortEvent.FE: // 9 case SerialPortEvent.PE: // 8 case SerialPortEvent.CD: // 6 case SerialPortEvent.CTS: // 3 case SerialPortEvent.DSR: // 4 case SerialPortEvent.RI: // 5 case SerialPortEvent.OUTPUT_BUFFER_EMPTY: // 2 break; case SerialPortEvent.DATA_AVAILABLE: // 1 try { // 多次读取,将所有数据读入 // while (inputStream.available() > 0) { // numBytes = inputStream.read(readBuffer); // } numBytes = inputStream.read(readBuffer); changeMessage(readBuffer, numBytes); } catch (IOException e) { e.printStackTrace(); } break; } } // 通过observer pattern将收到的数据发送给observer // 将buffer中的空字节删除后再发送更新消息,通知观察者 public void changeMessage(byte[] message, int length) { setChanged(); byte[] temp = new byte[length]; System.arraycopy(message, 0, temp, 0, length); // System.out.println("msg[" + numBytes + "]: [" + new String(temp) + "]"); notifyObservers(temp); } static void listPorts() { Enumeration portEnum = CommPortIdentifier.getPortIdentifiers(); while (portEnum.hasMoreElements()) { CommPortIdentifier portIdentifier = (CommPortIdentifier) portEnum.nextElement(); System.out.println(portIdentifier.getName() + " - " + getPortTypeName(portIdentifier.getPortType())); } } static String getPortTypeName(int portType) { switch (portType) { case CommPortIdentifier.PORT_I2C: return "I2C"; case CommPortIdentifier.PORT_PARALLEL: return "Parallel"; case CommPortIdentifier.PORT_RAW: return "Raw"; case CommPortIdentifier.PORT_RS485: return "RS485"; case CommPortIdentifier.PORT_SERIAL: return "Serial"; default: return "unknown type"; } } public static HashSet<CommPortIdentifier> getAvailableSerialPorts() { HashSet<CommPortIdentifier> h = new HashSet<CommPortIdentifier>(); Enumeration thePorts = CommPortIdentifier.getPortIdentifiers(); while (thePorts.hasMoreElements()) { CommPortIdentifier com = (CommPortIdentifier) thePorts.nextElement(); switch (com.getPortType()) { case CommPortIdentifier.PORT_SERIAL: try { CommPort thePort = com.open("CommUtil", 50); thePort.close(); h.add(com); } catch (PortInUseException e) { System.out.println("Port, " + com.getName() + ", is in use."); } catch (Exception e) { System.out.println("Failed to open port " + com.getName() + e); } } } return h; } }
---------- 3 -----------------
package serial; import java.util.Observable; import java.util.Observer; class CommDataObserver implements Observer { String name; public CommDataObserver(String name) { this.name = name; } public void update(Observable o, Object arg) { System.out.println("[" + name + "] GetMessage:/n [" + new String((byte[]) arg) + "]"); } }
转自:
http://blog.sina.com.cn/s/blog_3c6ecea90100ccra.html