1. 解析SerialPort API 串口通信例子

首先分析一下例子中的类结构 :

通过类结构可知,最主要的还是在SerialPortJNI.java 类 ,该类写了一些Native 方法处理打开与关闭 串口 接收 发送的

SerialPort.Java 代码如下 :


packagecom.dwin.navy.serialportapi;


importjava.io.FileDescriptor;


importandroid.util.Log;


/**

*串口JNI

*

*@authordwin

*

*/

publicclassSerialPortJNI{


static{

Log.i("NativeClass","beforeloadlibrary");

System.loadLibrary("serialport");

Log.i("NativeClass","afterloadlibrary");

}


publicFileDescriptormFd;


publicStringmDevNum;

publicintmSpeed;

publicintmDataBits;

publicintmStopBits;

publicintmParity;


publicintRS485ModFp=-1;


publicstaticintRS485Read=0;

publicstaticintRS485Write=1;


publicnativeintsetSpeed(FileDescriptorfd,intspeed);


publicnativeintsetParity(FileDescriptorfd,intdataBits,intstopBits,

intparity);


publicnativeFileDescriptoropenDev(StringdevNum);


publicnativeFileDescriptoropen485Dev(StringdevNum);


publicnativeintcloseDev(FileDescriptorfd);


publicnativeintclose485Dev(FileDescriptorfd);


publicnativeintreadBytes(FileDescriptorfd,byte[]buffer,intlength);


publicnativebooleanwriteBytes(FileDescriptorfd,byte[]buffer,

intlength);


publicnativeintset485mod(intmode);

}

红色区域 先申明一个static 静态域 加载.so 动态库文件 .so通过JNI方式生成 保存在libs目录下 ,由于本例子使用的cpu为 mips(默认为arm) 所以 .so库文件 将存入mips文件夹下

还有打开串口,关闭串口,485模式下开关方式 ,接收 与发送 byte


1> SerialPort API 处理了接收与发送 文本与十六进制 两种模式下接收与发送数据

UI布局如下 :

2>在电脑中插入usb转串口工具 ,然后在app中打开串口

SerialPortOpt.java 继承了 SerialPortJNI.Java 类 调用相应的JNI接口

SerialPortOpt.java 类大致如下 :


packagecom.dwin.navy.serialportapi;


importjava.io.FileDescriptor;

importjava.io.FileInputStream;

importjava.io.FileOutputStream;

importjava.io.InputStream;

importjava.io.OutputStream;


importandroid.util.Log;


/**

*调用JNI的串口

*

*@authordwin

*

*/

publicclassSerailPortOptextendsSerialPortJNI{


privatestaticfinalStringTAG="SerialPort";


privateFileInputStreammFileInputStream;

privateFileOutputStreammFileOutputStream;


publicFileDescriptoropenDev(StringdevNum){

super.mFd=super.openDev(devNum);

if(super.mFd==null){

Log.e(TAG,"nativeopenreturnsnull");

returnnull;

}

mFileInputStream=newFileInputStream(super.mFd);

mFileOutputStream=newFileOutputStream(super.mFd);

returnsuper.mFd;

}


publicFileDescriptoropen485Dev(StringdevNum){

super.mFd=super.open485Dev(devNum);

if(super.mFd==null){

Log.e(TAG,"nativeopenreturnsnull");

returnnull;

}

mFileInputStream=newFileInputStream(super.mFd);

mFileOutputStream=newFileOutputStream(super.mFd);

returnsuper.mFd;

}


publicInputStreamgetInputStream(){

returnmFileInputStream;

}


publicOutputStreamgetOutputStream(){

returnmFileOutputStream;

}


publicintsetSpeed(FileDescriptoroptFd,intspeed){

returnsuper.setSpeed(optFd,speed);

}


publicintsetParity(FileDescriptoroptFd,intdatabits,intstopbits,

intparity){

returnsuper.setParity(optFd,databits,stopbits,parity);

}


publicintcloseDev(FileDescriptoroptFd){

intretStatus;

retStatus=super.closeDev(optFd);

super.mFd=null;

returnretStatus;

}


publicintclose485Dev(FileDescriptoroptFd){

intretStatus;

retStatus=super.close485Dev(optFd);

super.mFd=null;

returnretStatus;

}


publicintreadBytes(FileDescriptorfd,byte[]buffer,intlength){

returnsuper.readBytes(fd,buffer,length);

}


publicbooleanwriteBytes(FileDescriptorfd,byte[]buffer,intlength){

returnsuper.writeBytes(fd,buffer,length);

}


publicintreadBytes(FileDescriptorfd,byte[]buffer){

returnsuper.readBytes(fd,buffer,buffer.length);

}


publicbooleanwriteBytes(FileDescriptorfd,byte[]buffer){

returnsuper.writeBytes(fd,buffer,buffer.length);

}


publicintreadBytes(byte[]buffer){

returnsuper.readBytes(mFd,buffer,buffer.length);

}


publicbooleanwriteBytes(byte[]buffer){

returnsuper.writeBytes(mFd,buffer,buffer.length);

}


publicintread485Bytes(FileDescriptorfd,byte[]buffer,intlength){

returnsuper.readBytes(fd,buffer,length);

}


publicbooleanwrite485Bytes(FileDescriptorfd,byte[]buffer,intlength){

booleanret;

super.set485mod(RS485Write);

ret=super.writeBytes(fd,buffer,length);

super.set485mod(RS485Read);

returnret;

}


publicintread485Bytes(FileDescriptorfd,byte[]buffer){

returnsuper.readBytes(fd,buffer,buffer.length);

}


publicbooleanwrite485Bytes(FileDescriptorfd,byte[]buffer){

booleanret;

super.set485mod(RS485Write);

ret=super.writeBytes(fd,buffer,buffer.length);

super.set485mod(RS485Read);

returnret;

}


publicintread485Bytes(byte[]buffer){

returnsuper.readBytes(mFd,buffer,buffer.length);

}


publicbooleanwrite485Bytes(byte[]buffer){

booleanret;

super.set485mod(RS485Write);

ret=super.writeBytes(mFd,buffer,buffer.length);

super.set485mod(RS485Read);

returnret;

}


}


在openDev(String str)方法中调用父类的方法 ,将返回一个文件描述对象FileDescriptor ,然后初始化FileInputStream,FileOutputStream 输入输出IO流对象 ,

还有读写byte字节数组的方法


3>申明或者实例化一个类用来调用和实现 这些方法 前两个类只是申明 相应的方法并没有实现相应的方法体

定义一个SerialPort 类

SerialPort.Java :


packagecom.dwin.dwinapi;


importjava.io.FileDescriptor;

importjava.io.IOException;

importjava.io.InputStream;

importjava.io.UnsupportedEncodingException;


importcom.dwin.navy.serialportapi.SerailPortOpt;


importandroid.content.Context;

importandroid.util.Log;


/**

*自定义串口对象

*

*@authorF

*

*/

publicclassSerialPort{

Contextcontext;


/**

*自定义串口对象

*/

privatestaticSerialPortserialPort;


/**

*调用JNI的串口

*/

privateSerailPortOptserialportopt;


/**

*读取数据线程

*/


/**

*串口接收到的流

*/

privateInputStreammInputStream;


/**

*用来判断串口是否已打开

*/

publicbooleanisOpen=false;


/*

*接收到的数据

*/

Stringdata;


/**

*实例化并打开串口对象

*

*@paramdevNum

*串口号S0,S1,S2,S3,S4

*@paramdataBits

*数据位

*@paramspeed

*波特率

*@paramstopBits

*停止位

*@paramparity

*校验位

*/

publicSerialPort(StringdevNum,intspeed,intdataBits,intstopBits,

intparity){

serialportopt=newSerailPortOpt();

openSerial(devNum,speed,dataBits,stopBits,parity);

}


/**

*打开串口时传入参数,可以指定打开某个串口,并设置相应的参数

*

*@paramdevNum

*串口号COM0,COM1,COM2,COM3,COM4

*@paramdataBits

*数据位

*@paramspeed

*波特率

*@paramstopBits

*停止位

*@paramparity

*校验位

*@return

*/

privatebooleanopenSerial(StringdevNum,intspeed,intdataBits,

intstopBits,intparity){

serialportopt.mDevNum=devNum;

serialportopt.mDataBits=dataBits;

serialportopt.mSpeed=speed;

serialportopt.mStopBits=stopBits;

serialportopt.mParity=parity;


//打开串口

FileDescriptorfd=serialportopt.openDev(serialportopt.mDevNum);

if(fd==null){

returnfalse;//串口打开失败

}else{

//设置串口参数

serialportopt.setSpeed(fd,speed);

serialportopt.setParity(fd,dataBits,stopBits,parity);

mInputStream=serialportopt.getInputStream();

isOpen=true;

returntrue;

}

}


/**

*关闭串口

*/

publicvoidcloseSerial(){

if(serialportopt.mFd!=null){

serialportopt.closeDev(serialportopt.mFd);

isOpen=false;

}

}


/**

*发送数据

*

*@paramdata

*数据内容

*/

publicvoidsendData(Stringdata,Stringtype){

try{

serialportopt.writeBytes(type.equals("HEX")?HexString2Bytes(data

.length()%2==1?data+="0":data.replace("",""))

:HexString2Bytes(toHexString(data)));

}catch(Exceptione){


}

}


/**

*接收数据

*

*@param收发数据类型

*@return接收到的字符串

*/

publicStringreceiveData(Stringtype){

byte[]buf=newbyte[1024];

intsize;

if(mInputStream==null){

returnnull;

}

size=serialportopt.readBytes(buf);

if(size>0){

try{

data=type.equals("HEX")?bytesToHexString(buf,size)

:newString(buf,0,size,"gb2312").trim().toString();

}catch(UnsupportedEncodingExceptione){

e.printStackTrace();

}

returndata;

}else{

returnnull;

}

}


/**

*转化字符串为十六进制编码

*

*@params

*@return

*/

privateStringtoHexString(Strings){

Stringstr="";

for(inti=0;i<s.length();i++){

intch=(int)s.charAt(i);

Strings4=Integer.toHexString(ch);

str=str+s4;

}

returnstr;

}


/**

*将指定字符串src,以每两个字符分割转换为16进制形式如:"2B44EFD9"-->byte[]{0x2B,0x44,0xEF,

*0xD9}

*

*@paramsrc

*String

*@returnbyte[]

*/

privatestaticbyte[]HexString2Bytes(Stringsrc){

byte[]ret=newbyte[src.length()/2];

byte[]tmp=src.getBytes();

for(inti=0;i<tmp.length/2;i++){

ret[i]=uniteBytes(tmp[i*2],tmp[i*2+1]);

}

returnret;

}


/**

*将Hex数组转换为Hex字符串

*

*@paramsrc

*@paramsize

*@return

*/

publicstaticStringbytesToHexString(byte[]src,intsize){

Stringret="";

if(src==null||size<=0){

returnnull;

}

for(inti=0;i<size;i++){

Stringhex=Integer.toHexString(src[i]&0xFF);

if(hex.length()<2){

hex="0"+hex;

}

hex+="";

ret+=hex;

}

returnret.toUpperCase();

}


/**

*将两个ASCII字符合成一个字节;如:"EF"-->0xEF

*

*@paramsrc0

*byte

*@paramsrc1

*byte

*@returnbyte

*/

privatestaticbyteuniteBytes(bytesrc0,bytesrc1){

byte_b0=Byte.decode("0x"+newString(newbyte[]{src0}))

.byteValue();

_b0=(byte)(_b0<<4);

byte_b1=Byte.decode("0x"+newString(newbyte[]{src1}))

.byteValue();

byteret=(byte)(_b0^_b1);

returnret;

}


}


剩下的就是MainActivty中 ,打开串口 创建两个线程,ReceiverThread,SendThread 先 ,Handler 异步更新UI

privateHandlermHandler=newHandler(){

publicvoidhandleMessage(android.os.Messagemsg){

switch(msg.what){

case1:

Datedate=newDate();

eTextShowMsg.append("["+date.getMinutes()+":"

+date.getSeconds()+"]"+(CharSequence)msg.obj);

break;

default:

break;

}

};

};


/**

*接收数据线程

*/

classReceiveThreadextendsThread{

publicvoidrun(){


while(serialPort.isOpen){

if(isReceive){

Stringtype=togBtnShowDataType.getText().toString()

.trim();

Stringdata=serialPort.receiveData(type);

if(data!=null){

Messagemsg=newMessage();

msg.what=1;

msg.obj=data;

System.out.println(data+"<<<<<<<<==========data");

mHandler.sendMessage(msg);

}

}

try{

Thread.sleep(1000);

}catch(InterruptedExceptione){

e.printStackTrace();

}

}

}

}


当串口接收到数据每一秒更新一次UI,根据需求可转换为十六进制与字符