java 串口通信

之前使用c++/C#时,都是可以直接调用串口API的,java没有源生支持串口,只能通过第三方扩展

这里使用的是javacomm20-win32.zip这个实现的库,可自行网上下载

使用时,先解压,再复制三个文件到jdk中,可执行下面的命令进行复制

copy comm.jar "%JAVA_HOME%\jre\lib\ext\"
copy javax.comm.properties "%JAVA_HOME%\jre\lib\"
copy win32com.dll "%JAVA_HOME%\jre\bin\"

在eclipse中在引入comm.jar即可使用串口api

虽然这中方式确实能够解决java调用串口问题,但是需要复制第三方库到jdk,总感觉不爽。

为了在不修改jdk的情况下,也能够使用串口功能,就需要修改引用串口库的方式了。

在我的项目中,

将comm.jar、javax.comm.properties、win32com.dll放在了src/main/resources/commapi/下

第一步是引入comm.jar

这一步相对比较容易,只要将这个jar包引入项目即可,如果使用maven进行管理的话,可参考如下方式引用

<dependency>
	<groupId>javax.comm</groupId>
	<artifactId>comm</artifactId>
	<version>1.0</version>
	<scope>system</scope>
	<systemPath>${project.basedir}/src/main/resources/commapi/comm.jar</systemPath>
</dependency>

第二部是引入win32com.dll

这一步实际上需要将win32com.dll所在的路径加入到path路径,但是不想通过直接修改环境变量,而是通过程序动态添加的方式实现,实现方式参考如下代码

	public static void addDir(String s){
		try {
			logger.info(s);
			Field field = ClassLoader.class.getDeclaredField("sys_paths");
			field.setAccessible(true);
			String[] paths = (String[]) field.get(null);
			for (int i = 0; i < paths.length; i++) {
				if (s.equals(paths[i])) {
					return;
				}
			}
			String[] tmp = new String[paths.length + 1];
			System.arraycopy(paths, 0, tmp, 0, paths.length);
			tmp[paths.length] = s;
			field.set(null, tmp);
		} catch (Exception e) {
			logger.error("加载path异常", e);
		}
	}

	/**
	 * 动态添加系统path变量,用于动态加载DLL
	 */
	public static void systemPathInit() {
		// 添加串口win32com.dll路径
		addDir(PathUtil.getResourcePath() + "\\commapi");
		// 添加一个站位用的classpath,用于串口comm.jar寻找javax.comm.properties。详见comm.jar->CommPortIdentifier->findPropFile();
		System.setProperty("java.class.path", System.getProperty("java.class.path") + ";" + PathUtil.getResourcePath() + "\\commapi\\comm.jar");
	}

设置java.class.path,主要是因为,comm.jar源码中,会通过java.class.path找到comm.jar的路径,再在comm.jar的相同路径中寻找javax.comm.properties文件。这样就可以让comm.jar找到javax.comm.properties文件。

systemPathInit()方法要在程序启动时,使用串口前先调用一次

以下是串口使用的参考代码


import java.io.InputStream;
import java.io.OutputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;



public abstract class CommunicateBase {
	
	private Logger logger = LoggerFactory.getLogger(getClass());

    protected OutputStream outputStream;
    protected InputStream inputStream;
    
	/**
	 * 打开
	 * @return
	 */
	public abstract String open();
	
	/**
	 * 
	 * @param cmd
	 */
	public void send(String cmd) {
		try {
			if (outputStream == null) {
				logger.error("连接未打开");
				return;
			}
			outputStream.write(cmd.getBytes());
		} catch (Exception e) {
			logger.error("发送异常", e);
		}
	}
	
	
	/**
	 * 读取数据
	 * @return
	 */
	public String read(int len){
		try {
			
			if (inputStream == null) {
				logger.error("连接未打开");
				return "";
			}
			
			int off = 0;//偏移
			byte[] readBuffer = new byte[len];//读数据缓存
			while (inputStream.available() > 0) {//循环读取接收的数据
				
				// 读取数据
				int readCount = inputStream.read(readBuffer, off, len);
				
				// 修正偏移量
				off += readCount;
				len -= readCount;
				
				ThreadUtil.sleep(50);
			}
			
			// 转换为字符串
			return new String(readBuffer).trim();
		} catch (Exception e) {
			logger.error("接收异常", e);
			return "";
		}
	}
	
	
	/**
	 * 关闭
	 */
	public abstract void close();
}

import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

import javax.comm.CommDriver;
import javax.comm.CommPort;
import javax.comm.CommPortIdentifier;
import javax.comm.SerialPort;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sun.comm.Win32Driver;

/**
 * 串口工具类
 * 
 *
 */
public class Uart extends CommunicateBase{
	protected Logger logger = LoggerFactory.getLogger(Uart.class);

	private CommPortIdentifier portId;
	private SerialPort serialPort;
	
	private String comm = "COM9";// 端口号
	private int baudRate = 9600;// 波特率
    private int timeout = 100;//open 端口时的等待时间  
	
    public Uart(String comm, int baudRate) {
    	this.comm = comm;
    	this.baudRate = baudRate;
    }
    
    
    /**
     * 通过反射清空CommPortIdentifier中的串口列表
     * 可以解决动态添加串口时,也能够识别出的功能,
     */
	private void commClear() {
		try {
			// 清空api链表
			Constructor con = CommPortIdentifier.class.getDeclaredConstructor(
					String.class, CommPort.class, int.class, CommDriver.class);
			con.setAccessible(true);
			Object o = (CommPortIdentifier) con
					.newInstance(null, null, 0, null);// 获取对象
			Field f = CommPortIdentifier.class.getDeclaredField("masterIdList");// 根据key获取参数
			f.setAccessible(true);
			f.set(o, null);
			
			// 重新初始化串口api
			new Win32Driver().initialize();
		} catch (Exception e) {
			logger.error("清除串口异常", e);
		}

	}

	/**
	 * 打开串口
	 * @return
	 */
	@Override
	public String open(){
		try {
			close();//先保证串口为关闭状态
			PathUtil.printAllPath();
			
			// 清空串口链表
			commClear();
			
			portId = CommPortIdentifier.getPortIdentifier(comm);
			serialPort = (SerialPort) portId.open("Uart", timeout);

			outputStream = serialPort.getOutputStream();
			inputStream = serialPort.getInputStream();
			
			serialPort.setSerialPortParams(baudRate,// 波特率
					SerialPort.DATABITS_8, // 数据位
					SerialPort.STOPBITS_1,// 停止位
					SerialPort.PARITY_NONE);// 校验位
			
			logger.info("串口已打开");
			return "成功";
		} catch (Exception e) {
			logger.error("串口打开失败", e);
			return e.getMessage();
		}

	}
	
	/**
	 * 关闭串口
	 */
	@Override
	public void close() {
		if (serialPort != null) {
			serialPort.close();
			portId = null;
			serialPort = null;
			outputStream = null;
			inputStream = null;
			logger.info("串口已关闭");
		}
	}

	
	/**
	 * 是否连接
	 * @return
	 */
	public boolean isConnected() {
		
		if (portId != null) {
			
			logger.info(portId.getCurrentOwner());
			return portId.getCurrentOwner().equals("Port currently not owned");
		}
		return false;
		
	}
	
	
	public static void main(String[] args) throws InterruptedException{
		
		PathUtil.addDir(PathUtil.getResourcePath() + "\\commapi");
		
		Uart uart = new Uart("COM4", 9600);
		uart.open();
		
		uart.send("aa");
		
		Thread.sleep(1000);
		
		System.out.println(uart.read(50));
		
		uart.close();
		
	}

}

 

转载于:https://my.oschina.net/u/2450094/blog/794309

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值