1. 解析SerialPort API 串口通信例子
首先分析一下例子中的类结构 :
通过类结构可知,最主要的还是在SerialPortJNI.java 类 ,该类写了一些Native 方法处理打开与关闭 串口 接收 发送的
SerialPort.Java 代码如下 :
package com.dwin.navy.serialportapi;
import java.io.FileDescriptor;
import android.util.Log;
/**
* 串口JNI
*
* @author dwin
*
*/
public class SerialPortJNI {
static {
Log.i("NativeClass", "before load library");
System.loadLibrary("serialport");
Log.i("NativeClass", "after load library");
}
public FileDescriptor mFd;
public String mDevNum;
public int mSpeed;
public int mDataBits;
public int mStopBits;
public int mParity;
public int RS485ModFp = -1;
public static int RS485Read = 0;
public static int RS485Write = 1;
public native int setSpeed(FileDescriptor fd, int speed);
public native int setParity(FileDescriptor fd, int dataBits, int stopBits,
int parity);
public native FileDescriptor openDev(String devNum);
public native FileDescriptor open485Dev(String devNum);
public native int closeDev(FileDescriptor fd);
public native int close485Dev(FileDescriptor fd);
public native int readBytes(FileDescriptor fd, byte[] buffer, int length);
public native boolean writeBytes(FileDescriptor fd, byte[] buffer,
int length);
public native int set485mod(int mode);
}
红色区域 先申明一个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 类大致如下 :
package com.dwin.navy.serialportapi;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import android.util.Log;
/**
* 调用JNI的串口
*
* @author dwin
*
*/
public class SerailPortOpt extends SerialPortJNI {
private static final String TAG = "SerialPort";
private FileInputStream mFileInputStream;
private FileOutputStream mFileOutputStream;
public FileDescriptor openDev(String devNum) {
super.mFd = super.openDev(devNum);
if (super.mFd == null) {
Log.e(TAG, "native open returns null");
return null;
}
mFileInputStream = new FileInputStream(super.mFd);
mFileOutputStream = new FileOutputStream(super.mFd);
return super.mFd;
}
public FileDescriptor open485Dev(String devNum) {
super.mFd = super.open485Dev(devNum);
if (super.mFd == null) {
Log.e(TAG, "native open returns null");
return null;
}
mFileInputStream = new FileInputStream(super.mFd);
mFileOutputStream = new FileOutputStream(super.mFd);
return super.mFd;
}
public InputStream getInputStream() {
return mFileInputStream;
}
public OutputStream getOutputStream() {
return mFileOutputStream;
}
public int setSpeed(FileDescriptor optFd, int speed) {
return super.setSpeed(optFd, speed);
}
public int setParity(FileDescriptor optFd, int databits, int stopbits,
int parity) {
return super.setParity(optFd, databits, stopbits, parity);
}
public int closeDev(FileDescriptor optFd) {
int retStatus;
retStatus = super.closeDev(optFd);
super.mFd = null;
return retStatus;
}
public int close485Dev(FileDescriptor optFd) {
int retStatus;
retStatus = super.close485Dev(optFd);
super.mFd = null;
return retStatus;
}
public int readBytes(FileDescriptor fd, byte[] buffer, int length) {
return super.readBytes(fd, buffer, length);
}
public boolean writeBytes(FileDescriptor fd, byte[] buffer, int length) {
return super.writeBytes(fd, buffer, length);
}
public int readBytes(FileDescriptor fd, byte[] buffer) {
return super.readBytes(fd, buffer, buffer.length);
}
public boolean writeBytes(FileDescriptor fd, byte[] buffer) {
return super.writeBytes(fd, buffer, buffer.length);
}
public int readBytes(byte[] buffer) {
return super.readBytes(mFd, buffer, buffer.length);
}
public boolean writeBytes(byte[] buffer) {
return super.writeBytes(mFd, buffer, buffer.length);
}
public int read485Bytes(FileDescriptor fd, byte[] buffer, int length) {
return super.readBytes(fd, buffer, length);
}
public boolean write485Bytes(FileDescriptor fd, byte[] buffer, int length) {
boolean ret;
super.set485mod(RS485Write);
ret = super.writeBytes(fd, buffer, length);
super.set485mod(RS485Read);
return ret;
}
public int read485Bytes(FileDescriptor fd, byte[] buffer) {
return super.readBytes(fd, buffer, buffer.length);
}
public boolean write485Bytes(FileDescriptor fd, byte[] buffer) {
boolean ret;
super.set485mod(RS485Write);
ret = super.writeBytes(fd, buffer, buffer.length);
super.set485mod(RS485Read);
return ret;
}
public int read485Bytes(byte[] buffer) {
return super.readBytes(mFd, buffer, buffer.length);
}
public boolean write485Bytes(byte[] buffer) {
boolean ret;
super.set485mod(RS485Write);
ret = super.writeBytes(mFd, buffer, buffer.length);
super.set485mod(RS485Read);
return ret;
}
}
在openDev(String str)方法中调用父类的方法 ,将返回一个文件描述对象FileDescriptor ,然后初始化 FileInputStream,FileOutputStream 输入输出IO流对象 ,
还有读写byte字节数组的方法
3>申明或者实例化一个类用来调用和实现 这些方法 前两个类只是申明 相应的方法并没有实现相应的方法体
定义一个SerialPort 类
SerialPort.Java :
package com.dwin.dwinapi;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import com.dwin.navy.serialportapi.SerailPortOpt;
import android.content.Context;
import android.util.Log;
/**
* 自定义串口对象
*
* @author F
*
*/
public class SerialPort {
Context context;
/**
* 自定义串口对象
*/
private static SerialPort serialPort;
/**
* 调用JNI的串口
*/
private SerailPortOpt serialportopt;
/**
* 读取数据线程
*/
/**
* 串口接收到的流
*/
private InputStream mInputStream;
/**
* 用来判断串口是否已打开
*/
public boolean isOpen = false;
/*
* 接收到的数据
*/
String data;
/**
* 实例化并打开串口对象
*
* @param devNum
* 串口号 S0,S1,S2,S3,S4
* @param dataBits
* 数据位
* @param speed
* 波特率
* @param stopBits
* 停止位
* @param parity
* 校验位
*/
public SerialPort(String devNum, int speed, int dataBits, int stopBits,
int parity) {
serialportopt = new SerailPortOpt();
openSerial(devNum, speed, dataBits, stopBits, parity);
}
/**
* 打开串口时传入参数,可以指定打开某个串口,并设置相应的参数
*
* @param devNum
* 串口号 COM0,COM1,COM2,COM3,COM4
* @param dataBits
* 数据位
* @param speed
* 波特率
* @param stopBits
* 停止位
* @param parity
* 校验位
* @return
*/
private boolean openSerial(String devNum, int speed, int dataBits,
int stopBits, int parity) {
serialportopt.mDevNum = devNum;
serialportopt.mDataBits = dataBits;
serialportopt.mSpeed = speed;
serialportopt.mStopBits = stopBits;
serialportopt.mParity = parity;
// 打开串口
FileDescriptor fd = serialportopt.openDev(serialportopt.mDevNum);
if (fd == null) {
return false;// 串口打开失败
} else {
// 设置串口参数
serialportopt.setSpeed(fd, speed);
serialportopt.setParity(fd, dataBits, stopBits, parity);
mInputStream = serialportopt.getInputStream();
isOpen = true;
return true;
}
}
/**
* 关闭串口
*/
public void closeSerial() {
if (serialportopt.mFd != null) {
serialportopt.closeDev(serialportopt.mFd);
isOpen = false;
}
}
/**
* 发送数据
*
* @param data
* 数据内容
*/
public void sendData(String data, String type) {
try {
serialportopt.writeBytes(type.equals("HEX") ? HexString2Bytes(data
.length() % 2 == 1 ? data += "0" : data.replace(" ", ""))
: HexString2Bytes(toHexString(data)));
} catch (Exception e) {
}
}
/**
* 接收数据
*
* @param 收发数据类型
* @return 接收到的字符串
*/
public String receiveData(String type) {
byte[] buf = new byte[1024];
int size;
if (mInputStream == null) {
return null;
}
size = serialportopt.readBytes(buf);
if (size > 0) {
try {
data = type.equals("HEX") ? bytesToHexString(buf, size)
: new String(buf, 0, size, "gb2312").trim().toString();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return data;
} else {
return null;
}
}
/**
* 转化字符串为十六进制编码
*
* @param s
* @return
*/
private String toHexString(String s) {
String str = "";
for (int i = 0; i < s.length(); i++) {
int ch = (int) s.charAt(i);
String s4 = Integer.toHexString(ch);
str = str + s4;
}
return str;
}
/**
* 将指定字符串src,以每两个字符分割转换为16进制形式 如:"2B44EFD9" --> byte[]{0x2B, 0x44, 0xEF,
* 0xD9}
*
* @param src
* String
* @return byte[]
*/
private static byte[] HexString2Bytes(String src) {
byte[] ret = new byte[src.length() / 2];
byte[] tmp = src.getBytes();
for (int i = 0; i < tmp.length / 2; i++) {
ret[i] = uniteBytes(tmp[i * 2], tmp[i * 2 + 1]);
}
return ret;
}
/**
* 将Hex数组转换为Hex字符串
*
* @param src
* @param size
* @return
*/
public static String bytesToHexString(byte[] src, int size) {
String ret = "";
if (src == null || size <= 0) {
return null;
}
for (int i = 0; i < size; i++) {
String hex = Integer.toHexString(src[i] & 0xFF);
if (hex.length() < 2) {
hex = "0" + hex;
}
hex += " ";
ret += hex;
}
return ret.toUpperCase();
}
/**
* 将两个ASCII字符合成一个字节; 如:"EF"--> 0xEF
*
* @param src0
* byte
* @param src1
* byte
* @return byte
*/
private static byte uniteBytes(byte src0, byte src1) {
byte _b0 = Byte.decode("0x" + new String(new byte[] { src0 }))
.byteValue();
_b0 = (byte) (_b0 << 4);
byte _b1 = Byte.decode("0x" + new String(new byte[] { src1 }))
.byteValue();
byte ret = (byte) (_b0 ^ _b1);
return ret;
}
}
剩下的就是MainActivty中 ,打开串口 创建两个线程,ReceiverThread,SendThread 先 ,Handler 异步更新UI
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 1:
Date date = new Date();
eTextShowMsg.append("[" + date.getMinutes() + ":"
+ date.getSeconds() + "] " + (CharSequence) msg.obj);
break;
default:
break;
}
};
};
/**
* 接收数据线程
*/
class ReceiveThread extends Thread {
public void run() {
while (serialPort.isOpen) {
if (isReceive) {
String type = togBtnShowDataType.getText().toString()
.trim();
String data = serialPort.receiveData(type);
if (data != null) {
Message msg = new Message();
msg.what = 1;
msg.obj = data;
System.out.println(data + "<<<<<<<<==========data");
mHandler.sendMessage(msg);
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
当串口接收到数据 每一秒更新一次UI,根据需求可转换为十六进制与字符
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。