惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Y
Y Combinator Blog
有赞技术团队
有赞技术团队
J
Java Code Geeks
H
Hackread – Cybersecurity News, Data Breaches, AI and More
美团技术团队
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Hugging Face - Blog
Hugging Face - Blog
人人都是产品经理
人人都是产品经理
酷 壳 – CoolShell
酷 壳 – CoolShell
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
C
Check Point Blog
博客园 - 【当耐特】
The GitHub Blog
The GitHub Blog
Recent Announcements
Recent Announcements
The Cloudflare Blog
Microsoft Azure Blog
Microsoft Azure Blog
腾讯CDC
Vercel News
Vercel News
IT之家
IT之家
MyScale Blog
MyScale Blog
博客园_首页
Martin Fowler
Martin Fowler
WordPress大学
WordPress大学
罗磊的独立博客

热爱生活与梦想

迁移LeanCloud的Waline评论数据到TiDB/Neon 25年江浙地区暑假自驾游之感慨 使用阿里云灵码继续完成构建 SSL 证书管理器 亲手给儿子做的棒棒糖摩天轮六一礼物 使用腾讯云 CodeBuddy 构建 SSL 证书管理器 修复代码块默认高度相关缺陷 HugoNexT4.7.2 新功能和升级提示 改善代码块折叠和选中功能 支持Github风格的警告样式 新增音乐短代码功能支持 给文章添加摘要和过期提示功能 回顾24年的过去总结 修复Mathjax行内显示公式的问题 给Hugo文章增加阅读更多跳转的锚点定位功能 Win10隐匿的VPN设置和L2TP连接常见错误 Go语言中“糟糕”的日期时间格式化设计 DaoCloud道客提供的免费Docker镜像代理服务 感谢万能淘宝让自己吃到喜欢的美食 WSL运行时遇到未知异常错误无法使用 隐藏的换行符导致Base64加密解密失败 MySQL自带客户端直接免密登录操作 Linux系统中删除目录软链接的注意项 Java程序调用外网API时CA问题 如何不关机重启WSL2恢复虚拟服务 在Windows上安装Podman容器平台做虚拟化 记一次无法弹出移动硬盘的记录 为友情链接添加自动检测脚本 重新激活Github的2FA认证 Linux中使用tar压缩命令排除文件 继续小米手环的健康生活之旅
Java程序与RSR232串口通讯小练手
凡梦星尘 · 2012-03-25 · via 热爱生活与梦想

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

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

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

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433

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();
	}
}