以前使用c++/C#時,都是能夠直接調用串口API的,java沒有源生支持串口,只能經過第三方擴展java
這裏使用的是javacomm20-win32.zip這個實現的庫,可自行網上下載c++
使用時,先解壓,再複製三個文件到jdk中,可執行下面的命令進行復制api
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,總感受不爽。eclipse
爲了在不修改jdk的狀況下,也可以使用串口功能,就須要修改引用串口庫的方式了。maven
在個人項目中,ide
將comm.jar、javax.comm.properties、win32com.dll放在了src/main/resources/commapi/下工具
第一步是引入comm.jarthis
這一步相對比較容易,只要將這個jar包引入項目便可,若是使用maven進行管理的話,可參考以下方式引用spa
<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(); } }