摘要:
由于Java语言的诸多优点,Java得到了广泛的应用,如今利用Java开发串口通讯已相当成熟,实现简单,可移植性强。文章详细介绍了如何配置开发环境以及使用Java串口API函数编写PC机程序。本程序比使用C++语言编写的串口通讯程序更容易理解,且移植性非常强,视图与控制分开,便于维护和升级。
关键字:
Java,
JBuilder,
KeilC,
Java
Communications API,串口通讯,
RS232,单片机
1 硬件部分(
KeilC)
图1 硬件电路图
串口通讯硬件部分电路,收发器件采用max232,5V供电。J31接一单片机如AT89C52,单片机的串口与max232的10和11脚相连。
max232与微机通过
9针接头相连。
本文的实验环境是AT89C52,单片机的内部程序是用KeilC语言编写的,程序功能非常简单,检测到开始信号后从串口读数据,然后把读入的数据发送给串口,遇到结束符停止。C语言代码如下供大家参考。在uv3中含有两个文件comm.h和comm.c,代码分别为:
/********************************************************/
/* comm.h */
/* serial port define, only use in comm project */
/********************************************************/
#define uchar unsigned char
#define uint unsigned int
#define length 0x0F //数据包长度
uchar CR = 0x0D;
uchar LF = 0x0A;
uchar ESC = 0x1B;
uchar SYNC = 0x01; //数据包启始符
uchar PID = 0x00; //数据包标示字
uchar ADDR; //串口接收外部命令(片内地址)
uchar DATA; //串口返回片内数据
uchar ENDP = 0x00; //数据包结束符
uchar ACK = 0x06; //串口确认
uchar ERROR = 0x18; //串口错误
uchar wrong[] = "Bad command";
/*END*/
/*******************************************************/
/*comm..c */
/* Write time is 2005.4.15,By Gooseli */
/* Copyright is changsha HUNU unversity gooseli.com */
/* Cpu is At89C51,Fclk is 11.059MHz */
/* Compiler is keilC51 compiler */
/*******************************************************/
#include
#include
#include
#include
void commInit(){
//**************************//
// 8051串口初始化 //
//**************************//
SCON = 0x52;
PCON = 0x80;
TMOD = 0x21;
TH1 = 0x0FA;
TL1 = 0x0FA;
TCON = 0x40;
//*****************************************************//
// 串口控制器工作于方式1,8位,频率可变。接收允许 //
// 串口波特率系数SMOD =
1 //
// 定时器1工作于方式1,8位自动装载。定时器0方式1,16位 //
// 11.059M晶振,波特率 =
9600,TH1 = 0x0FA; //
// 19200 0x0FD //
// 57600 0x0FF //
// 3.6864M晶振 9600 0x0FE //
// 19200 0x0FF //
// #3.6864M晶振工作于方式2 //
// #在SMOD = 1时,波特率 =
115200 //
// 开中断TR1 = 1 //
//*****************************************************//
}
uchar flag;
uchar readln();
void println( uchar *str );
main(){
commInit(); //初始化串口
while(1){
flag = readln();
}
}
uchar readln(){
uchar a;
uchar str[length];
int i;
scanf("%c",&a); //寻找起始符,回车则开始
if( a==SYNC || a==LF ){
while(1){
printf("
>>");
//printf(">>");
scanf("%c",&a);
if( a==ENDP || a == ESC ){ //如果ESC则对话结束
break;
}
for( i="0"; i
//读入数据包,如果溢出或者回车则结束
str[i] = a;
scanf("%c",&a);
}
str[i] = ENDP; //为数据包添加结束符,“0”
printf("%s",str); //输出输入值
/*To do something by yourself*/
}
return ACK;
}
printf("
%s
>>",wrong);
return ERROR;
}
/*END*/
2 配置运行环境
(JDK)
Java通讯库函数
Java Communications API,
Java开发工具
JBuilderX。
此Java(TM) Communications API Specification 2.0(Windows
Platform)是Sun公司为Windows平台提供的一个串口API扩展,可以到 http://java.sun.com/products/javacomm/ 下载。Sun公司还提供了其他操作系统下的API下载,移植性是Java先天的优势,如果需要在其他操作系统运行程序,不需要改动程序本身,只要在操作系统下植入相应的API库函数即可实现。
JBuilder是
Borland公司出品的一款功能强大的可视化
Java集成开发工具,可以快速开发包括复杂企业级应用系统的各种
Java程序,本文的程序都用其实现。当然我们以可使用其他优秀的开发工具,例如开放源代码的
Eclipse,功能强大,插件丰富。
在下载Java Communications API压缩文件里找到三个文件:comm.jar,win32comm.dll,javax.comm.properties,这三个文件是把API安装到Windows环境中的重要文件,我们把他们放在我们的JDK里面。
把comm..jar复制到%JAVA_HOME%jrelibext,javax.comm.properties复制到%JAVA_HOME%jrelib,win32comm.dll复制到%JAVA_HOME%in即可。这样我们的程序就可以在Windows环境中运行了,Java
Communications API压缩文件中自带有例子,我们可以尝试一下。
接下来我们要把Java Communications API安装到JBuilder里面,如果JBuilder不是使用的外部的JDK,照上面的的步骤再做一次。假如我们外部的JDK和JBuilder的JDK是同一的JDK,我们就直接跳到下一步。
1)打开
JBuilder,为我们的任务建立一个工程,给它起个有意义的名字,不多讲了。
JBuilder会自动生成两个文件,如,工程名为
comm,就会生成文件
commApplication和
commFrame。
2)选择
Tools菜单,选择
Configure Libraries…,如图
1所示。
3)点击
New按钮,为
JBuilder增加一个函数库。如图
2,点击
OK即可。
4)下一步为你的工程增加这个库函数,以便你在工程里调用它们。选择
Project菜单中的
Project Properities选项,左侧选中
Paths,右侧选中
Required
Libraries,单击
Add,出现一个小的对话框,选择我们刚才增加的
comm函数库,如图
3,点击
OK两次即可。
现在环境已经配置好了,我们要开始正式工作了。
图2 Configure Library
图2 New Librariy Wizard
图3
Select comm. Library
3 程序开发
(JBuilderX)
1. 与51单片机交互信息,数据库存取(Data)
为了保证数据传输的顺利进行,单片机与PC之间通讯要建立一个协议,在本实例中,采用如下协议:
程序打开串口后,程序发送“启始”符(0x01)表示通讯开始。
通讯开始后,程序就开始发送和接收数据包,数据包以“结束”符(0x00,0x0D,
0x0A)表示结尾。由于单片机受控于PC机,所以单片机一般不主动发送数据,只有在PC机发送一个“命令”,它才会发送一个“回应”。
如果程序“停止”符(0x00),则通讯结束。
2. 界面设计(View)
这部分设计主程序的视图,即使用者看到的部分,包括按钮,下拉菜单,文字编辑框等。
为了程序的可读性,我们将所有的视图从主程序中分离出来,作成Bean的形式,然后在主程序中调用它们。Java提供了五种布局管理形式FlowLayout,,GridLayout, GridBagLayout, BorderLayout,,CardLayout。灵活的运用这些布局,可以达到各种各样的效果,其中GridBagLayout功能强大,使用灵活,本文主要采用这种布局。
3. 主程序设计(Control)
这部分设计程序的实现方法,逻辑步骤。
1)首先定义串口,输入输出流等,如下所示:
package comm;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.comm.*;//包含comm类包,才能使用其API函数
import java.io.*;
import java.util.*;
public class CommFrame extends JFrame implements Runnable, SerialPortEventListener {
JPanel contentPane; //定义一个JPanel,将视图Bean包含进来
BorderLayout borderLayout1 = new BorderLayout();
IOBean ioBean = new IOBean();//右侧视图Bean类事例化
ControlBean controlBean = new ControlBean();//左侧视图Bean类事例化
//Communination define
static CommPortIdentifier portName;//定义串口
int portId;
static Enumeration portList;
InputStream inputStream;//定义输入流
OutputStream outputStream;//定义输出流
SerialPort serialPort;
Thread readThread;//定义一个线程,程序全双工通讯
static String TimeStamp;
//Construct the frame
public CommFrame() {
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try {
jbInit();//程序初始化
commInit();//串口初始化
}catch(Exception e) {
e.printStackTrace();
}
}
private void jbInit() throws Exception {……}
public void commInit() {……}
public void commClose() {……}
public void commWrite() {……}
public void CommRead() {……}
public void run() {……}
public void serialEvent(SerialPortEvent event) {…….}//代码如下
//Overridden so we can exit when window is closed
protected void processWindowEvent(WindowEvent e) {
super.processWindowEvent(e);
if (e.getID() == WindowEvent.WINDOW_CLOSING) {
commClose();
System.exit(0);
}
}
}
2)串口初始化,首先监测串口是否被占用,如果没有被占用则打开串口。打开输入输出流以便下面的程序从串口读写数据,定义串口的波特率,位数,停止位,奇偶校验,在使用过程中可以改变这些内容以适应不同的需求。
public void commInit() {
//Communination ports owned or not
portId = 1;
try{
portList = CommPortIdentifier.getPortIdentifiers();
while (portList.hasMoreElements()) {
portName = (CommPortIdentifier) portList.nextElement();
if (portName.getPortType() == CommPortIdentifier.PORT_SERIAL) {
if (portName.isCurrentlyOwned()) {//串口是否被占用
ioBean.Receiver.append("
COM"+portId+"Ownedby"+ portName.getCurrentOwner());
TimeStamp = new java.util.Date().toString();
portId ++;
}else if (portName.getName().equals("COM" + portId)) {
break;
}
}
}
//Communination ports init
try {
serialPort = (SerialPort) portName.open("Gooseli_MCU_Control_App", 2000);//打开串口
controlBean.CommPortID.setText("COM" + portId);
controlBean.OnOff.setText("ON");//开关按钮置开状态
controlBean.OnOff.setSelected(true);
TimeStamp = new java.util.Date().toString();
System.out.println(TimeStamp + ": msg2 - SerialPort COM" + portId + " is opend");
ioBean.Receiver.append("
COM" + portId + " is opend");//显示区域显示串口被打开
} catch (PortInUseException e) {
System.out.println(e);
ioBean.Receiver.append("
COM" + portId + " " + e);
}
try {
inputStream = serialPort.getInputStream();//打开输入流
} catch (IOException e) {}
try {
outputStream = serialPort.getOutputStream();
outputStream.write((byte)0x01);//向串口写入启始符开始传送数据包
ioBean.Receiver.setText("
COM" + portId + ">>" + "Start");
controlBean.begin.setSelected(true);
} catch (IOException e) {}
try {
serialPort.setSerialPortParams(9600,//波特率
SerialPort.DATABITS_8,//数据位
SerialPort.STOPBITS_1,//停止位
SerialPort.PARITY_NONE);//校验位
} catch (UnsupportedCommOperationException e) {}
CommRead();//程序开始从串口读数据
}catch(Exception e) {}
}
public void commClose() {
try {
inputStream.close();
outputStream.close();
serialPort.close();
System.out.println(TimeStamp + ": msg2 - SerialPort COM" + portId + " is closing");
ioBean.Receiver.append("
COM" + portId + " is closing");
}catch (Exception e) {
System.out.println(e);
}
}
3)程序初始化,这里定义了一些事件,以便控制程序的运行。例如开始按钮的事件定义如下:
private void jbInit() throws Exception {
contentPane = (JPanel) this.getContentPane();
contentPane.setLayout(borderLayout1);
this.setSize(new Dimension(400, 300));
this.setTitle("Serial Ports Communication Current");
contentPane.add(ioBean,BorderLayout.CENTER);
contentPane.add(controlBean, BorderLayout.WEST);
controlBean.OnOff.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent ae) {