使用RXTX实现简单串口通信调试工具

最终效果如下图:
使用RXTX实现简单串口通信调试工具_第1张图片

1、把rxtxParallel.dll、rxtxSerial.dll拷贝到:C:\WINDOWS\system32下。
2、RXTXcomm.jar 添加到项目类库中。

package serialPort;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.TooManyListenersException;

import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.SerialPortEventListener;
import gnu.io.UnsupportedCommOperationException;

/**串口服务类,提供打开、关闭串口,读取、发送串口数据等服务
 */
public class SerialTool {

	private static SerialTool serialTool = null;

	static {
		//在该类被ClassLoader加载时就初始化一个SerialTool对象
		if (serialTool == null) {
			serialTool = new SerialTool();
		}
	}

	//私有化SerialTool类的构造方法,不允许其他类生成SerialTool对象
	private SerialTool() {}	
	/**
	 * 获取提供服务的SerialTool对象
	 * @return serialTool
	 */
	public static SerialTool getSerialTool() {

		if (serialTool == null) {
			serialTool = new SerialTool();
		}
		return serialTool;
	}
	/**
	 * 查找所有可用端口
	 * @return 可用端口名称列表
	 */
	public static final List findPort() {

		//获得当前所有可用串口
		@SuppressWarnings("unchecked")
		Enumeration portList = CommPortIdentifier.getPortIdentifiers();	
		List portNameList = new ArrayList<>();
		//将可用串口名添加到List并返回该List
		while (portList.hasMoreElements()) {
			String portName = portList.nextElement().getName();
			portNameList.add(portName);
		}
		return portNameList;
	}
	/**
	 * 打开串口
	 * @param portName 端口名称
	 * @param baudrate 波特率
	 * @return 串口对象
	 * @throws UnsupportedCommOperationException
	 * @throws PortInUseException
	 * @throws NoSuchPortException
	 */
	public static final SerialPort openPort(String portName, int baudrate) throws UnsupportedCommOperationException, PortInUseException, NoSuchPortException {

		//通过端口名识别端口
		CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
		//打开端口,并给端口名字和一个timeout(打开操作的超时时间)
		CommPort commPort = portIdentifier.open(portName, 2000);
		//判断是不是串口
		if (commPort instanceof SerialPort) {
			SerialPort serialPort = (SerialPort) commPort;
			//设置一下串口的波特率等参数
			serialPort.setSerialPortParams(baudrate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);                              
			return serialPort;
		}
		return null;
	}
	/**
	 * 关闭串口
	 * @param serialport 待关闭的串口对象
	 */
	public static void closePort(SerialPort serialPort) {

		if (serialPort != null) {
			serialPort.close();
			serialPort = null;
		}
	}
	/**
	 * 往串口发送数据
	 * @param serialPort 串口对象
	 * @param order	待发送数据
	 * @throws IOException 
	 */
	public static void sendToPort(SerialPort serialPort, byte[] order) throws IOException  {

		OutputStream out = null;
		out = serialPort.getOutputStream();
		out.write(order);
		out.flush();
		out.close();
	}
	/**
	 * 从串口读取数据
	 * @param serialPort 当前已建立连接的SerialPort对象
	 * @return 读取到的数据
	 * @throws IOException 
	 */
	public static byte[] readFromPort(SerialPort serialPort) throws IOException {

		InputStream in = null;
		byte[] bytes = null;
		try {
			in = serialPort.getInputStream();
			int bufflenth = in.available();	//获取buffer里的数据长度
			while (bufflenth != 0) {                             
				bytes = new byte[bufflenth]; //初始化byte数组为buffer中数据的长度
				in.read(bytes);
				bufflenth = in.available();
			} 
		} catch (IOException e) {
			throw e;
		} finally {
			if (in != null) {
				in.close();
				in = null;
			}
		}
		return bytes;
	}
	/**添加监听器
	 * @param port     串口对象
	 * @param listener 串口监听器
	 * @throws TooManyListenersException 
	 */
	public static void addListener(SerialPort port, SerialPortEventListener listener) throws TooManyListenersException {

		//给串口添加监听器
		port.addEventListener(listener);
		//设置当有数据到达时唤醒监听接收线程
		port.notifyOnDataAvailable(true);
		//设置当通信中断时唤醒中断线程
		port.notifyOnBreakInterrupt(true);
	}
}
package serialPort;

import java.awt.Color;
import java.awt.Font;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TooManyListenersException;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.border.TitledBorder;

import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import gnu.io.UnsupportedCommOperationException;

/**
 * 监测数据显示类
 * @author Zhong
 *
 */
public class SerialView extends JFrame {

	/**
	 */
	private static final long serialVersionUID = 1L;

	//设置window的icon
	Toolkit toolKit = getToolkit();
	Image icon = toolKit.getImage(SerialView.class.getResource("computer.png"));
	DateTimeFormatter df= DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss.SSS");

	private JComboBox commChoice;
	private JComboBox bpsChoice;
	private JButton openSerialButton;
	private JButton sendButton;
	private JTextArea sendArea;
	private JTextArea receiveArea;
	private JButton closeSerialButton;

	private List commList = null;	//保存可用端口号
	private SerialPort serialPort = null;	//保存串口对象

	/**类的构造方法
	 * @param client
	 */
	public SerialView() {

		init();
		TimerTask task = new TimerTask() {  
			@Override  
			public void run() {  
				commList = SerialTool.findPort(); //程序初始化时就扫描一次有效串口
				//检查是否有可用串口,有则加入选项中
				if (commList == null || commList.size()<1) {
					JOptionPane.showMessageDialog(null, "没有搜索到有效串口!", "错误", JOptionPane.INFORMATION_MESSAGE);
				}else{
					commChoice.removeAllItems();
					for (String s : commList) {
						commChoice.addItem(s);
					}
				}
			}
		};
		Timer timer = new Timer();  
		timer.scheduleAtFixedRate(task, 0, 10000);
		listen();

	}
	/**
	 */
	private void listen(){

		//打开串口连接
		openSerialButton.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				//获取串口名称
				String commName = (String) commChoice.getSelectedItem();			
				//获取波特率
				String bpsStr = (String) bpsChoice.getSelectedItem();
				//检查串口名称是否获取正确
				if (commName == null || commName.equals("")) {
					JOptionPane.showMessageDialog(null, "没有搜索到有效串口!", "错误", JOptionPane.INFORMATION_MESSAGE);			
				}else {
					//检查波特率是否获取正确
					if (bpsStr == null || bpsStr.equals("")) {
						JOptionPane.showMessageDialog(null, "波特率获取错误!", "错误", JOptionPane.INFORMATION_MESSAGE);
					}else {
						//串口名、波特率均获取正确时
						int bps = Integer.parseInt(bpsStr);
						try {
							//获取指定端口名及波特率的串口对象
							serialPort = SerialTool.openPort(commName, bps);
							SerialTool.addListener(serialPort, new SerialListener());
							if(serialPort==null) return;
							//在该串口对象上添加监听器
							closeSerialButton.setEnabled(true);
							sendButton.setEnabled(true);
							openSerialButton.setEnabled(false);
							String time=df.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(System.currentTimeMillis()),ZoneId.of("Asia/Shanghai")));
							receiveArea.append(time+" ["+serialPort.getName().split("/")[3]+"] : "+" 连接成功..."+"\r\n");
							receiveArea.setCaretPosition(receiveArea.getText().length());  
						} catch (UnsupportedCommOperationException | PortInUseException | NoSuchPortException | TooManyListenersException e1) {
							e1.printStackTrace();
						}
					}
				}
			}
		});
		//发送数据
		sendButton.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				if(!sendButton.isEnabled())return;
				String message= sendArea.getText();
				//"FE0400030001D5C5"
				try {
					SerialTool.sendToPort(serialPort, hex2byte(message));
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		});
		//关闭串口连接
		closeSerialButton.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				if(!closeSerialButton.isEnabled())return;
				SerialTool.closePort(serialPort);
				String time=df.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(System.currentTimeMillis()),ZoneId.of("Asia/Shanghai")));
				receiveArea.append(time+" ["+serialPort.getName().split("/")[3]+"] : "+" 断开连接"+"\r\n");
				receiveArea.setCaretPosition(receiveArea.getText().length());  
				openSerialButton.setEnabled(true);
				closeSerialButton.setEnabled(false);
				sendButton.setEnabled(false);
			}
		});
	}
	/**
	 * 主菜单窗口显示;
	 * 添加JLabel、按钮、下拉条及相关事件监听;
	 */
	private void init() {

		this.setBounds(WellcomView.LOC_X, WellcomView.LOC_Y, WellcomView.WIDTH, WellcomView.HEIGHT);
		this.setTitle("串口调试");
		this.setIconImage(icon);
		this.setBackground(Color.gray);
		this.setLayout(null);

		Font font =new Font("微软雅黑", Font.BOLD, 16);

		receiveArea=new JTextArea(18, 30);
		receiveArea.setEditable(false);
		JScrollPane receiveScroll = new JScrollPane(receiveArea);
		receiveScroll.setBorder(new TitledBorder("接收区"));
		//滚动条自动出现 FE0400030001D5C5
		receiveScroll.setHorizontalScrollBarPolicy( 
				JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); 
		receiveScroll.setVerticalScrollBarPolicy( 
				JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); 
		receiveScroll.setBounds(52, 20, 680,340);
		this.add(receiveScroll);

		JLabel chuankou=new JLabel(" 串口选择: ");
		chuankou.setFont(font);
		chuankou.setBounds(50, 380, 100,50);
		this.add(chuankou);

		JLabel botelv=new JLabel(" 波 特 率: ");
		botelv.setFont(font);
		botelv.setBounds(290, 380, 100,50);
		this.add(botelv);

		//添加串口选择选项
		commChoice = new JComboBox();	//串口选择(下拉框)
		commChoice.setBounds(145, 390, 100, 30);
		this.add(commChoice);

		//添加波特率选项
		bpsChoice = new JComboBox();	//波特率选择
		bpsChoice.setBounds(380, 390, 100, 30);
		bpsChoice.addItem("1500");
		bpsChoice.addItem("2400");
		bpsChoice.addItem("4800");
		bpsChoice.addItem("9600");
		bpsChoice.addItem("14400");
		bpsChoice.addItem("19500");
		bpsChoice.addItem("115500");
		this.add(bpsChoice);

		//添加打开串口按钮
		openSerialButton = new JButton("连接");
		openSerialButton.setBounds(540, 390, 80, 30);
		openSerialButton.setFont(font);
		openSerialButton.setForeground(Color.darkGray);
		this.add(openSerialButton);

		//添加关闭串口按钮
		closeSerialButton = new JButton("关闭");
		closeSerialButton.setEnabled(false);
		closeSerialButton.setBounds(650, 390, 80, 30);
		closeSerialButton.setFont(font);
		closeSerialButton.setForeground(Color.darkGray);
		this.add(closeSerialButton);

		sendArea=new JTextArea(30,20);
		JScrollPane sendScroll = new JScrollPane(sendArea);
		sendScroll.setBorder(new TitledBorder("发送区"));
		//滚动条自动出现
		sendScroll.setHorizontalScrollBarPolicy( 
				JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); 
		sendScroll.setVerticalScrollBarPolicy( 
				JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); 
		sendScroll.setBounds(52, 450, 500,100);
		this.add(sendScroll);

		sendButton = new JButton("发 送");
		sendButton.setBounds(610, 520, 120, 30);
		sendButton.setFont(font);
		sendButton.setForeground(Color.darkGray);
		sendButton.setEnabled(false);
		this.add(sendButton);

		this.setResizable(false);	//窗口大小不可更改
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setVisible(true);
	}

	/**字符串转16进制
	 * @param hex
	 * @return
	 */
	private byte[] hex2byte(String hex) {
		
		String digital = "0123456789ABCDEF";
		String hex1 = hex.replace(" ", "");
		char[] hex2char = hex1.toCharArray();
		byte[] bytes = new byte[hex1.length() / 2];
		byte temp;
		for (int p = 0; p < bytes.length; p++) {
			temp = (byte) (digital.indexOf(hex2char[2 * p]) * 16);
			temp += digital.indexOf(hex2char[2 * p + 1]);
			bytes[p] = (byte) (temp & 0xff);
		}
		return bytes;
	}
	/**字节数组转16进制
	 * @param b
	 * @return
	 */
	private String printHexString(byte[] b) {

		StringBuffer sbf=new StringBuffer();
		for (int i = 0; i < b.length; i++) { 
			String hex = Integer.toHexString(b[i] & 0xFF); 
			if (hex.length() == 1) { 
				hex = '0' + hex; 
			} 
			sbf.append(hex.toUpperCase()+"  ");
		}
		return sbf.toString().trim();
	}
	/**
	 * 以内部类形式创建一个串口监听类
	 * @author zhong
	 */
	class SerialListener implements SerialPortEventListener {

		/**
		 * 处理监控到的串口事件
		 */
		public void serialEvent(SerialPortEvent serialPortEvent) {

			switch (serialPortEvent.getEventType()) {
			case SerialPortEvent.BI: // 10 通讯中断
				JOptionPane.showMessageDialog(null, "与串口设备通讯中断", "错误", JOptionPane.INFORMATION_MESSAGE);
				break;
			case SerialPortEvent.OE: // 7 溢位(溢出)错误
				break;
			case SerialPortEvent.FE: // 9 帧错误
				break;
			case SerialPortEvent.PE: // 8 奇偶校验错误
				break;
			case SerialPortEvent.CD: // 6 载波检测
				break;
			case SerialPortEvent.CTS: // 3 清除待发送数据
				break;
			case SerialPortEvent.DSR: // 4 待发送数据准备好了
				break;
			case SerialPortEvent.RI: // 5 振铃指示
				break;
			case SerialPortEvent.OUTPUT_BUFFER_EMPTY: // 2 输出缓冲区已清空
				break;
			case SerialPortEvent.DATA_AVAILABLE: // 1 串口存在可用数据
				String time=df.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(System.currentTimeMillis()),ZoneId.of("Asia/Shanghai")));
				byte[] data;//FE0400030001D5C5
				try {
					data = SerialTool.readFromPort(serialPort);
					receiveArea.append(time+" ["+serialPort.getName().split("/")[3]+"] : "+ printHexString(data)+"\r\n");
					receiveArea.setCaretPosition(receiveArea.getText().length());  
				} catch (IOException e) {
					e.printStackTrace();
				}
				break;
			default:
				break;
			}
		}
	}
}
package serialPort;

import java.awt.Color;
import java.awt.Font;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.JFrame;
import javax.swing.JLabel;


/**
 * @author bh
 * 如果运行过程中抛出 java.lang.UnsatisfiedLinkError 错误,
 * 请将rxtx解压包中的 rxtxParallel.dll,rxtxSerial.dll 这两个文件复制到 C:\Windows\System32 目录下即可解决该错误。
 */
public class WellcomView {

	/** 程序界面宽度*/
	public static final int WIDTH = 800;
	/** 程序界面高度*/
	public static final int HEIGHT = 620;
	/** 程序界面出现位置(横坐标) */
	public static final int LOC_X = 200;
	/** 程序界面出现位置(纵坐标)*/
	public static final int LOC_Y = 70;
	
	private JFrame jFrame;

	/**主方法
	 * @param args	//
	 */
	public static void main(String[] args) {
		
		new WellcomView();
	}
	public WellcomView() {
		
		init();
		listen();
	}
	/**
	 */
	private void listen() {
		
		//添加键盘监听器
		jFrame.addKeyListener(new KeyAdapter() {
			public void keyReleased(KeyEvent e) {
				int keyCode = e.getKeyCode();
				if (keyCode == KeyEvent.VK_ENTER) {	//当监听到用户敲击键盘enter键后执行下面的操作
					jFrame.setVisible(false);	//隐去欢迎界面
					new SerialView();	//主界面类(显示监控数据主面板)
				}
			}
		});	
	}
	/**
	 * 显示主界面
	 */
	private void init() {

		jFrame=new JFrame("串口调试");
		jFrame.setBounds(LOC_X, LOC_Y, WIDTH, HEIGHT);	//设定程序在桌面出现的位置
		jFrame.setLayout(null);
		//设置window的icon(这里我自定义了一下Windows窗口的icon图标,因为实在觉得哪个小咖啡图标不好看 = =)
		Toolkit toolKit = jFrame.getToolkit();
		Image icon = toolKit.getImage(WellcomView.class.getResource("computer.png"));
		jFrame.setIconImage(icon); 
		jFrame.setBackground(Color.white);	//设置背景色
		
		JLabel huanyin=new JLabel("欢迎使用串口调试工具");
		huanyin.setBounds(170, 80,600,50);
		huanyin.setFont(new Font("微软雅黑", Font.BOLD, 40));
		jFrame.add(huanyin);
		
		JLabel banben=new JLabel("Version:1.0   Powered By:cyq");
		banben.setBounds(180, 390,500,50);
		banben.setFont(new Font("微软雅黑", Font.ITALIC, 26));
		jFrame.add(banben);
		
		JLabel enter=new JLabel("————点击Enter键进入主界面————");
		enter.setBounds(100, 480,600,50);
		enter.setFont(new Font("微软雅黑", Font.BOLD, 30));
		jFrame.add(enter);
		
		jFrame.setResizable(false);	//窗口大小不可更改
		jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		jFrame.setVisible(true);	//显示窗口
	}
}

无意中发现了一个巨牛的人工智能教程,忍不住分享一下给大家。教程不仅是零基础,通俗易懂,而且非常风趣幽默,像看小说一样!觉得太牛了,所以分享给大家。点这里可以跳转到教程。

你可能感兴趣的:(CommPort,串口通信,java,调试工具,java,串口)