由于安卓的开源性以及可拓展性,近些年,Android在各种智能设备上的使用越来越多,如电视机、机顶盒、车载系统以及公交刷卡系统等等。在我们的认识中Android系统是手机系统,它的物理接口一般只有usb host接口和耳机接口,但其实安卓支持各种各样的工业接口,如HDMI、usb、网口、串口等等。本文将来说一下安卓下的串口。
下图就是一块Android工业板,标圈的DB9(也叫RS232串口)就是串口中的一种形态
串行端口 ,即:SerialPort,简称串口,主要用于数据被逐位按顺序传送的通讯方式称为串口通讯(简单来讲就是按顺序一位一位地传输数据)。
串口一般有RS232和RS485之分,485串口可以使用RS-232转RS-485串口的转换器转换。
RS232:
232协议的串口是全双工 的,它允许数据同时接收和发送,但RS232的理论传输距离只有10米。
RS-485:
485是半双工的,半双工意味着同一时间只能收/发,就像是独木桥,同时只能有一个方向的人流通过,如果对向有来人则会造成数据丢失,RS485的理论距离是1200峭,通常如果要远距离使用的话会使用485串口,短距离则可以使用232。
无论是Android、windows还是linus,串口的使用都要以下几步:
打开串口
串口配置(一般为:波特率、数据位、停止位和奇偶校验)
串口操作(读/写,无非就是输入输出流的操作罢了)
关闭串口
下面说下在安卓下如果使用串口,现在大家使用的一般都是谷歌开源的一个项目:android-serialport-api。这里就不对这个项目过多介绍,有兴趣的同学可以直接看:https://github.com/cepr/android-serialport-api 我这里直接说下如果使用(以AS项目为例):
1、 创建jniLibs/armeabi目录(以存在跳过这步),把libserial_port.so拷贝进去
2、创建包名:android_serialport_api,并把SerialPort.java拷贝进去,SerialPort.java已经封装了串口的打开、配置、读写和关闭串口的方法,直接调用就行。注意:这里的包名和类名都不能改,否则会报错找不该类。
上代码,对应so库的串口操作类SerialPort.java:
package android_serialport_api;
import android.util.Log;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class SerialPort {
private static final String TAG = "SerialPort";
public FileDescriptor mFd;
private FileInputStream mFileInputStream;
private FileOutputStream mFileOutputStream;
public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException {
mFd = open(device.getAbsolutePath(), baudrate, flags);
if (mFd == null) {
Log.e(TAG, "native open returns null");
throw new IOException();
}
mFileInputStream = new FileInputStream(mFd);
mFileOutputStream = new FileOutputStream(mFd);
}
// Getters and setters
public InputStream getInputStream() {
return mFileInputStream;
}
public OutputStream getOutputStream() {
return mFileOutputStream;
}
// JNI(调用java本地接口,实现串口的打开和关闭)
private native static FileDescriptor open(String path, int baudrate, int flags);
public native void close();
static {//加载jni下的C文件库
Log.d(TAG, "本地库加载中");
System.loadLibrary("serial_port");
}
}
使用串口的工具类SerialPortUtils.java:
package android_serialport_api;
import android.util.Log;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
/**
* 串口监听工具
* Created by sam on 2018/3/31.
*/
public class SerialPortUtils {
private final String TAG = "SerialPortUtils";
private String path = "/dev/ttyS3";//串口名,安卓下一般为ttys1,ttys2... 而windows下则为com1,com2,com3... 我们平时用的usb口其实也是一个串口,所以用usb转232转换器就可以在电脑上使用串口了
private int baudrate = 19200;//波特率
public boolean serialPortStatus = false; //是否打开串口标志
public String data_;
public boolean threadStatus; //线程状态,为了安全终止线程
public SerialPort serialPort = null;
public InputStream inputStream = null;
public OutputStream outputStream = null;
/**
* 打开串口
* @return serialPort串口对象
*/
public SerialPort openSerialPort(){
try {
serialPort = new SerialPort(new File(path),baudrate,0);
this.serialPortStatus = true;
threadStatus = false; //线程状态
//获取打开的串口中的输入输出流,以便于串口数据的收发
inputStream = serialPort.getInputStream();
outputStream = serialPort.getOutputStream();
new ReadThread().start(); //开始线程监控是否有数据要接收
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "openSerialPort: 打开串口异常:" + e.toString());
return serialPort;
}
Log.d(TAG, "openSerialPort: 打开串口");
return serialPort;
}
/**
* 关闭串口
*/
public void closeSerialPort(){
try {
inputStream.close();
outputStream.close();
this.serialPortStatus = false;
this.threadStatus = true; //线程状态
serialPort.close();
} catch (Exception e) {
Log.e(TAG, "closeSerialPort: 关闭串口异常:"+e.toString());
return;
}
Log.d(TAG, "closeSerialPort: 关闭串口成功");
}
/**
* 发送串口指令
* @param data
*/
public void sendSerialPort(byte[] data){
//Log.d(TAG, "sendSerialPort: 发送数据");
try {
if (data.length > 0) {
outputStream.write(data);
//注意:有些工控机上需要以\n或者\r来表示数据发送完毕,所以在write完毕后要多输出个\r,这个看个人情况
//outputStream.write('\n');
//outputStream.write('\r'+'\n');
outputStream.flush();
//Log.d(TAG, "sendSerialPort: 串口数据发送成功");
}
} catch (IOException e) {
Log.e(TAG, "sendSerialPort: 串口数据发送失败:"+e.toString());
}
}
/**
* 因为这里是阻塞的,所以单开一线程来读数据
*/
private class ReadThread extends Thread {
@Override
public void run() {
super.run();
//判断进程是否在运行,更安全的结束进程
while (!threadStatus){
//Log.d(TAG, "监听打卡...");
//64 1024
byte[] buffer = new byte[16];
int size; //读取数据的大小
try {
size = inputStream.read(buffer);
if (size > 0){
Log.d(TAG, "接收到数据:" + new String(buffer));
//注意,多数情况下使用串口的单片机都是使用十六井控进行输送,所以这里可能还需要把buffer转换为十六进制
}
} catch (IOException e) {
Log.e(TAG, "run: 数据读取异常:" +e.toString());
}
}
}
}
/**
* Utility class to convert a byte array to a hexadecimal string.
*
* @param bytes Bytes to convert
* @return String, containing hexadecimal representation.
*/
public static String ByteArrayToHexString(byte[] bytes) {
final char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
char[] hexChars = new char[bytes.length * 2];
int v;
for ( int j = 0; j < bytes.length; j++ ) {
v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
/**
* 将byte[]转为各种进制的字符串
* @param bytes byte[]
* @param radix 基数可以转换进制的范围,从Character.MIN_RADIX到Character.MAX_RADIX,超出范围后变为10进制
* @return 转换后的字符串
*/
public static String binary(byte[] bytes, int radix){
return new BigInteger(1, bytes).toString(radix);// 这里的1代表正数
}
}
好了,安卓下串口的开发就是这些了,下一篇博文将介绍Android下如何与读卡器进行交互。
最后附上so库:libserial_port.so