Java串口开发教程(javax.comm)

串口通讯是什么?

串口通信(Serial Communication), 是指外设和计算机间,通过数据信号线 、地线、控制线等,按位进行传输数据的一种通讯方式(注意,是一位一位的传输,区别于并口通讯,传输慢)。这种通信方式使用的数据线少,在远距离通信中可以节约通信成本,但其传输速度比并行传输低。。。虽然说慢,不如并,但不代表就要抛弃,某些项目还是很有用的,例如公司最近营养探索馆的一个血压探测仪,就是串口通讯,需要开发串口。

Javax.comm是什么?

Javax.comm是Sun公司提供的,用于开发平台独立的通讯应用程序的扩展API,简单的说,就是年代久远的串口开发包。(ps:这里javax的x很准确地表明了它是一个扩展包,而不是核心包(core package),但由于历史原因,javax下的并不都是扩展包,比如swing包已经是Java核心架构的一部分了,不过为了与Java1.1编码兼容,仍使用javax.swing。)javax.comm可以访问RS232接口(串口)及有限制地访问IEEE-1284(并口)。

版本注意事项

  • javax.comm开发中,由于win32com.dll只能工作在32位模式下,所以基本就是说串口通讯只支持jdk16~1.7~1.8的32位版本(已测试)
  • spring boot 2.0以上只能工作在jdk1.8以上,所以这次我是从jdk1.7-32bit转到jdk1.8-32bit
  • win32com.dll这个东西太老了,又没有64位的。网上说建议使用RxTx,支持Linux 32/64、Windows 32/64,这个晚点有空再研究。http://mfizz.com/oss/rxtx-for-java

资源打包下载

  1. csdn
    https://download.csdn.net/download/moshowgame/10403876
  2. 百度网盘
    链接: https://pan.baidu.com/s/1uii3uoWwHp71MlkxjKjZag 密码: 957h

文件清单

  • comm.jar(javax.comm的包,添加到)
  • javax.comm.properties(javax.comm的配置,,放到jdk的bin和lib目录)
  • win32com.dll(串口调试DLL,放到jdk的bin和lib目录)
  • vspd(虚拟串口调试工具,可以虚拟COM1和COM2,监听COM1并发送数据,COM2就可以收到,本质上是映射了COM1<->COM2相互通讯)
  • 串口调试精灵,可以打开端口,设置一样的参数之后可以进行调试

用虚拟串口驱动添加COM1+COM2的端口

这里写图片描述

打开串口调试精灵监听端口

这里写图片描述
当然要根据设备的参数设置串口号,波特率,校验位,数据位,停止位,然后打开串口。(这里我的设备信息如下)

这里写图片描述

接着是启动程序,我这里是spring boot2,当然,可能是其他程序,反正,启动就对了。

这里写图片描述

我的程序分及部分,一个是initCom,就是传入参数初始化端口,
一个是writeCom,就是写数据到端口上(由于这是进行了虚拟端口映射,所以写到COM1的数据,监听COM2就可以收到了)

然后就是数据接收了,那个类中我设定58个字节就截取一次,放到StringList里面,

然后有个nowDataIndex来控制获取数据的下标。当然,可能也有更好的方法,但是当时就是这么想得。

其他的可以仔细看下代码,一些地方我已经给出了注释

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

import javax.comm.CommPortIdentifier;
import javax.comm.PortInUseException;
import javax.comm.SerialPort;
import javax.comm.SerialPortEvent;
import javax.comm.SerialPortEventListener;
import javax.comm.UnsupportedCommOperationException;

public class DSerialPort implements Runnable, SerialPortEventListener {

    private String appName = "串口通讯测试";
    private int timeout = 2000;// open 端口时的等待时间,延迟时间(毫秒数)
    private int threadTime = 0;

    private CommPortIdentifier commPort;
    private SerialPort serialPort;
    private InputStream inputStream;
    private OutputStream outputStream;
    //当前接收COM口的数据
    public static String receiptDataString="";
    //当前已取的数组下标
    public static int nowDataIndex =0;
    //当前接收COM口的数据自动切割成StringList
    public static ArrayList<String> receiptDataList=new ArrayList<String>();

    /**
     * @方法名称 :listPort
     * @功能描述 :列出所有可用的串口
     * @返回值类型 :void
     */
    @SuppressWarnings("rawtypes")
    public void listPort() {
        CommPortIdentifier cpid;
        Enumeration en = CommPortIdentifier.getPortIdentifiers();//获得端口列表

        System.out.println("now to list all Port of this PC:" + en);

        while (en.hasMoreElements()) {
            cpid = (CommPortIdentifier) en.nextElement();
            if (cpid.getPortType() == CommPortIdentifier.PORT_SERIAL) {//判断是否串口
                System.out.println(cpid.getName() + ", " + cpid.getCurrentOwner());
            }
//          if (cpid.getPortType() == CommPortIdentifier.PORT_PARALLEL) {//判断是否并行口
//              System.out.println(cpid.getName() + ", " + cpid.getCurrentOwner());
//          }
        }
    }

    /**
     * @方法名称 :selectPort
     * @功能描述 :选择一个端口,比如:COM1
     * @返回值类型 :void
     * @param portName
     */
    @SuppressWarnings("rawtypes")
    public void selectPort(String portName) {

        this.commPort = null;
        CommPortIdentifier cpid;
        Enumeration en = CommPortIdentifier.getPortIdentifiers();

        while (en.hasMoreElements()) {
            cpid = (CommPortIdentifier) en.nextElement();
            if (cpid.getPortType() == CommPortIdentifier.PORT_SERIAL && cpid.getName().equals(portName)) {
                this.commPort = cpid;
                break;
            }
        }

        openPort();
    }

    /**
     * @方法名称 :openPort
     * @功能描述 :打开SerialPort
     * @返回值类型 :void
     */
    private void openPort() {
        if (commPort == null)
            log(String.format("无法找到串口!"));
        else {
            log("端口选择成功,当前端口:" + commPort.getName() + ",现在实例化 SerialPort:");

            try {
                serialPort = (SerialPort) commPort.open(appName, timeout);//打开端口
                serialPort.setSerialPortParams(2400, SerialPort.DATABITS_7, SerialPort.STOPBITS_1,SerialPort.PARITY_EVEN);
                //设置参数
                log("实例 SerialPort 成功!");
            } catch (PortInUseException e) {
                throw new RuntimeException(String.format("端口'%1$s'正在使用中!", commPort.getName()));
            }catch (UnsupportedCommOperationException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * @方法名称 :checkPort
     * @功能描述 :检查端口是否正确连接
     * @返回值类型 :void
     */
    public void checkPort() {
        if (commPort == null)
            throw new RuntimeException("没有选择端口,请使用 " + "selectPort(String portName) 方法选择端口");

        if (serialPort == null) {
            throw new RuntimeException("SerialPort 对象无效!");
        }
    }

    /**
     * @方法名称 :write
     * @功能描述 :向端口发送数据,请在调用此方法前 先选择端口,并确定SerialPort正常打开!
     * @返回值类型 :void
     * @param message
     */
    public void write(String message) {
        checkPort();

        try {
            outputStream = new BufferedOutputStream(serialPort.getOutputStream());
        } catch (IOException e) {
            throw new RuntimeException("获取端口的OutputStream出错:" + e.getMessage());
        }

        try {
            outputStream.write(message.getBytes());
            log("信息发送成功!");
        } catch (IOException e) {
            throw new RuntimeException("向端口发送信息时出错:" + e.getMessage());
        } finally {
            try {
                outputStream.close();
            } catch (Exception e) {
            }
        }
    }
    public void write(byte[]  message) {
        checkPort();

        try {
            outputStream = new BufferedOutputStream(serialPort.getOutputStream());
        } catch (IOException e) {
            throw new RuntimeException("获取端口的OutputStream出错:" + e.getMessage());
        }

        try {
            outputStream.write(message);
            log("信息发送成功!");
        } catch (IOException e) {
            throw new RuntimeException("向端口发送信息时出错:" + e.getMessage());
        } finally {
            try {
                outputStream.close();
            } catch (Exception e) {
            }
        }
    }
    /**
     * @方法名称 :startRead
     * @功能描述 :开始监听从端口中接收的数据
     * @返回值类型 :void
     * @param time
     *            监听程序的存活时间,单位为秒,0 则是一直监听
     */
    public void startRead(int time) {
        checkPort();

        try {
            inputStream = new BufferedInputStream(serialPort.getInputStream());
        } catch (IOException e) {
            throw new RuntimeException("获取端口的InputStream出错:" + e.getMessage());
        }

        try {
            serialPort.addEventListener(this);//向SerialPort对象中添加串口事件监听器
        } catch (TooManyListenersException e) {
            throw new RuntimeException(e.getMessage());
        }

        serialPort.notifyOnDataAvailable(true);//设置串口有数据的事件true有效,false无效

        log(String.format("开始监听来自'%1$s'的数据--------------", commPort.getName()));
        if (time > 0) {
            this.threadTime = time * 1000;
//          this.threadTime = time* 10;
            Thread t = new Thread(this);
            t.start();
            log(String.format("监听程序将在%1$d秒后关闭。。。。", threadTime));
        }
    }
    /**
     * @方法名称 :startRead
     * @功能描述 :开始监听从端口中接收的数据
     * @返回值类型 :void
     * @param time
     *            监听程序的存活时间,单位为秒,0 则是一直监听
     */
    public void read(int time) {
        checkPort();

        try {
            inputStream = new BufferedInputStream(serialPort.getInputStream());
        } catch (IOException e) {
            throw new RuntimeException("获取端口的InputStream出错:" + e.getMessage());
        }

        try {
            serialPort.addEventListener(this);//向SerialPort对象中添加串口事件监听器
        } catch (TooManyListenersException e) {
            throw new RuntimeException(e.getMessage());
        }

        serialPort.notifyOnDataAvailable(true);//设置串口有数据的事件true有效,false无效

        log(String.format("开始监听来自'%1$s'的数据--------------", commPort.getName()));
        if (time > 0) {
            this.threadTime = time * 1000;
//          this.threadTime = time* 10;
            Thread t = new Thread(this);
            t.start();
            log(String.format("监听程序将在%1$d秒后关闭。。。。", threadTime));
        }
    }
    /**
     * @方法名称 :close
     * @功能描述 :关闭 SerialPort
     * @返回值类型 :void
     */
    public void close() {
        serialPort.close();
        serialPort = null;
        commPort = null;
    }

    public void log(String msg) {
        System.out.println(appName + " --> " + msg);
    }

    /**
     * 数据接收的监听处理函数
     */
    @Override
    public void serialEvent(SerialPortEvent arg0) {
        switch (arg0.getEventType()) {
        case SerialPortEvent.BI:/* Break interrupt,通讯中断 */
        case SerialPortEvent.OE:/* Overrun error,溢位错误 */
        case SerialPortEvent.FE:/* Framing error,传帧错误 */
        case SerialPortEvent.PE:/* Parity error,校验错误 */
        case SerialPortEvent.CD:/* Carrier detect,载波检测 */
        case SerialPortEvent.CTS:/* Clear to send,清除发送 */
        case SerialPortEvent.DSR:/* Data set ready,数据设备就绪 */
        case SerialPortEvent.RI:/* Ring indicator,响铃指示 */
        case SerialPortEvent.OUTPUT_BUFFER_EMPTY:/*
                                                     * Output buffer is
                                                     * empty,输出缓冲区清空
                                                     */
            break;
        case SerialPortEvent.DATA_AVAILABLE:/*
                                             * Data available at the serial
                                             * port,端口有可用数据。读到缓冲数组,输出到终端
                                             */
            byte[] readBuffer = new byte[1024];
            String readStr = "";
            String s2 = "";

            try {

                while (inputStream.available() > 0) {
                    inputStream.read(readBuffer);
                    readStr += new String(readBuffer).trim();
                }

                s2 = new String(readBuffer).trim();
                //接收的精华再这里
                //1。readStr为当次读入的,一般设备是1位1位读,模拟的时候就很多位,但是不重要
                //2。receiptDataString是用来缓存输入字符串的
                //3。receiptDataString.length()==XX这里可以设定你要接受的长度,然后接收指定数据
                //4。超长或者不符合长度,你可以看情况抛弃数据或者清空,或者累加
                //5。接受成功的数据,放入receiptDataList供获取调用
                //6。nowDataIndex是当前数组的下标,可以参考PortController中对数据获取的方法
                log("接收端口COM->返回数据(长度为" + readStr.length() + "):数据" + s2);
                receiptDataString+=readStr;
                log("receiptDataString->长度" + receiptDataString.length() + "),数据" + receiptDataString);
                if(receiptDataString.length()==58){
                    receiptDataList.add(receiptDataString);
                    receiptDataString="";
                    log("校验通过,数据接收成功");
                }else if(receiptDataString.length()>100){
                    receiptDataString="";
                }
            } catch (IOException e) {
            }
        }
    }

    @Override
    public void run() {
        try {
            Thread.sleep(threadTime);
            serialPort.close();
            log(String.format("端口'%1$s'监听关闭了!", commPort.getName()));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        DSerialPort sp = new DSerialPort();
        //sp.listPort();
        sp.selectPort("COM1");
        sp.write("210.36.16.166");
        //sp.write("2");
        //sp.startRead(120);
    }
}

import java.util.ArrayList;
import org.microservice.tcbj.yytsg.checkcollectsys.util.ApiReturnObject;
import org.microservice.tcbj.yytsg.checkcollectsys.util.ApiReturnUtil;
import org.microservice.tcbj.yytsg.checkcollectsys.util.DSerialPort;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/port")
public class PortController {
        DSerialPort sp = new DSerialPort();

        String nowCom="";
        @RequestMapping("/index")
        public String index() {
            return "/port";
        }
        @RequestMapping("/initCom")
        public ApiReturnObject initCom(String comCode){
            sp.listPort();
            sp.selectPort(comCode);
            sp.startRead(0);
            nowCom=comCode;
            DSerialPort.receiptDataString="";
            DSerialPort.nowDataIndex=0;
            DSerialPort.receiptDataList=new ArrayList<String>();
            return ApiReturnUtil.success("");
        }
        @RequestMapping("/writeCom")
        public ApiReturnObject  writeCom(String comData){
            System.out.println("#comData->"+comData);
            sp.checkPort();
            //sp.selectPort(comCode);
            sp.write(comData);
            return ApiReturnUtil.success("传输数据成功");
        }
        @RequestMapping("/getDataFromCom")
        public ApiReturnObject   getDataFromCom(){
            String dataStr="";
            System.out.println(DSerialPort.receiptDataList.size());
            if(DSerialPort.receiptDataList.size()>DSerialPort.nowDataIndex){
                dataStr=DSerialPort.receiptDataList.get(DSerialPort.nowDataIndex);
                DSerialPort.nowDataIndex++;
            }
            return ApiReturnUtil.success(dataStr);
        }
        public static int receiptNumber=0;
        @RequestMapping("/closeCom")
        public ApiReturnObject  closeCom(String comCode,String comData){
            System.out.println("#comCode->"+comCode+"#comData->"+comData);
            if(sp!=null) sp.close();
            return ApiReturnUtil.success("传输数据成功");
        }
        @RequestMapping("/startScan")
        public ApiReturnObject  startScan(){
            //开始
            byte[] b = new byte[3];
            b[0] = (byte) 0x02;
            b[1] = (byte) 0x53;
            b[2] = (byte) 0x03;
            //sp.selectPort(nowCom);
            sp.write(b);
            return ApiReturnUtil.success("启动扫描成功");
        }
        @RequestMapping("/stopScan")
        public ApiReturnObject  stopScan(){
            byte[] b2 = new byte[3];
            b2[0] = (byte) 0x02;
            b2[1] = (byte) 0x52;
            b2[2] = (byte) 0x03;
            //sp.selectPort(nowCom);
            sp.write(b2);
            return ApiReturnUtil.success("停止扫描成功");
        }
}
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 博客之星2020 设计师:CY__ 返回首页