转载:http://blog.sina.com.cn/s/blog_ad0672d601017qjs.html
一、首先搭建平台,我用的是eclipse+rxtx+SWT。
安装eclipse就是安装java包后,然后下载eclipse即可。因为eclipse是绿色的,不用安装,下载即可用。
下载rxtx。在网上下载rxtx包含串口开发的常用函数,是开源社区的一个产物,与sun公司的comm包相同,只是调用前的导入语句由import javax.comm.*变为import gnu.io.*而已.这个包包含一个jar包和几个用于windows平台的几个dll文件。
下载swt。同rxtx一样,包含一个jar包和几个dll文件。详见可到SWT网站查询。
安装rxtx和swt。把上面rxtx和swt涉及到的文件放到你的项目下,然后在该项目下添加外包jar包,关于添加外部jar包的详细的网上有很多.
二、支持图形化编辑
下载安装swt designer。
注册swt designer,重启你的eclipse,在项目菜单里找到属性一项,然后在对话框里找到designer一项,里面有提示,根据提示一次填写,注意的是在想免费使用set designer时,必须选中free 下的swt,同时你的Email必须填写,这样你的注册码才能收到。注册后,会提示你注册码已经发送到你的邮箱了。再次打开项目菜单的属性一项,同样是designer下,和上次填写注册信息一样,只是这此你不必填写信息了。直接把你的注册码写到注册码一项里,然后单击完成即可。
使用图形界面。打开你的项目下的源文件,工作区的左下角会有source/designer选项,单击designer就到了图形编辑状态,要回到非图形状态,同样到工作区的左下角,只是这此单击source选项。有时,左下角会找不到这样的切换选项,这时你选中你要打开的文件,然后右击,在弹出的菜单里由一项是“open in designer”,选中该项,你就会发现工作区的左下角由source/designer切换选项.
三、项目
和一般的java项目一样,编写你的文件。当然可以用图形界面编辑了。
源代码:
第一个是串口对象,支持串口的一些基本操作,
package one;
import java.io.*;import java.util.*;import gnu.io.*;public class Comm implements SerialPortEventListener {
private volatile boolean usingOnePort = false;// 宣告对象打开了(或者正在使用)一个串口
// volatile,保证不同线程得到的值一致这里是监听器和readPort()函数读取
protected volatile boolean receiving = false;// 处于接收串口信息状态
protected volatile boolean sentting = false;// 处于向串口发送信息状态
private CommPortIdentifier portId;// 硬件初始化
private SerialPort serialPort;// 软件和硬件的接口
private OutputStream out;// 向串口发送数据流
private InputStream in;// 接收串口数据流
private String commName = "COM2";
private int bPS = 9600;
private int dataBit = 7;// 和C51通信必须设为7位,不然输出不对
private int stopBit = 1;
private int parityBit = 0;
private PipedInputStream pipin;// 管道流,接收串口信息的
private PipedOutputStream pipout;
public void setName(String commname) { commName=commname; } public void setBps(int bps) { bPS = bps; }
public void setDataBit(int databit) { dataBit = databit; }
public void setStopBit(int stopbit) { stopBit = stopbit; }
public void setParityBit(int paritybit) { parityBit = paritybit; }
public static String[] listPort() { int i = 0; String commList[] = new String[15];// 假定最多为十五个串口 CommPortIdentifier commPort = null; Enumeration portList = null; portList = CommPortIdentifier.getPortIdentifiers(); // iterate through the ports. while (portList.hasMoreElements()) { commPort = (CommPortIdentifier) portList.nextElement(); if (commPort.getPortType() == CommPortIdentifier.PORT_SERIAL) { commList[i] = commPort.getName(); // System.out.print(commList[i] + " ");// 调试语句 i += 1; } } // System.out.println();// 调试语句 return commList; }
public boolean open() { boolean ok = false; try {// 打开硬件资源,获取并口 portId = CommPortIdentifier.getPortIdentifier(commName); } catch (NoSuchPortException e) { System.out.println("Get portid err" + e.getMessage()); return ok; } try {// 打开软件资源,设置进程名称和超时时间 serialPort = (SerialPort) portId.open("comm", 1000); } catch (PortInUseException e) { // System.out.println("Port in used err " + e.getMessage());//调试语句 return ok; } // 很有意思这句,你不设置他也能正确收到信息,但是设置不正确就收不到信息 try {// 设置数据传输参数 serialPort.setSerialPortParams(bPS, dataBit, stopBit, parityBit); } catch (UnsupportedCommOperationException e) { System.out.println("Set port params err " + e.getMessage()); return ok; } try {// 创建输入流 in = serialPort.getInputStream(); } catch (IOException e) { System.out.println("Create instream err " + e.getMessage()); return ok; } try {// 创建输出流 out = serialPort.getOutputStream(); } catch (IOException e) { System.out.println("Create outstream err " + e.getMessage()); return ok; } // 创建管道流 pipin = new PipedInputStream(); pipout = new PipedOutputStream(); try {// 连接管道 pipin.connect(pipout); } catch (IOException e1) { System.out.println("Connect pipin and pipout err " + e1.getMessage()); return ok; }
try {// 监听串口
serialPort.addEventListener(this); } catch (TooManyListenersException e) { System.out.println("SerialPort addEventListener err" + e.getMessage()); return ok; } serialPort.notifyOnDataAvailable(true);// 开启,是否监听有数据 usingOnePort = true;// 已经使用一个串口了 receiving = true;// 处于接收状态 sentting = true;// 处于发送状态// System.out.println(commName+" init is ok");// 调试语句
ok = true; return ok; } private synchronized void read() {// 由打开串口后,增加的监听程序自动调用 final byte[] buf;// 缓冲区 int n;// 检测输入流是否真的有输入 if (usingOnePort) {// usingOnePort针对增加监听时出错识别 try { n = in.available(); } catch (IOException e) { System.out.println("Check inputstream available err " + e.getMessage()); return; } if (receiving) {// 串口处于接收状态 if (n > -1) {// 输入流有数据输入,全部读入到管道 buf = new byte[n]; try {// 数据读到缓存区 in.read(buf); } catch (IOException e) { System.out.println("Start read port err " + e.getMessage()); return; } if (new String(buf).trim() != " ") {// 数据有效,非全为空格 try {// 把数据写到到管道 pipout.write(buf); pipout.flush(); } catch (IOException e) { System.out.println("Write data to pip err " + e.getMessage()); } } } else {// 没有数据输入,不再写入管道了 buf = null; return; } } else {// 非接受状态,跳过N个字节 try { in.skip(n); } catch (IOException e) { e.printStackTrace(); } } } notifyAll();// 完成一次读取,释放该函数,其他程序可以调用了 return; }public String getMessage() { int n = -1; byte[] buf; String msg = null; if (usingOnePort) {// 正在使用一个串口 try { n = pipin.available(); } catch (IOException e) { System.out.println("Check pipin has data or not err " + e.getMessage()); return msg; } if (n > -1) {// 管道里有数据 buf = new byte[n]; try {// 获取数据 pipin.read(buf); } catch (IOException e) { System.out.println("Get message form pipin err " + e.getMessage()); return msg; } msg = new String(buf); if (msg.trim() == " ") msg = null; } } return msg; } public String getMessage(int[] num) { num[0]=0;//表示接收多少个数据了 int n = -1; byte[] buf; String msg = null; if (usingOnePort) {// 正在使用一个串口 try { n = pipin.available(); } catch (IOException e) { System.out.println("Check pipin has data or not err " + e.getMessage()); return msg; } if (n > -1) {// 管道里有数据 num[0]=n; buf = new byte[n]; try {// 获取数据 pipin.read(buf); } catch (IOException e) { System.out.println("Get message form pipin err " + e.getMessage()); return msg; } msg = new String(buf); if (msg.trim() == " "){ num[0]=0; msg = null; } } } return msg; }
private synchronized boolean write(String msg) { boolean ok = false; if (sentting && msg != null) {// 处于发送状态且发送字符为非空 try { out.write(msg.getBytes()); out.flush(); } catch (Exception e) { System.out.println("Write port err " + e.getMessage()); return ok; } } ok = true; notifyAll(); // System.out.println("Writting port");// 调试语句 return ok; } public boolean sentMessage(String msg) { boolean ok = false; if (usingOnePort) {// 正在使用一个串口 write(msg); ok = true; } return ok; } public boolean sentMessage(String msg,int[] num) { boolean ok = false; if (usingOnePort) {// 正在使用一个串口 write(msg); num[0]=msg.length(); ok = true; } return ok; }
public boolean close() { boolean ok = false; if (usingOnePort) {// 只有已经打开一个串口,在使用时,才能关闭串口 try {// 关闭输入输出流 in.close(); out.close(); } catch (Exception e) { System.out.println("Close IO err " + e.getMessage()); return ok; } try {// 关闭管道流 pipout.close(); pipin.close(); } catch (IOException e1) { System.out.println("Close pipedIO err " + e1.getMessage()); return ok; }
serialPort.close();// 关闭串口
usingOnePort = false;// 串口处于关闭状态 // System.out.println("Close port");// 调试语句 ok = true; } return ok; }
public void serialEvent(SerialPortEvent e) {
switch (e.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: { // System.out.println("Have Data"); read();
}
}}
}
***************************************下面是图形界面部分,包括界面和串口动态生成和关闭.
package one;
import java.io.File;
import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.FileWriter;import java.io.IOException;import org.eclipse.swt.SWT;import org.eclipse.swt.events.*;import org.eclipse.swt.graphics.*;import org.eclipse.swt.layout.*;import org.eclipse.swt.layout.GridData;import org.eclipse.swt.widgets.*;public class CommTest {
private Display display;
private Shell shell;
private Comm comm;// 串口对象
private Thread commT;// 串口对象管理线程
// 五个串口参数,因串口对象是动态的存在
private volatile String commname = "COM2";// 串口参数必须在这个类里设置,不然不行private volatile int bps = 9600;
private volatile int databit = 7;// 和C51通信必须设为7位,不然输出不对
private volatile int stopbit = 1;
private volatile int paritybit = 0;
private volatile int what2Do;// 串口关闭、打开或者保持不变标志等
// 以下四个为对串口对象的管理动作
private final int closeComm = 0;// 关闭串口private final int openComm = 1;// 打开串口
private final int changePara = 2;// 改变串口参数
private final int keepStat = 3;// 保持当前状态
// 接收信息
private Text receiveText;// 接收信息文本框private final int disLimit = 1000;// 接收信息显示1000自字符后,清屏
private volatile boolean disHex = false;// 十六进制显示接收的数据
// 发送信息
private Text sentText;// 发送信息文本框private volatile boolean sentHexS = false;// 十六进制格式发送数据
private volatile boolean autoSentS = false;// 是否自动发送
private volatile long autoSentTime = 1000;// 自动发送时间间隔,1秒
// 信息保存
private volatile boolean autoSaveS = false;// 自动保存标志private volatile String fileName = "./comm.txt";// 保存路径(默认)
// 收、发信息量
private volatile int numReceived = 0;// 接收到串口数据的数目private volatile int numSend = 0;// 发送数据的数目
public static void main(String[] args) { try { CommTest window = new CommTest(); window.open(); } catch (Exception e) { System.out.println("window open err " + e.getMessage()); } }
@SuppressWarnings("deprecation") public void open() { display = Display.getDefault();// 打开显示SWT显示功能 createContents();// 创建窗口部件 shell.open();// 打开主窗口 shell.layout();// 主窗口布局 while (!shell.isDisposed()) {// shell没有注销掉 if (!display.readAndDispatch()) display.sleep(); } comm.close();// 关闭串口 commT.stop();// 关闭串口管理线程 shell.dispose();// 关闭主窗口 display.dispose();// 关闭显示swt功能 }
protected void createContents() { shell = new Shell(display,SWT.MIN|SWT.CLOSE);// 由display生成shell final GridLayout gridLayout = new GridLayout(); gridLayout.numColumns = 2; shell.setLayout(gridLayout); shell.setSize(701, 532); shell.setText("串口调试助手"); shell.setImage(new Image(display, "./icon.gif"));// 设置窗口图像
createSetting();
createCommT(); createReceive(); createSent(); final Label label_4 = new Label(shell, SWT.CENTER | SWT.BORDER); label_4.setLayoutData(new GridData(116, SWT.DEFAULT)); final Thread couter = new Thread(new Runnable() {// 统计接收和发送到的数据数目 public void run() { while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } display.syncExec(new Runnable() { public void run() { label_4.setText("RX:" + numReceived + "\tTX:" + numSend); } });}
}
}); couter.start(); label_4.addDisposeListener(new DisposeListener() { @SuppressWarnings("deprecation") public void widgetDisposed(DisposeEvent arg0) { couter.stop(); } });final Label label_5 = new Label(shell, SWT.NONE | SWT.CENTER);
label_5.setLayoutData(new GridData(549, SWT.DEFAULT)); label_5.setText("串口调试助手(SWT) \t版权所有:anyone \t作者:adlong");}
private void createSetting() { final Group group = new Group(shell, SWT.NONE | SWT.CENTER); final GridData gd_group = new GridData(SWT.LEFT, SWT.TOP, false, false); gd_group.heightHint = 160; gd_group.widthHint = 110; group.setLayoutData(gd_group); final GridLayout gridLayout = new GridLayout(); gridLayout.numColumns = 2; group.setText("串口设置"); group.setLayout(gridLayout);
final Label Label = new Label(group, SWT.NONE);
Label.setText("串口");final Combo combo = new Combo(group, SWT.NONE);
combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); String[] list = Comm.listPort();// 最大串口名称列表 int n = list.length;// 最大串口个数 for (int i = 0; i < n - 1; i++) if (list[i] != null) combo.add(list[i]); combo.select(1);// 默认选中第二项,与默认串口一致 combo.addSelectionListener(new SelectionAdapter() {// 选择响应 public void widgetSelected(SelectionEvent e) { int n = combo.getSelectionIndex();// 获取选中的选项位置值 commname = combo.getItem(n);// 获取应该设置的值 what2Do = changePara; } });final Label label_1 = new Label(group, SWT.NONE);
label_1.setText("波特率");final Combo combo_1 = new Combo(group, SWT.NONE);
combo_1.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); combo_1.add("300"); combo_1.add("600"); combo_1.add("1200"); combo_1.add("2400"); combo_1.add("4800"); combo_1.add("9600"); combo_1.add("19200"); combo_1.add("38400"); combo_1.add("43000"); combo_1.add("56000"); combo_1.add("57600"); combo_1.add("115200"); combo_1.select(5);// 默认选中9600项,与默认波特率一致 combo_1.addSelectionListener(new SelectionAdapter() {// 选择响应 public void widgetSelected(SelectionEvent e) { int n = combo_1.getSelectionIndex();// 获取选中的选项位置值 bps = Integer.valueOf(combo_1.getItem(n));// 获取应该设置的值 what2Do = changePara; } });final Label label_2 = new Label(group, SWT.NONE);
label_2.setText("校验位");final Combo combo_2 = new Combo(group, SWT.NONE);
combo_2.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); combo_2.add("无"); combo_2.add("奇校验"); combo_2.add("偶校验"); combo_2.select(0);// 默认选中校验位为0 combo_2.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { int n = combo_2.getSelectionIndex(); paritybit = n; what2Do = changePara; } });final Label label_3 = new Label(group, SWT.NONE);
label_3.setText("数据位");final Combo combo_3 = new Combo(group, SWT.NONE);
combo_3.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); combo_3.add("6"); combo_3.add("7"); combo_3.add("8"); combo_3.select(1);// 默认值为7位 combo_3.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { int n = combo_3.getSelectionIndex(); databit = Integer.valueOf(combo_3.getItem(n)); what2Do = changePara; } });final Label label_4 = new Label(group, SWT.NONE);
label_4.setText("停止位");final Combo combo_4 = new Combo(group, SWT.NONE);
combo_4.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); combo_4.add("1"); combo_4.add("2"); combo_4.select(0);// 默认值为1位停止位 combo_4.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { int n = combo_4.getSelectionIndex(); stopbit = Integer.valueOf(combo_4.getItem(n)); what2Do = changePara; } });Canvas sign = new Canvas(group, SWT.NONE);
sign.setLayoutData(new GridData(33, 27)); final GC gc = new GC(sign); sign.addPaintListener(new PaintListener() {// 软件打开时,红色,表示打开串口 public void paintControl(PaintEvent e) { gc.setBackground(display.getSystemColor(SWT.COLOR_RED)); gc.fillOval(0, 0, 25, 25); } });final Button button = new Button(group, SWT.NONE);
button.setLayoutData(new GridData(56, SWT.DEFAULT)); button.setText("关闭串口"); button.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { if (button.getText() == "关闭串口") {// 关闭串口 gc.setBackground(display.getSystemColor(SWT.COLOR_BLACK)); button.setText("打开串口"); what2Do = closeComm; } else {// 打开串口 button.setText("关闭串口"); gc.setBackground(display.getSystemColor(SWT.COLOR_RED)); what2Do = openComm; numReceived = 0;// 计数清空 numSend = 0; } gc.fillOval(0, 0, 25, 25); } }); }private void createCommT() {// comm的生成和消释都在此线程
commT = new Thread(new Runnable() { public void run() { boolean ok = false; comm = new Comm(); comm.setName(commname);// 首次执行,创建串口对象 comm.setBps(bps); comm.setDataBit(databit); comm.setParityBit(paritybit); comm.setStopBit(stopbit); if (comm.open()) ok = true; what2Do = keepStat;// 保持状态不变// 管理动态串口对象
while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } switch (what2Do) { case changePara: {// 改变参数 comm.close(); ok = false; comm = new Comm(); comm.setName(commname); comm.setBps(bps); comm.setDataBit(databit); comm.setParityBit(paritybit); comm.setStopBit(stopbit); if (comm.open()) ok = true; what2Do = keepStat;// 保持状态不变 break; } case closeComm: {// 关闭串口 comm.close(); ok = false; what2Do = keepStat; break; } case openComm: {// 打开串口 comm = new Comm(); comm.setName(commname); comm.setBps(bps); comm.setDataBit(databit); comm.setParityBit(paritybit); comm.setStopBit(stopbit); if (comm.open()) ok = true;// 打开一个串口后,便能存取串口了 what2Do = keepStat; break; } case keepStat:// 保持已有的状态 break; } if (ok) {// 如果串口对象存在并且打开着 int[] numR = new int[1]; final String str = comm.getMessage(numR); numReceived += numR[0]; display.syncExec(new Runnable() { public void run() { if (disHex) {// 十六进制显示 for (int i = 0; i < str.length(); i++) receiveText.append(Integer .toHexString(str.charAt(i)) + " "); } else receiveText.append(str); }});
if (autoSaveS) {// 如果自动保存 FileWriter writer = null;// 初始化写文件流 try { writer = new FileWriter(fileName, true);// 杜绝存在filename为空的情况 } catch (IOException e) { e.printStackTrace(); } try {// 保存信息 writer.write(str); writer.flush(); } catch (IOException e) { e.printStackTrace(); } try {//关闭写文件流 writer.close(); } catch (IOException e) { e.printStackTrace(); } } } } } }); commT.start(); }private void createReceive() {
receiveText = new Text(shell, SWT.BORDER | SWT.WRAP | SWT.V_SCROLL); GridData grLayout = new GridData(SWT.LEFT, SWT.FILL, false, false, 1, 5); grLayout.widthHint = 530; receiveText.setLayoutData(grLayout);final Composite composite = new Composite(shell, SWT.NONE);
composite.setLayoutData(new GridData(115, 54)); final GridLayout gridLayout_1 = new GridLayout(); gridLayout_1.numColumns = 2; composite.setLayout(gridLayout_1);final Button button = new Button(composite, SWT.NONE);
button.setText("清空接收"); button.addSelectionListener(new SelectionAdapter() {// 清空文本框 public void widgetSelected(SelectionEvent arg0) { receiveText.setText(""); } });final Label label = new Label(composite, SWT.NONE);
label.setLayoutData(new GridData(38, SWT.DEFAULT)); label.setText("接收区");final Button button_1 = new Button(composite, SWT.NONE);
button_1.setText("停止显示"); button_1.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent arg0) { if (button_1.getText() == "停止显示") { button_1.setText("继续显示"); if (what2Do != closeComm)// 串口存在对象时才能操作对象 comm.receiving = false; } else { button_1.setText("停止显示"); if (what2Do != closeComm)// 串口存在对象时才能操作对象 comm.receiving = true; }}
});final Composite composite_1 = new Composite(shell, SWT.NONE);
composite_1.setLayoutData(new GridData(115, 41)); composite_1.setLayout(new GridLayout());final Button button_2 = new Button(composite_1, SWT.CHECK);
button_2.setText("自动清空");final Button button_3 = new Button(composite_1, SWT.CHECK);
button_3.setText("十六进制显示"); final Composite composite_2 = new Composite(shell, SWT.NONE); composite_2.setLayoutData(new GridData(114, 29)); final GridLayout gridLayout_2 = new GridLayout(); gridLayout_2.numColumns = 2; composite_2.setLayout(gridLayout_2);final Button button_4 = new Button(composite_2, SWT.CHECK);
button_4.setLayoutData(new GridData(62, SWT.DEFAULT)); button_4.setText("自动保存");final Button button_5 = new Button(composite_2, SWT.NONE);
button_5.setLayoutData(new GridData(34, SWT.DEFAULT)); button_5.setText("保存");// 默认为”保存“final Composite composite_3 = new Composite(shell, SWT.NONE);
final GridData gd_composite_3 = new GridData(SWT.LEFT, SWT.TOP, false, false); gd_composite_3.heightHint = 28; gd_composite_3.widthHint = 113; composite_3.setLayoutData(gd_composite_3); composite_3.setLayout(new GridLayout());final Text text = new Text(composite_3, SWT.BORDER);// 用于显示保存路径
final GridData gd_text = new GridData(SWT.FILL, SWT.FILL, true, false); gd_text.widthHint = 106; gd_text.heightHint = 13; text.setLayoutData(gd_text); text.setText(fileName);// 默认手动保存路径为:自动保存路径button_5.addSelectionListener(new SelectionAdapter() {// 监听按钮按下
@Override public void widgetSelected(SelectionEvent arg0) { if (autoSaveS) {// 如果为自动保存 FileDialog changeFN = new FileDialog(shell, SWT.OPEN); String tempN =null; tempN=changeFN.open(); if (tempN != null){ // 文件名存在才改名 fileName=tempN; text.setText(fileName);// 更新提示信息 } } else {// 手动保存 FileDialog saveF = new FileDialog(shell, SWT.SAVE); String tempN = saveF.open(); if (tempN != null) {// 文件名存在---保存信息 text.setText(fileName);// 更新提示信息 FileOutputStream save = null; try { save = new FileOutputStream(tempN); } catch (IOException e) { e.printStackTrace(); } try {// 保存当前信息 save .write(receiveText.getText() .getBytes()); save.flush(); } catch (IOException e) { e.printStackTrace(); } try { save.close(); } catch