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)://保存数据
s
~
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日)
发布评论