新闻中心

EEPW首页>嵌入式系统>设计应用> 基于Java语言的51单片机串口通讯PC机程序

基于Java语言的51单片机串口通讯PC机程序

作者: 时间:2016-11-20 来源:网络 收藏
1硬件部分(KeilC)


本文引用地址://m.amcfsurvey.com/article/201611/318580.htm



图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 ucharunsigned char


#define uint unsigned int


#define length0x0F //数据包长度



ucharCR = 0x0D;


ucharLF = 0x0A;


ucharESC = 0x1B;


ucharSYNC = 0x01; //数据包启始符


ucharPID = 0x00; //数据包标示字


ucharADDR; //串口接收外部命令(片内地址)


ucharDATA; //串口返回片内数据


ucharENDP = 0x00; //数据包结束符


ucharACK = 0x06; //串口确认


ucharERROR = 0x18; //串口错误



ucharwrong[] = "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 iskeilC51 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("n>>");


//printf(">>");


scanf("%c",&a);


if( a==ENDP || a == ESC ){ //如果ESC则对话结束


break;


}


for( i="0"; i


//读入数据包,如果溢出或者回车则结束


str[i] = a;


scanf("%c",&a);


}


str[i] = ENDP; //为数据包添加结束符,“”


printf("%s",str); //输出输入值



/*To do something by yourself*/


}


return ACK;


}


printf("n%sn>>",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%bin即可。这样我们的程序就可以在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("nCOM"+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("nCOM" + portId + " is opend");//显示区域显示串口被打开


} catch (PortInUseException e) {


System.out.println(e);


ioBean.Receiver.append("nCOM" + portId + " " + e);


}


try {


inputStream = serialPort.getInputStream();//打开输入流


} catch (IOException e) {}


try {


outputStream = serialPort.getOutputStream();


outputStream.write((byte)0x01);//向串口写入启始符开始传送数据包


ioBean.Receiver.setText("nCOM" + 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("nCOM" + 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) {


JToggleButton toggle = (JToggleButton) ae.getSource();


if (toggle.isSelected()) {


controlBean.OnOff.setText("ON");


commInit();


} else {


controlBean.OnOff.setText("OFF");


commClose();


}


}


}


);


controlBean.begin.addActionListener(


new ActionListener() {


public void actionPerformed(ActionEvent ae) {


JToggleButton toggle = (JToggleButton) ae.getSource();


if (toggle.isSelected()) {//如果按钮被按下,则开始


controlBean.begin.setText("Start");


try {


outputStream.write((byte)0x01);//发送起始符


ioBean.Receiver.setText("nCOM" + portId + " " + "Start");


} catch (IOException e) {}


} else {//如果按纽复位


controlBean.begin.setText("Stop");


try {


outputStream.write((byte)0x00);//发送结束符


ioBean.Receiver.append("nCOM" + portId + " " + "Stop");


} catch (IOException e) {}


}


}


}


);


ioBean.jButton2.addActionListener(


new ActionListener() {


public void actionPerformed(ActionEvent ae) {


commWrite();


}


}


);


}


4)读写串口,使用多线程,实现全双工通讯。主函数CommFrame实现了Runnable接口,在程序中只需要重写run函数即可实现多线程。


public void commWrite() {


String outString = ioBean.jTextField1.getText();


if (outString.equals("clear")) {


ioBean.Receiver.setText("nCOM" + portId +" Receive:");


}


ioBean.jTextField1.setText("Gooseli:");


try {


//outputStream.write((byte)0x01);


outputStream.write(outString.getBytes());


outputStream.write((byte)0x0D);


//outputStream.write((byte)0x00);


ioBean.Receiver.setText("nCOM" + portId + ">>" + outString);


} catch (IOException e) {}


}


public void CommRead() {


try {


serialPort.addEventListener(this);


} catch (TooManyListenersException e) {}


serialPort.notifyOnDataAvailable(true);


readThread = new Thread(this);


readThread.start();


}



public void run() {


try {


Thread.sleep(20000);


}


catch (InterruptedException e) {}


}



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:


StringBuffer readBuffer = new StringBuffer();


String scannedInput = null;


int c;


try {


while ((c = inputStream.read()) != 0x00 && c != 0x0D && c != 0x0A) {


readBuffer.append( (char) c);//数据包以回车或者换行表示结束


}


scannedInput = readBuffer.toString();


ioBean.Receiver.append("n" + scannedInput);


TimeStamp = new java.util.Date().toString();


System.out.println(TimeStamp + ": scanned input received:" + scannedInput);



inputStream.close();


} catch (IOException e) {}


break;


}


}


4.测试程序


程序运行后如图四所示。


为了方便程序运行,我们作一个批处理文件,和程序生成的jar文件放在一个目录里,这样就可以很方便的在含有Java虚拟机的系统里运行我们的程序了。可以将JBuilder运行窗口(Messages)的信息直接拷贝过来,当然也可以自己建立。


我的批处理文件如下所示:


@echo off


@REM设置路径包括Java虚拟机和程序jar文件路径


set path=%JAVA_HOME%bin


set classpath=%cd%comm.jar


@REM运行程序,注意在主程序前增加包名,否则找不到主函数


echo Now initializing the program,please wait a minite...


java comm.CommApplication



图4 Test my programme



4展望


Java作为一个逐渐成熟的程序设计语言,在网络和嵌入式系统越来越多地得到广泛的应用,但是微机底层程序开发发展缓慢。本文应用Java作为串口上位机程序开发语言,是考虑到Java语言的各种优越性,主要是其平台无关性,强大的可移植性,这一点在不久的将来会受到更多的重视。


本文代码可免费使用,如果需要本文程序的源代码,请于本作者联系,gooseli@163.com。



参考资料:


1.A How-To on Accessing serial ports in the Windows environment using the Java Communications API,By Rick Proctor,www.borland.com,2004年3月14日,http://bdn.borland.com/article/0,1410,31915,00.html


2.The Java Communications API Documentations,www.sun.com,2004年11月


3.JBuilder 9集成开发实例解析,张洪斌编,机械工业出版社,2004年1月


4.精通JBuilder 9,飞思科技产品研发中心编,电子工业出版社,2003年8月


5.单片微机原理及应用,丁元杰、赵秀菊、陈瀛清编,机械工业出版社,1999年8月



评论


技术专区

关闭