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,根據需求可轉換爲十六進制與字符