2024年6月7日发(作者:)

维普资讯

…… 

眦删ORK&C0啊啊UNIC盯iON …………………… …… …………………………,….… … ,…… ……… 

&嗡|霸 .葡l' 器|蓓 廷 I 

薯玛玉 

摘要在NetBeans 5,0环境下实现了通用的串行通信类,可以方便地发送和读取任何字 

节,并给出了应用实例和测试效果。该技术广泛应用于基于Java的嵌入式设备的 

二次开发中。 

关键词 串行通信,串口初始化,多线程,数据接收 

串口的异常NosuchPortException,并返回Serlal_Error(常数一 

引言 

1),其源代码如下: 

Java程序具有一次编译、到处运行的跨平台特性,随着 

publicintGetPortlD()( 

Java在嵌入式系统中的广泛应用,研究Java的串行通信程序具 

tⅣ( 

有日益重要的意义。文献【1】实现了基于Java事件驱动的串行 

portlD=CommPortIdentifier.getPortIdentiifer(PortName》: 

通信,在该文献及Sun公司中国技术社区的蒋清野的基础之 

)catch(N0Such P0rtExcepti0n e)( 

return Serial

_

ErrOr 

上,设计了通用的串行通信类,跟文献【2】相比,这些类更简 

) 

洁、健壮,而且可以方便地处理任何字节,并可以将一批数据 

return SeriaLSuccess; 

作为一个整体来提交,方便用户的下一步处理。这里的通用串 

) 

口类由两个Java类组成,OperateCOM类用来初始化串口,并 

3.public int Open(Stirng AppName,int nTime) 

启动数据接收进程;ReadCOM类用来读取串口数据,并将一 

该方法通过获取的串口po ̄lD打开串口,输人参数App・ 

批数据作为整体提交。SerialExample类利用OperateCOM和 

Name是程序的名称,nTime表示延迟的毫秒数。如果该串口正 

ReadCOM类进行串行通信的测试,并利用文献【3]中的工具进 

在被使用,则会抛出P0rtInuseException异常;如果打开串口成 

行各种格式的显示。 

功,则根据serialPo ̄继续获取串口的输入输出流,其源代码如 

下: 

二、OperateCOM类 

public int Open(String AppName,jnt nTime)( 

OperateCOM与ReadCOM类的包名均为Ser

务 

ialPort。Oper. 

trv( 

ateCOM类主要完成的工作是:取得串口I

_一一 。 。 _ _  

D、打开串口、获取 

serialPort=(SeriaIPort)portlD.open(AppName,nTime) 

)catch(PortlnUseException e){ 

输入输出流、设置串口参数、启动串口数据读取进程。然后, 

return Serial上rror: 

就是常用的读取串口数据、从串口发送数据,以及关闭串口。 

) 

1.public OperateCOM(int PoaID,int nLen) trv( 

这是OperateCOM类的构造方法,Po ̄ID表示需要操作的 

in=serialPort getlnputStream(): 

串口号, …1’表示“COM1”, …2’表示“COM2”……以此 

out=serialPort.getOutputStream(): 

)catch(IOException e){ 

类推;nLen表示串口的输入缓冲区大小,最小值为1。构造方 

return Serial Error1 

法的源代码如下: 

) 

public OperateCOM(int PortlD,int nLen)( 

return SeriaLSuccess; 

PortName= COM +PortlD; 

nMaxLength:nLen: 

if(nLen<1)nMaxLength=1 

) 

该方法用来设置串口参数。输人参数分别对应波特率、数 

2.public int GetPortID() 

据位、停止位和校验方式。在Java Communications AP[的 

该方法通过串口名字,如COM1,取得串口的ID,如果正 

Javadoc中,有相应的常数代号,例如,DATABITS_8表示整数 

确则返回Serial ̄uccess(常数1);如果错误,则抛出没有此 

维普资讯

…………………………………… … …………… 

实用第一 智慧密集 

…………………………。………………… 一………… 

8,STOPBITS

__

I表示整数1等。源代码如下: 

public int SetParams(int baudrate,int dataBits,int stopBits, 

intparity){ 

try 

ser|aIPort.setSerialPortParams{baudrate,dataBits, 

stopBits,parity): 

}catch(UnsupportedCommOperationException e){ 

return Serial

_

Error; 

l 

return SeriaLSUCCess; 

} 

5.public void StartCom(int nDelay) 

该方法启动数据接收进程,nDelay是延迟的毫秒数,表示 

凡是时间间隔在nDelay毫秒之内的数据,都作为一个整体来 

处理。从文献[1】中的测试效果可知,串行通信的数据确实是 

不连续的,需要进行累加处理。ReadCOM是Thread类的子 

类,本方法根据给定的参数,生成一个多线程的实例,并启动 

多线程。OperateCOM类直接控制ReadCOM类的实例的生成、 

多线程的销毁与数据的读取等。StartCom的源代码如下: 

public void StartCom《int nDelay){ 

readThread=new ReadCOM(in.nDelay,nMaxLength): 

readThread.start( 

) 

6.public byte【】ReadPort() 

读取串口数据,并且根据要求的时间片将数据作为整体处 

理,是串行通信中的难点。ReadCOM类很好地实现了这个功 

能,其方法GetComBuffer()可以取得以字节数组形式的完整的 

数据包。ReadPort()的源代码如下: 

publicbytef l ReadPort(){ 

return readThread.GetComBuffer(): 

} 

7.public void WritePort(byte bData【】,int off,int len) 

该方法用来从串13发送数据,其参数包括需要发送的字节 

数组、数组的偏移量与发送的长度,其源代码如下。 

publicvoidWritePort(bytebData【】,intoff,intlenl{ 

try{ 

out.write(bData.off,len): 

}catch(IOException e){ 

System.out.println( IOException: +el: 

} 

} 

8.public void ClosePort() 

OperateCOM类的Open方法打开串13,ClosePort则用于 

关闭串口。由于读取串口数据的多线程是一个死循环,关闭 

串13前,需要首先关闭多线程。在多线程中,有一个布尔变 

量,可以通过公共方法DestroyReadThread调用,用来设置为 

trite,从而使多线程退出。纯粹给多线程实例readThread赋值 

20

磁08. 1

与 

null,并不能迫使多线程退出,在NetBeans 5.0的调试环境 

下可以发现这一点 另外,Thread的Destroy方法已经抛弃不 

用了。关闭多线程后,再关闭串13即可。ClosePort方法的源 

代码如下: 

public void ClosePort(){ 

readThread.DestroyReadThread() 

readThread=null; 

serialPort close(): 

l 

三、ReadCOM类 

ReadCOM类派生于Thread类,其主要方法为构造方法及 

rlln方法,其他方法被rlln方法所调用。 

1.public ReadCOM(InputStream Port,int steps,int nLen) 

该方法是ReadCOM类的构造方法,需要传人输入流 

Potr;需要等待的节拍数(即毫秒数)steps,在此时间片内的 

数据将被当作一个整体来处理;nLen则是输入缓冲区的大小, 

根据该数据调用ByteBuffer类的allocate方法分配缓冲区。构造 

方法的源代码如下: 

public ReadCOM(InputStream Port,int steps,int nLen){ 

ComPort=Port; 

meTdWa;t=steps; 

nPackageLen=nLen; 

ComBuffer:ByteBuffer allocate(nLen): 

} 

2.public void rlln() 

该方法是一个多线程方法,是ReadCOM类中的核心方 

法,本类中的其他方法几乎都是为该方法服务的。rlln方法是 

个while循环,不停地检测串口的输入流有无数据。这个循 

环由3组并列的条件语句组成: 

●bDestroy为true,则退出循环,该变量的设置通过公共 

方法DestroyReadThread完成; 

●如果输入流中有数据,即ComPort.available()>0,则读 

取当前字节,1Start中记录当前字节到达的时间。第一个字节 

到达后,通过方法SetAvailable(false)将当前数据包设置为不可 

用,因为数据还没有接收组装完成; 

●如果当前字节到达的时间1Start大于0,则计算当前时 

间与1Start之间的时间间隔,如果大于规定的数值TimeToWait 

(在构造方法中设置),则认为数据接收结束,通过方法se— 

tAvailable(title)将当前数据包设置为可用。rlln方法的源代码如 

下: 

publicvoid run(){ 

byte bin;//存放读取的当前字节 

try{ 

while(true){ 

if(bDestroy==true)return; 

|f(ComPort.available()>0){ 

维普资讯

…… 

NETWORK&C0啊啊UNIC盯ION……… …………………………………………………………… ………… 

bln=(byte)ComPort.read(); 

if(IStart==0)SetAvailable(false //清空缓冲区 

PutByte(bin)://保存数据 

Time=Calendar getlnstance( 

IStart=sTime.getTimelnMillis() 

//3前读取数据的时间 

、 

【f(1start>O){ 

s Time=CaIendar.getInstance(): 

Ilnterval=sTim e-getTimelnMillis()-IStart; 

if 『nterval>=TimeToWait)( 

SetAvailable(true): 

) 

) 

) 

)catch(I OException e){ 。 

System.out.println("IC}Exception ErrOf" 4-e); 

、 

) 

3 private synchronized void PutByte(byte bin) 

该方法是私有方法,被rUB方法调用,将当前从串口输人 

流ComPort中读取的字节存人串口输入缓冲区ComBuffer中, 

如果缓冲区的最后一个字节的位置已经大于最大值nPackage— 

Len(在构造方法中设置),则提示缓冲区溢出,同时,将缓 

冲区指针复位。ComBuffer.put((byte)bin)语句将字节bin存人 

缓冲区,这将使得缓冲区的指针(位置)后移一个字节,源代 

码如下: 

Iprivate synchronized void PutByte(byte bl n){ 

-f(ComBuffer position()>=nPackageLen)( 

System out。println("ComBuffer overflow! ): 

ComBuffer rewind();//指针复位- 

) 

ComBuffer.Iput(I byte)bIn): 

) 

4.private void SetAvailable(boolean bGet) 

该方法是私有方法,被rUB方法和GetComBuffer方法调 

用。如果设置为false,则串口缓冲区ComBuffer中的内容被清 

除,指针(位置)复位,在首次收到数据包的头部数据时,执 

行该动作;如果在rUB方法中等待的时间片大于TimeToWait, 

则表示一个数据包接收结束,设置为true,同时,令1Start为 

0,表示如果收到新的数据,则认为是下一个数据包中的内 

容。SetAvailable方法的源代码如下: 

private void SetAvailable(boolean bGet){ 

bAvailable=bGet; 

lf(bGet==false){ 

ComBuffer.clear(}: 

ComBuffer.rewind(): 

) 

IfIbGet==true)IStart=0: 

、 

5.public void DestroyReadThread() 

该公有方法用来将bDestroy的值设置为ture,从而让rUB 

方法从while循环中退出,达到杀死进程的目的。其源代码如 

下: 

public void DestroyReadThread(){ 

bDestroy=true;  。

) 

6 public byte[】GetComBuffer() 

该方法是公有方法,用来返回完整的数据包。输入缓冲区 

ComBuffer的指针(位置)即为数据包的长度。如果数据接收 

完成,则bAvailable为ture,就先取得数据的长度,然后,将 

ComBuffer缓冲区的指针(位置)复位,这样,就可以从位置0 

处开始读取给定的字节。数据读取完毕,就通过SetAvailable 

(false)方法销毁数据,以免数据被重复读取,如此模仿Visual 

Basic 6.0中的MSComm控件的动作。如果没有数据或者数据 

没有准备好,就返回null。 

public byte[、GetComBuffer(、 | 

in ̄bLen; 

b、谯枣U bReceive; 

i ̄bAvaliable= t ̄ue)f//数据接收结束 

been=ComBuf position l 

ee色 e=n洲 [bLen、 I| — 

COmBuffer rewind( 一 

COmBuffer.get(bReCeive,0,bLen): 0 

vaiiable faise); ,销毁数据l| 

return bReceivet j 

J 

eise return null;, 没 数据或数 准备姆 

、 

四、串口类的发布 

在NetBeans 5.0环境下完成代码编写后,可以单击项目名 

称,然后,选择生成项目,即可在项目的build\classes\Serila— 

Port目录下,看到OperateCOM.class和ReadCOM class。在C 

盘建立一个目录,如c:\JarPackage,将包含类文件的SerialPort 

文件夹复制到该文件夹。在DOS环境下进入JarPackage目录, 

输入如下命令(下划线所示): 

C:\JarPackage>iar cvf SerialPort. ar . 

即可得到将以上两个类打包后的jar文件。该命令的…e’ 

表示创建新的文档, “v”表示生成详细信息到标准输出上, 

…f’表示制定存档文件名。 

可以将该串口包复制到jre目录下的l;h中,并在CLASS— 

PATH系统环境变量中包含该包的绝对路径,即可通过“import 

SerialPort. ;”来使用串El类,并在DOS环境下利用javac命 

令对java源代码进行编译。如果在NetBeans 5.0环境下使用串 

El包,则需要添加库,并指出库的绝对路径。 

维普资讯

…E … …E ! … … k目 ‘ ■ 

实用第一 智慧密集 

。 。 。 。 … 

五、串口类的测试 

测试源代码除了需要引用上文生成的SerialPort包外,还 

4B,结果如下图所示。在串行通信中,字节最高位为1,可能 

由于字符集或系统环境原因,导致最高位为0;而字节oo通 

常作为高级语言中字符串的结束标志,这意味着字节oo后面 

的数据将被截去。从下图中可以看出,所有数据均被完整接 

收,并可以实现原样发送返回。在UTF一8的表示中,0xFF和 

0对应的字符不可见,0x66对应字符“f”,0x25对应字符 

“%”;在UTF一16BE的表示中,两个字节表示一个汉字, 

0xFF00是不可见汉字(或没有此汉字),0x6625表示汉字 

要使用文献【3】中的ComputerMonitor包,用来灵活地处理数 

据,并以需要的形式进行显示。测试程序首先初始化串口,然 

后,在while循环中读取数据,以各种形式显示,如果收到的 

数据包的第一个字节为0x21(即字符“!”)则退出程序。测 

试类SerialExample的源代码如下: 

import SerialPort。 

import ComputerMonitor. ; 

/**Creates a new instance of Seria\Example / 

public staricvoid main(String l】args){ 

OperateCOM SB=newOperateCOM(1.1 024); 

//open COM 1,the max length of package is 1 024 bytes 

f(SB.GetPortlD()2 一1){ 

System.out。print ( Nosuch port! ): 

System.exit 

) 

if(SB.Open( SerialExample ,100)==一1){ 

System.Out.println( Portinusenow! ); 

System.exit(1) 

、 一 

.f(SB SetParams(9600,8,1,0)==一1){ 

_System.out.println("Unsupported operation! ): 

System exitI 

、 一 

SB_StartCom(150) //延迟150毫秒 

while(true){ 。 

byte[】bData SB.ReadPort() 

if(bDataI nul\、 

System.out.printlnI"Receive and send back: 

System.out.println( UTF-8: + 

ByteProcess.BytesToEnString(bData)): 

System.out printlnI‘UTF一、6BE: + 

 lByteProcess UnicodeToString(bData)): 

System.out.println(,Bytes: + 

ByteProcess InsertSpaceToHexChars 

ByteProcess BytesToHexCharsIbData 

S8 WritePort(bData,0,bData,length); 

if(bData[0l==0x21){//Thefirstcharis 』 ,SOstopnow] 

SB.ClosePort(): 

retum; 

、 

、 “ 

) 

、 

Java例程在NetBeans 5.0环境下的调试方式运行,利用串 

口测试工具TestPort分别发送字节序列FF o0 66 25和21 4F 

与‘ 

“春”。由于第二个数据包的第一个字节是Ox2l,程序退出, 

与设计效果完全一样。 

六、结语 

本文利用Java Communications API函数,设计了Operate— 

COM与ReadCOM类,用来便捷地进行串口数据的收发,’并给 

出了应用实例和测试结果。该程序具有通用性,可以用于相关 

的嵌入式系统设备的二次开发中。完整的源代码可以从本刊网 

站下载。 

用户程序已亮戚 

串口类的测试效果图 

参考文献 

l马玉春。Java事件驱动的串行通信实现【J】。电脑编程技巧与 

维护,2007。 

2蒋清野。利用Java实现串口全双工通讯【EB/OL】.http:// 

WWW jspcn.net/htmlnews/1 10492979567 l 148 html,2007.5. 

3马玉春。Java通用数据处理技术【J】.电脑编程技巧与维护, 

2007. 

(收稿日期:2007年l1月26日)