使用Java實現(xiàn)串口通信

最新版程序請查看 《使用Java實現(xiàn)串口通信(二)》

1.介紹

使用Java實現(xiàn)的串口通信程序爆捞,支持十六進(jìn)制數(shù)據(jù)的發(fā)送與接收穷娱。
源碼下載地址:http://download.csdn.net/download/kong_gu_you_lan/10015803
效果圖如下:

串口通信

2.RXTXcomm

Java串口通信依賴的jar包RXTXcomm.jar
下載地址:http://download.csdn.net/detail/kong_gu_you_lan/9611334

內(nèi)含32位與64位版本
使用方法:
拷貝 RXTXcomm.jar 到 JAVA_HOME\jre\lib\ext目錄中卵史;
拷貝 rxtxSerial.dll 到 JAVA_HOME\jre\bin目錄中咆霜;
拷貝 rxtxParallel.dll 到 JAVA_HOME\jre\bin目錄中糖荒;
JAVA_HOME為jdk安裝路徑

3.串口通信管理

SerialPortManager實現(xiàn)了對串口通信的管理,包括查找可用端口鼓寺、打開關(guān)閉串口、發(fā)送接收數(shù)據(jù)勋磕。

package com.yang.serialport.manage;

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;

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 com.yang.serialport.exception.NoSuchPort;
import com.yang.serialport.exception.NotASerialPort;
import com.yang.serialport.exception.PortInUse;
import com.yang.serialport.exception.ReadDataFromSerialPortFailure;
import com.yang.serialport.exception.SendDataToSerialPortFailure;
import com.yang.serialport.exception.SerialPortInputStreamCloseFailure;
import com.yang.serialport.exception.SerialPortOutputStreamCloseFailure;
import com.yang.serialport.exception.SerialPortParameterFailure;
import com.yang.serialport.exception.TooManyListeners;

/**
 * 串口管理
 * 
 * @author yangle
 */
public class SerialPortManager {

    /**
     * 查找所有可用端口
     * 
     * @return 可用端口名稱列表
     */
    @SuppressWarnings("unchecked")
    public static final ArrayList<String> findPort() {
        // 獲得當(dāng)前所有可用串口
        Enumeration<CommPortIdentifier> portList = CommPortIdentifier
                .getPortIdentifiers();
        ArrayList<String> portNameList = new ArrayList<String>();
        // 將可用串口名添加到List并返回該List
        while (portList.hasMoreElements()) {
            String portName = portList.nextElement().getName();
            portNameList.add(portName);
        }
        return portNameList;
    }

    /**
     * 打開串口
     * 
     * @param portName
     *            端口名稱
     * @param baudrate
     *            波特率
     * @return 串口對象
     * @throws SerialPortParameterFailure
     *             設(shè)置串口參數(shù)失敗
     * @throws NotASerialPort
     *             端口指向設(shè)備不是串口類型
     * @throws NoSuchPort
     *             沒有該端口對應(yīng)的串口設(shè)備
     * @throws PortInUse
     *             端口已被占用
     */
    public static final SerialPort openPort(String portName, int baudrate)
            throws SerialPortParameterFailure, NotASerialPort, NoSuchPort,
            PortInUse {
        try {
            // 通過端口名識別端口
            CommPortIdentifier portIdentifier = CommPortIdentifier
                    .getPortIdentifier(portName);
            // 打開端口妈候,設(shè)置端口名與timeout(打開操作的超時時間)
            CommPort commPort = portIdentifier.open(portName, 2000);
            // 判斷是不是串口
            if (commPort instanceof SerialPort) {
                SerialPort serialPort = (SerialPort) commPort;
                try {
                    // 設(shè)置串口的波特率等參數(shù)
                    serialPort.setSerialPortParams(baudrate,
                            SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
                            SerialPort.PARITY_NONE);
                } catch (UnsupportedCommOperationException e) {
                    throw new SerialPortParameterFailure();
                }
                return serialPort;
            } else {
                // 不是串口
                throw new NotASerialPort();
            }
        } catch (NoSuchPortException e1) {
            throw new NoSuchPort();
        } catch (PortInUseException e2) {
            throw new PortInUse();
        }
    }

    /**
     * 關(guān)閉串口
     * 
     * @param serialport
     *            待關(guān)閉的串口對象
     */
    public static void closePort(SerialPort serialPort) {
        if (serialPort != null) {
            serialPort.close();
            serialPort = null;
        }
    }

    /**
     * 向串口發(fā)送數(shù)據(jù)
     * 
     * @param serialPort
     *            串口對象
     * @param order
     *            待發(fā)送數(shù)據(jù)
     * @throws SendDataToSerialPortFailure
     *             向串口發(fā)送數(shù)據(jù)失敗
     * @throws SerialPortOutputStreamCloseFailure
     *             關(guān)閉串口對象的輸出流出錯
     */
    public static void sendToPort(SerialPort serialPort, byte[] order)
            throws SendDataToSerialPortFailure,
            SerialPortOutputStreamCloseFailure {
        OutputStream out = null;
        try {
            out = serialPort.getOutputStream();
            out.write(order);
            out.flush();
        } catch (IOException e) {
            throw new SendDataToSerialPortFailure();
        } finally {
            try {
                if (out != null) {
                    out.close();
                    out = null;
                }
            } catch (IOException e) {
                throw new SerialPortOutputStreamCloseFailure();
            }
        }
    }

    /**
     * 從串口讀取數(shù)據(jù)
     * 
     * @param serialPort
     *            當(dāng)前已建立連接的SerialPort對象
     * @return 讀取到的數(shù)據(jù)
     * @throws ReadDataFromSerialPortFailure
     *             從串口讀取數(shù)據(jù)時出錯
     * @throws SerialPortInputStreamCloseFailure
     *             關(guān)閉串口對象輸入流出錯
     */
    public static byte[] readFromPort(SerialPort serialPort)
            throws ReadDataFromSerialPortFailure,
            SerialPortInputStreamCloseFailure {
        InputStream in = null;
        byte[] bytes = null;
        try {
            in = serialPort.getInputStream();
            // 獲取buffer里的數(shù)據(jù)長度
            int bufflenth = in.available();
            while (bufflenth != 0) {
                // 初始化byte數(shù)組為buffer中數(shù)據(jù)的長度
                bytes = new byte[bufflenth];
                in.read(bytes);
                bufflenth = in.available();
            }
        } catch (IOException e) {
            throw new ReadDataFromSerialPortFailure();
        } finally {
            try {
                if (in != null) {
                    in.close();
                    in = null;
                }
            } catch (IOException e) {
                throw new SerialPortInputStreamCloseFailure();
            }
        }
        return bytes;
    }

    /**
     * 添加監(jiān)聽器
     * 
     * @param port
     *            串口對象
     * @param listener
     *            串口監(jiān)聽器
     * @throws TooManyListeners
     *             監(jiān)聽類對象過多
     */
    public static void addListener(SerialPort port,
            SerialPortEventListener listener) throws TooManyListeners {
        try {
            // 給串口添加監(jiān)聽器
            port.addEventListener(listener);
            // 設(shè)置當(dāng)有數(shù)據(jù)到達(dá)時喚醒監(jiān)聽接收線程
            port.notifyOnDataAvailable(true);
            // 設(shè)置當(dāng)通信中斷時喚醒中斷線程
            port.notifyOnBreakInterrupt(true);
        } catch (TooManyListenersException e) {
            throw new TooManyListeners();
        }
    }
}

4.程序主窗口

/*
 * MainFrame.java
 *
 * Created on 2016.8.19
 */

package com.yang.serialport.ui;

import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;

import java.awt.Color;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

import com.yang.serialport.exception.NoSuchPort;
import com.yang.serialport.exception.NotASerialPort;
import com.yang.serialport.exception.PortInUse;
import com.yang.serialport.exception.SendDataToSerialPortFailure;
import com.yang.serialport.exception.SerialPortOutputStreamCloseFailure;
import com.yang.serialport.exception.SerialPortParameterFailure;
import com.yang.serialport.exception.TooManyListeners;
import com.yang.serialport.manage.SerialPortManager;
import com.yang.serialport.utils.ByteUtils;
import com.yang.serialport.utils.ShowUtils;

/**
 * 主界面
 * 
 * @author yangle
 */
public class MainFrame extends JFrame {

    /**
     * 程序界面寬度
     */
    public static final int WIDTH = 500;

    /**
     * 程序界面高度
     */
    public static final int HEIGHT = 360;

    private JTextArea dataView = new JTextArea();
    private JScrollPane scrollDataView = new JScrollPane(dataView);

    // 串口設(shè)置面板
    private JPanel serialPortPanel = new JPanel();
    private JLabel serialPortLabel = new JLabel("串口");
    private JLabel baudrateLabel = new JLabel("波特率");
    private JComboBox commChoice = new JComboBox();
    private JComboBox baudrateChoice = new JComboBox();

    // 操作面板
    private JPanel operatePanel = new JPanel();
    private JTextField dataInput = new JTextField();
    private JButton serialPortOperate = new JButton("打開串口");
    private JButton sendData = new JButton("發(fā)送數(shù)據(jù)");

    private List<String> commList = null;
    private SerialPort serialport;

    public MainFrame() {
        initView();
        initComponents();
        actionListener();
        initData();
    }

    private void initView() {
        // 關(guān)閉程序
        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        // 禁止窗口最大化
        setResizable(false);

        // 設(shè)置程序窗口居中顯示
        Point p = GraphicsEnvironment.getLocalGraphicsEnvironment()
                .getCenterPoint();
        setBounds(p.x - WIDTH / 2, p.y - HEIGHT / 2, WIDTH, HEIGHT);
        this.setLayout(null);

        setTitle("串口通訊");
    }

    private void initComponents() {
        // 數(shù)據(jù)顯示
        dataView.setFocusable(false);
        scrollDataView.setBounds(10, 10, 475, 200);
        add(scrollDataView);

        // 串口設(shè)置
        serialPortPanel.setBorder(BorderFactory.createTitledBorder("串口設(shè)置"));
        serialPortPanel.setBounds(10, 220, 170, 100);
        serialPortPanel.setLayout(null);
        add(serialPortPanel);

        serialPortLabel.setForeground(Color.gray);
        serialPortLabel.setBounds(10, 25, 40, 20);
        serialPortPanel.add(serialPortLabel);

        commChoice.setFocusable(false);
        commChoice.setBounds(60, 25, 100, 20);
        serialPortPanel.add(commChoice);

        baudrateLabel.setForeground(Color.gray);
        baudrateLabel.setBounds(10, 60, 40, 20);
        serialPortPanel.add(baudrateLabel);

        baudrateChoice.setFocusable(false);
        baudrateChoice.setBounds(60, 60, 100, 20);
        serialPortPanel.add(baudrateChoice);

        // 操作
        operatePanel.setBorder(BorderFactory.createTitledBorder("操作"));
        operatePanel.setBounds(200, 220, 285, 100);
        operatePanel.setLayout(null);
        add(operatePanel);

        dataInput.setBounds(25, 25, 235, 20);
        operatePanel.add(dataInput);

        serialPortOperate.setFocusable(false);
        serialPortOperate.setBounds(45, 60, 90, 20);
        operatePanel.add(serialPortOperate);

        sendData.setFocusable(false);
        sendData.setBounds(155, 60, 90, 20);
        operatePanel.add(sendData);
    }

    @SuppressWarnings("unchecked")
    private void initData() {
        commList = SerialPortManager.findPort();
        // 檢查是否有可用串口,有則加入選項中
        if (commList == null || commList.size() < 1) {
            ShowUtils.warningMessage("沒有搜索到有效串口挂滓!");
        } else {
            for (String s : commList) {
                commChoice.addItem(s);
            }
        }

        baudrateChoice.addItem("9600");
        baudrateChoice.addItem("19200");
        baudrateChoice.addItem("38400");
        baudrateChoice.addItem("57600");
        baudrateChoice.addItem("115200");
    }

    private void actionListener() {
        serialPortOperate.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                if ("打開串口".equals(serialPortOperate.getText())
                        && serialport == null) {
                    openSerialPort(e);
                } else {
                    closeSerialPort(e);
                }
            }
        });

        sendData.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                sendData(e);
            }
        });
    }

    /**
     * 打開串口
     * 
     * @param evt
     *            點擊事件
     */
    private void openSerialPort(java.awt.event.ActionEvent evt) {
        // 獲取串口名稱
        String commName = (String) commChoice.getSelectedItem();
        // 獲取波特率
        int baudrate = 9600;
        String bps = (String) baudrateChoice.getSelectedItem();
        baudrate = Integer.parseInt(bps);

        // 檢查串口名稱是否獲取正確
        if (commName == null || commName.equals("")) {
            ShowUtils.warningMessage("沒有搜索到有效串口苦银!");
        } else {
            try {
                serialport = SerialPortManager.openPort(commName, baudrate);
                if (serialport != null) {
                    dataView.setText("串口已打開" + "\r\n");
                    serialPortOperate.setText("關(guān)閉串口");
                }
            } catch (SerialPortParameterFailure e) {
                e.printStackTrace();
            } catch (NotASerialPort e) {
                e.printStackTrace();
            } catch (NoSuchPort e) {
                e.printStackTrace();
            } catch (PortInUse e) {
                e.printStackTrace();
                ShowUtils.warningMessage("串口已被占用!");
            }
        }

        try {
            SerialPortManager.addListener(serialport, new SerialListener());
        } catch (TooManyListeners e) {
            e.printStackTrace();
        }
    }

    /**
     * 關(guān)閉串口
     * 
     * @param evt
     *            點擊事件
     */
    private void closeSerialPort(java.awt.event.ActionEvent evt) {
        SerialPortManager.closePort(serialport);
        dataView.setText("串口已關(guān)閉" + "\r\n");
        serialPortOperate.setText("打開串口");
    }

    /**
     * 發(fā)送數(shù)據(jù)
     * 
     * @param evt
     *            點擊事件
     */
    private void sendData(java.awt.event.ActionEvent evt) {
        // 輸入框直接輸入十六進(jìn)制字符赶站,長度必須是偶數(shù)
        String data = dataInput.getText().toString();
        try {
            SerialPortManager.sendToPort(serialport,
                    ByteUtils.hexStr2Byte(data));
        } catch (SendDataToSerialPortFailure e) {
            e.printStackTrace();
        } catch (SerialPortOutputStreamCloseFailure e) {
            e.printStackTrace();
        }
    }

    private class SerialListener implements SerialPortEventListener {
        /**
         * 處理監(jiān)控到的串口事件
         */
        public void serialEvent(SerialPortEvent serialPortEvent) {

            switch (serialPortEvent.getEventType()) {

            case SerialPortEvent.BI: // 10 通訊中斷
                ShowUtils.errorMessage("與串口設(shè)備通訊中斷");
                break;

            case SerialPortEvent.OE: // 7 溢位(溢出)錯誤

            case SerialPortEvent.FE: // 9 幀錯誤

            case SerialPortEvent.PE: // 8 奇偶校驗錯誤

            case SerialPortEvent.CD: // 6 載波檢測

            case SerialPortEvent.CTS: // 3 清除待發(fā)送數(shù)據(jù)

            case SerialPortEvent.DSR: // 4 待發(fā)送數(shù)據(jù)準(zhǔn)備好了

            case SerialPortEvent.RI: // 5 振鈴指示

            case SerialPortEvent.OUTPUT_BUFFER_EMPTY: // 2 輸出緩沖區(qū)已清空
                break;

            case SerialPortEvent.DATA_AVAILABLE: // 1 串口存在可用數(shù)據(jù)
                byte[] data = null;
                try {
                    if (serialport == null) {
                        ShowUtils.errorMessage("串口對象為空幔虏!監(jiān)聽失敗贝椿!");
                    } else {
                        // 讀取串口數(shù)據(jù)
                        data = SerialPortManager.readFromPort(serialport);
                        dataView.append(ByteUtils.byteArrayToHexString(data,
                                true) + "\r\n");
                    }
                } catch (Exception e) {
                    ShowUtils.errorMessage(e.toString());
                    // 發(fā)生讀取錯誤時顯示錯誤信息后退出系統(tǒng)
                    System.exit(0);
                }
                break;
            }
        }
    }

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new MainFrame().setVisible(true);
            }
        });
    }
}

5.寫在最后

本文Demo下載

GitHub傳送門

歡迎同學(xué)們吐槽評論想括,如果你覺得本篇博客對你有用,那么就留個言或者點下喜歡吧(^-^)

感謝:
http://www.cnblogs.com/Dreamer-1/p/5523046.html

最新版程序請查看 《使用Java實現(xiàn)串口通信(二)》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末烙博,一起剝皮案震驚了整個濱河市瑟蜈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌渣窜,老刑警劉巖踪栋,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異图毕,居然都是意外死亡夷都,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進(jìn)店門予颤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來囤官,“玉大人,你說我怎么就攤上這事蛤虐〉骋” “怎么了?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵驳庭,是天一觀的道長刑顺。 經(jīng)常有香客問我,道長饲常,這世上最難降的妖魔是什么蹲堂? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮贝淤,結(jié)果婚禮上柒竞,老公的妹妹穿的比我還像新娘。我一直安慰自己播聪,他們只是感情好朽基,可當(dāng)我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布布隔。 她就那樣靜靜地躺著,像睡著了一般稼虎。 火紅的嫁衣襯著肌膚如雪衅檀。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天霎俩,我揣著相機(jī)與錄音哀军,去河邊找鬼。 笑死茸苇,一個胖子當(dāng)著我的面吹牛排苍,可吹牛的內(nèi)容都是我干的沦寂。 我是一名探鬼主播学密,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼传藏!你這毒婦竟也來了腻暮?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤毯侦,失蹤者是張志新(化名)和其女友劉穎哭靖,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體侈离,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡试幽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了卦碾。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铺坞。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖洲胖,靈堂內(nèi)的尸體忽然破棺而出济榨,到底是詐尸還是另有隱情,我是刑警寧澤绿映,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布擒滑,位于F島的核電站,受9級特大地震影響叉弦,放射性物質(zhì)發(fā)生泄漏丐一。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一淹冰、第九天 我趴在偏房一處隱蔽的房頂上張望钝诚。 院中可真熱鬧,春花似錦榄棵、人聲如沸凝颇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拧略。三九已至芦岂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間垫蛆,已是汗流浹背禽最。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留袱饭,地道東北人川无。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像虑乖,于是被迫代替她去往敵國和親懦趋。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,697評論 2 351

推薦閱讀更多精彩內(nèi)容