Java程序与RSR232串口通讯小练手(转载)

一直以来都是在学习J2EE方面的应用系统开发,从未想过用JAVA来编写硬件交互程序,不过自己就是喜欢尝试一些未曾接触的新东西。在网上搜索了些资源,了解到JAVA写串口通讯的还是蛮多的,那么便着手准备开发调试环境。软件程序开发环境搭建不成问题,可这硬件环境就有点犯难啦。更何况自己用的是笔记本哪来的串口呀,再说要是真拿这串口硬件来自己也不会弄,随即想到了虚拟机,觉得这东西应该也有虚拟的吧,果真跟自己的猜测一样还真有这东西,顺便也下载了个串口小助手做为调试之用。下面就先看看软件环境的搭建:

1.下载comm.jar、win32com.dll和javax.comm.properties。 (附件提供下载)

介绍:comm.jar提供了通讯用的java API,win32com.dll提供了供comm.jar调用的本地驱动接口,javax.comm.properties是这个驱动的类配置文件

2.拷贝javacomm.jar到X:\jre\lib\ext目录下面;

3.拷贝javax.comm.properties到X:\jre\lib目录下面;

4.拷贝win32com.dll到X:\jre\bin目录下面;

5.更新下IDE里面的JDK环境,如下图:

接着是硬件虚拟环境安装虚拟串口,这里我用的是VSPD6.0(附件提供下载),安装好后启动VSPD添加我们所需要的端口,注意这里是按组的方式添加的,例如COM1和COM2是一组同时添加,以此类推。如下图所示:

所有环境都准备好后,先来简单认识下comm.jar的内容。单从comm API的javadoc来看,SUM提供给我们的只有区区以下13个类或接口,具体如下:

javax.comm.CommDriver

javax.comm.CommPort javax.comm.ParallelPort

javax.comm.SerialPort javax.comm.CommPortIdentifier

javax.comm.CommPortOwnershipListener

javax.comm.ParallelPortEvent javax.comm.SerialPortEvent

javax.comm.ParallelPortEventListener (extends java.util.EventListener)

javax.comm.SerialPortEventListener (extends java.util.EventListener)

javax.comm.NoSuchPortException javax.comm.PortInUseException

javax.comm.UnsupportedCommOperationException

这些类和接口命名一看便知其意,就不做一一介绍啦,可以到官网或网上找到更详细的信息。下面先测试下所搭建的环境是否可用,主要代码如下:

Enumeration<?> en = CommPortIdentifier.getPortIdentifiers();
CommPortIdentifier portId;
while (en.hasMoreElements()) {
    portId = (CommPortIdentifier) en.nextElement();
    // 如果端口类型是串口,则打印出其端口信息
    if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
        System.out.println(portId.getName());
    }
}

运行代码后,控制台有输出正确的端口(如下图),说明所有环境正常可进行下步工作,否则请检查。

最后要解决的就是与串口数据交互的问题。在这个问题上,最主要的难点就是数据读取,因为我们不知道端口什么时候会有数据到来,也不知数据长度如何。通常,串口通信应用程序有两种模式,一种是实现SerialPortEventListener接口,监听各种串口事件并作相应处理;另一种就是建立一个独立的接收线程专门负责数据的接收。参考众多老前辈的代码后,下面就采用第一种方式写了个简单的助手程序,具体的实现请看详细代码,如下:

package com.elkan1788.view;

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
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 javax.comm.CommPortIdentifier;
import javax.comm.NoSuchPortException;
import javax.comm.PortInUseException;
import javax.comm.SerialPort;
import javax.comm.SerialPortEvent;
import javax.comm.SerialPortEventListener;
import javax.comm.UnsupportedCommOperationException;
import javax.imageio.ImageIO;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;

public class JavaRs232 extends JFrame implements ActionListener, SerialPortEventListener {

    /**
     * JDK Serial Version UID
     */
    private static final long serialVersionUID = -7270865686330790103L;

    protected int WIN_WIDTH = 380;

    protected int WIN_HEIGHT = 300;

    private JComboBox<?> portCombox, rateCombox, dataCombox, stopCombox, parityCombox; 

    private Button openPortBtn, closePortBtn, sendMsgBtn;

    private TextField sendTf;

    private TextArea readTa;

    private JLabel statusLb;

    private String portname, rate, data, stop, parity;

    protected CommPortIdentifier portId;

    protected Enumeration<?> ports;

    protected List<String> portList;

    protected SerialPort serialPort;

    protected OutputStream outputStream = null; 

protected InputStream inputStream = null; 

protected String mesg;

protected int sendCount, reciveCount;

    /**
     * 默认构造函数
     */
    public JavaRs232() {
        super("Java RS-232串口通信测试程序   凡梦星尘");
        setSize(WIN_WIDTH, WIN_HEIGHT);
        setLocationRelativeTo(null);
        Image icon = null;
        try {
            icon = ImageIO.read(JavaRs232.class.getResourceAsStream("/res/rs232.png"));
        } catch (IOException e) {
            showErrMesgbox(e.getMessage());
        }
        setIconImage(icon);
        setResizable(false);
        scanPorts();
        initComponents();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
    }

    /**
     * 初始化各UI组件
     * @since 2012-3-22 下午11:56:39
     */
    public void initComponents() {
        // 共用常量
        Font lbFont = new Font("微软雅黑", Font.TRUETYPE_FONT, 14);

        // 创建左边面板
        JPanel northPane = new JPanel();
        northPane.setLayout(new GridLayout(1, 1));
        // 设置左边面板各组件
        JPanel leftPane = new JPanel();
        leftPane.setOpaque(false);
        leftPane.setLayout(new GridLayout(3,2));
        JLabel portnameLb = new JLabel("串口号:");
        portnameLb.setFont(lbFont);
        portnameLb.setHorizontalAlignment(SwingConstants.RIGHT);
        portCombox = new JComboBox<String>((String [])portList.toArray(new String[0]));
        portCombox.addActionListener(this);
        JLabel databitsLb = new JLabel("数据位:");
        databitsLb.setFont(lbFont);
        databitsLb.setHorizontalAlignment(SwingConstants.RIGHT);
        dataCombox = new JComboBox<Integer>(new Integer[]{5, 6, 7, 8});
        dataCombox.setSelectedIndex(3);
        dataCombox.addActionListener(this);
        JLabel parityLb = new JLabel("校验位:");
        parityLb.setFont(lbFont);
        parityLb.setHorizontalAlignment(SwingConstants.RIGHT);
        parityCombox = new JComboBox<String>(new String[]{"NONE","ODD","EVEN","MARK","SPACE"});
        parityCombox.addActionListener(this);
        // 添加组件至面板
        leftPane.add(portnameLb);
        leftPane.add(portCombox);
        leftPane.add(databitsLb);
        leftPane.add(dataCombox);
        leftPane.add(parityLb);
        leftPane.add(parityCombox);

        //创建右边面板
        JPanel rightPane = new JPanel();
        rightPane.setLayout(new GridLayout(3,2));
        // 设置右边面板各组件
        JLabel baudrateLb = new JLabel("波特率:");
        baudrateLb.setFont(lbFont);
        baudrateLb.setHorizontalAlignment(SwingConstants.RIGHT);
        rateCombox = new JComboBox<Integer>(new Integer[]{2400,4800,9600,14400,19200,38400,56000});
        rateCombox.setSelectedIndex(2);
        rateCombox.addActionListener(this);
        JLabel stopbitsLb = new JLabel("停止位:");
        stopbitsLb.setFont(lbFont);
        stopbitsLb.setHorizontalAlignment(SwingConstants.RIGHT);
        stopCombox = new JComboBox<String>(new String[]{"1","2","1.5"});
        stopCombox.addActionListener(this);
        openPortBtn = new Button("打开端口");
        openPortBtn.addActionListener(this);
        closePortBtn = new Button("关闭端口");
        closePortBtn.addActionListener(this);
        // 添加组件至面板
        rightPane.add(baudrateLb);
        rightPane.add(rateCombox);
        rightPane.add(stopbitsLb);
        rightPane.add(stopCombox);
        rightPane.add(openPortBtn);
        rightPane.add(closePortBtn);
        // 将左右面板组合添加到北边的面板
        northPane.add(leftPane);
        northPane.add(rightPane);

        // 创建中间面板
        JPanel centerPane = new JPanel();
        // 设置中间面板各组件
        sendTf = new TextField(42);
        readTa = new TextArea(8,50);
        readTa.setEditable(false);
        readTa.setBackground(new Color(225,242,250));
        centerPane.add(sendTf);
        sendMsgBtn = new Button(" 发送 ");
        sendMsgBtn.addActionListener(this);
        // 添加组件至面板
        centerPane.add(sendTf);
        centerPane.add(sendMsgBtn);
        centerPane.add(readTa);

        // 设置南边组件
        statusLb = new JLabel();
        statusLb.setText(initStatus());
        statusLb.setOpaque(true);

        // 获取主窗体的容器,并将以上三面板以北、中、南的布局整合
        JPanel contentPane = (JPanel)getContentPane();
        contentPane.setLayout(new BorderLayout());
        contentPane.setBorder(new EmptyBorder(0, 0, 0, 0));
        contentPane.setOpaque(false);
        contentPane.add(northPane, BorderLayout.NORTH);
        contentPane.add(centerPane, BorderLayout.CENTER);
        contentPane.add(statusLb, BorderLayout.SOUTH);
    }

    /**
     * 初始化状态标签显示文本
     * @return String
     * @since 2012-3-23 上午12:01:53
     */
    public String initStatus() {
        portname = portCombox.getSelectedItem().toString();
        rate = rateCombox.getSelectedItem().toString();
        data = dataCombox.getSelectedItem().toString();
        stop = stopCombox.getSelectedItem().toString();
        parity = parityCombox.getSelectedItem().toString();

        StringBuffer str = new StringBuffer("当前串口号:");
        str.append(portname).append(" 波特率:");
        str.append(rate).append(" 数据位:");
        str.append(data).append(" 停止位:");
        str.append(stop).append(" 校验位:");
        str.append(parity);
        return str.toString();
    }

    /**
     * 扫描本机的所有COM端口
     * @since 2012-3-23 上午12:02:42
     */
    public void scanPorts() {
        portList = new ArrayList<String>();
        Enumeration<?> en = CommPortIdentifier.getPortIdentifiers();
        CommPortIdentifier portId;
        while(en.hasMoreElements()){
            portId = (CommPortIdentifier) en.nextElement();
            if(portId.getPortType() == CommPortIdentifier.PORT_SERIAL){
                String name = portId.getName();
                if(!portList.contains(name)) {
                    portList.add(name);
                }
            }
        }
        if(null == portList
                || portList.isEmpty()) {
            showErrMesgbox("未找到可用的串行端口号,程序无法启动!");
            System.exit(0);
        }
    }

    /**
     * 打开串行端口
     * @since 2012-3-23 上午12:03:07
     */
    public void openSerialPort() {
        // 获取要打开的端口
        try {
            portId = CommPortIdentifier.getPortIdentifier(portname);
        } catch (NoSuchPortException e) {
            showErrMesgbox("抱歉,没有找到"+portname+"串行端口号!");
            setComponentsEnabled(true);
            return ;
        }
        // 打开端口
        try {
            serialPort = (SerialPort) portId.open("JavaRs232", 2000);
            statusLb.setText(portname+"串口已经打开!");
        } catch (PortInUseException e) {
            showErrMesgbox(portname+"端口已被占用,请检查!");
            setComponentsEnabled(true);
            return ;
        }

        // 设置端口参数
        try {
            int rate = Integer.parseInt(this.rate);
            int data = Integer.parseInt(this.data);
            int stop = stopCombox.getSelectedIndex()+1;
            int parity = parityCombox.getSelectedIndex();
            serialPort.setSerialPortParams(rate,data,stop,parity);
        } catch (UnsupportedCommOperationException e) {
            showErrMesgbox(e.getMessage());
        }

        // 打开端口的IO流管道
        try {
            outputStream = serialPort.getOutputStream();
            inputStream = serialPort.getInputStream();
        } catch (IOException e) {
            showErrMesgbox(e.getMessage());
        } 

        // 给端口添加监听器
        try {
            serialPort.addEventListener(this);
        } catch (TooManyListenersException e) {
            showErrMesgbox(e.getMessage());
        } 

        serialPort.notifyOnDataAvailable(true);
    } 

    /**
     * 给串行端口发送数据
     * @since 2012-3-23 上午12:05:00
     */
    public void sendDataToSeriaPort() {
        try {
            sendCount++;
            outputStream.write(mesg.getBytes());
            outputStream.flush(); 

        } catch (IOException e) {
            showErrMesgbox(e.getMessage());
        } 

        statusLb.setText("  发送: "+sendCount+"                                      接收: "+reciveCount);
    } 

    /**
     * 关闭串行端口
     * @since 2012-3-23 上午12:05:28
     */
    public void closeSerialPort() {
        try {
            if(outputStream != null)
                outputStream.close();
            if(serialPort != null)
                serialPort.close();
            serialPort = null;
            statusLb.setText(portname+"串口已经关闭!");
            sendCount = 0;
            reciveCount = 0;
            sendTf.setText("");
            readTa.setText("");
        } catch (Exception e) {
            showErrMesgbox(e.getMessage());
        }
    }     

    /**
     * 显示错误或警告信息
     * @param msg 信息
     * @since 2012-3-23 上午12:05:47
     */
    public void showErrMesgbox(String msg) {
        JOptionPane.showMessageDialog(this, msg);
    }

    /**
     * 各组件行为事件监听
     */
    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == portCombox
                || e.getSource() == rateCombox
                || e.getSource() == dataCombox
                || e.getSource() == stopCombox
                || e.getSource() == parityCombox){
            statusLb.setText(initStatus());
        }
        if(e.getSource() == openPortBtn){
            setComponentsEnabled(false);
            openSerialPort();
        }
        if(e.getSource() == closePortBtn){
            if(serialPort != null){
                closeSerialPort();
            }
            setComponentsEnabled(true);
        }

        if(e.getSource() == sendMsgBtn){
            if(serialPort == null){
                showErrMesgbox("请先打开串行端口!");
                return ;
            }
            mesg = sendTf.getText();
            if(null == mesg || mesg.isEmpty()){
                showErrMesgbox("请输入你要发送的内容!");
                return ;
            }
            sendDataToSeriaPort();
        }
    }

    /**
     * 端口事件监听
     */
    public void serialEvent(SerialPortEvent event) {
        switch (event.getEventType()) {
            case SerialPortEvent.BI:
            case SerialPortEvent.OE:
            case SerialPortEvent.FE:
            case SerialPortEvent.PE:
            case SerialPortEvent.CD:
            case SerialPortEvent.CTS:
            case SerialPortEvent.DSR:
            case SerialPortEvent.RI:
            case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
                break;
            case SerialPortEvent.DATA_AVAILABLE:
                byte[] readBuffer = new byte[50];

            try {
                while (inputStream.available() > 0) {
                    inputStream.read(readBuffer);
                }
                StringBuilder receivedMsg = new StringBuilder("/-- ");
                receivedMsg.append(new String(readBuffer).trim()).append(" --/\n");
                readTa.append(receivedMsg.toString());
                reciveCount++;
                statusLb.setText("  发送: "+sendCount+"                                      接收: "+reciveCount);
            } catch (IOException e) {
                showErrMesgbox(e.getMessage());
            }
        }
    }

    /**
     * 设置各组件的开关状态
     * @param enabled 状态
     * @since 2012-3-23 上午12:06:24
     */
    public void setComponentsEnabled(boolean enabled) {
        openPortBtn.setEnabled(enabled);
        openPortBtn.setEnabled(enabled);
        portCombox.setEnabled(enabled);
        rateCombox.setEnabled(enabled);
        dataCombox.setEnabled(enabled);
        stopCombox.setEnabled(enabled);
        parityCombox.setEnabled(enabled);
    }

    /**
     * 运行主函数
     * @param args
     * @since 2012-3-23 上午12:06:45
     */
    public static void main(String[] args) {
        new JavaRs232();
    }
}

代码编写完成,按下F11键进入调试状态,一切运行正常良好,请看图:

1.启动界面

2.端口检测

3. 通讯测试

最后再抽空来美化程序下,效果更漂亮,谁还会说JAVA程序的界面丑陋呢,呵呵...

 第一次发文虽没有什么技术含量但也实属不易哪,欢迎大家拍砖,嘻嘻....

时间: 2024-08-09 16:42:18

Java程序与RSR232串口通讯小练手(转载)的相关文章

微信小程序组件构建UI界面小练手 —— 表单登录注册微信小程序

通过微信小程序中丰富的表单组件来完成登录界面.手机快速注册界面.企业用户注册界面的微信小程序设计. 将会用到view视图容器组件.button按钮组件.image图片组件.input输入框组件.checkbox多项选择器组件.switch开关选择组件.navigator页面连接组件等. Ⅰ.登录设计 登录表单中,需输入账号.密码进行登录,在账号.密码输入框中都有友好的提示信息:登录按钮默认是灰色不可用状态,只有输入内容后,才会变为可用状态:在登录按钮下面提供手机快速注册.企业用户注册.找回密码链

【高级java程序员应该知道的小知识】 volatile

本篇态度: simple & stupid volatile 用法: private volatile Service.STATE state; 优点: 比其他的锁机制(synchronized.lock)简单: 不阻塞: 对volatile修饰的变量允许并发读. 谨记: Java Memory Model ensures that all threads see a consistent value for the variable volatile提供变量修改后对其他线程的可见性. 变量被一

微信小程序框架分析小练手(二)——天气微信小程序制作

简单的天气微信小程序. 一.首先,打开微信开发者工具,新建一个项目:weather.如下图: 二.进入app.json中,修改导航栏标题为“贵州天气网”. 三.进入index.wxml,进行当天天气情况的界面布局,包括温度.最低温度和最高温度.天气情况.城市.星期.风向情况.如下图: 四.进入index.js,在data里提供天气数据,让这些数据在界面里显示出来: 五.进入index.wxml,将data里提供的天气数据绑定到页面里: 界面效果如下: 六.进入index.wxss,为index.

【高级java程序员应该知道的小知识】 WeakHashMap

本篇宗旨:simple & stupid WeakHashMap (弱引用的哈希表) Hash table based implementation of the Map interface, with weak keys. An entry in a WeakHashMap will automatically be removed when its key is no longer in ordinary use. More precisely, the presence of a mapp

【高级java程序员应该知道的小知识】weak reference

本篇态度: simple & stupid weak reference Weak reference objects, which do not prevent their referents from being made finalizable, finalized, and then reclaimed. Weak references are most often used to implement canonicalizing mappings. 弱引用不会阻止其引用的对象变成fin

用BSF + Beanshell使Java程序能够运行字符串形式的代码(转载)

BSF(Bean Scripting Framework)最初是IBM Alpha工作组的项目,后来贡献给了Apache,BSF使Java能够更好的与各种流行脚本语言整合,实现不同语言之间的交互功能.Beanshell是一种完全符合Java语法的脚本语言. 运行以下代码需下载相关jar包(http://jakarta.apache.org/bsf/,http://www.beanshell.org/) PS:新浪博客将“eval”视为危险角本,所以将下面例子里的所有“eval”方法替换成了“ev

小练手2

1. """ Write a program that accepts a sentence and calculate the number of upper case letters and lower case letters. Suppose the following input is supplied to the program: Hello world! Then, the output should be: UPPER CASE 1 LOWER CASE 9

java中的简单Udp通讯程序

前言 原创文章欢迎转载,请保留出处. 若有任何疑问建议,欢迎回复. 邮箱:[email protected] 本人学习java中的socket练手的小程序,高手请飘过,仅作为学习笔记,没有技术含量. 分析 这里使用两个独立线程负责发包和收包,设计两个类实现Runnable接口,利用管道和主窗体通信. 代码 mainfest.txt文件:这里注意有myqq.MyQQ后的回车符和myqq.MyQQ前的空格 Main-Class: myqq.MyQQ MyQQ.java文件: package myqq

应用程序初次运行数据库配置小程序(Java版)

应用程序初始化数据库配置小程序 之前写过一个Java版的信息管理系统,但部署系统的时候还需要手动的去配置数据库和导入一些初始化的数据才能让系统运行起来,所以我在想是不是可以写一个小程序在系统初次运行的时候自动部署数据库和导入一些初始化数据.然后就有了以下的思路: 在应用程序入口处判断数据库是否已经配置完成,若配置完成则进入正常的登录系统完成正常操作即可,若未配置则进入数据库配置的小程序完成数据库配置然后再进入系统,但如何来判断是否已经配置完成呢,在这里我用的是比较原始的方法,配置数据库的时候系统