一、前言

由于安卓的开源性以及可拓展性,近些年,Android在各种智能设备上的使用越来越多,如电视机、机顶盒、车载系统以及公交刷卡系统等等。在我们的认识中Android系统是手机系统,它的物理接口一般只有usb host接口和耳机接口,但其实安卓支持各种各样的工业接口,如HDMI、usb、网口、串口等等。本文将来说一下安卓下的串口。

下图就是一块Android工业板,标圈的DB9(也叫RS232串口)就是串口中的一种形态

 

二、什么是串口?

串行端口 ,即:SerialPort,简称串口,主要用于数据被逐位按顺序传送的通讯方式称为串口通讯(简单来讲就是按顺序一位一位地传输数据)。

 

三、串口的一般形态

串口一般有RS232和RS485之分,485串口可以使用RS-232RS-485串口的转换器转换。

RS232:

232协议的串口是全双工 的,它允许数据同时接收和发送,但RS232的理论传输距离只有10米。

RS-485:

485是半双工的,半双工意味着同一时间只能收/发,就像是独木桥,同时只能有一个方向的人流通过,如果对向有来人则会造成数据丢失,RS485的理论距离是1200峭,通常如果要远距离使用的话会使用485串口,短距离则可以使用232。

四、串口的使用

无论是Android、windows还是linus,串口的使用都要以下几步:

  1. 打开串口

  2. 串口配置(一般为:波特率、数据位、停止位和奇偶校验

  3. 串口操作(读/写,无非就是输入输出流的操作罢了

  4. 关闭串口

五、编码

下面说下在安卓下如果使用串口,现在大家使用的一般都是谷歌开源的一个项目: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

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐