Android串口使用总结

准备工作

  • NDK环境
  • 串口通信使用的C文件

注意:c文件中需要修改函数名为当前包名的路径,c文件和.h头文件都需要修改

接入底层库

使用SO库引入

  1. main目录下新建jni录,将C文件置于该处
  2. 新建Android.mk、Application.mk文件并配置
  3. 编译项目,得到编译好的so库文件,生成目录默认为:app\build\intermediates\jniLibs\app\debug\armeabi,如果编译的是release版本则在releasse目录下
  4. main目录下新建目录jniLibs,将生成的so库拷贝到此
  5. 配置app目录下的gradle文件
buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        ndk {
            moduleName "serial_port"
            ldLibs "log", "z", "m"
            abiFilters "armeabi", "x86"
        }
    }

    debug {
        ndk {
            moduleName "serial_port"
            ldLibs "log", "z", "m"
            abiFilters "armeabi", "x86"
        }
    }
}
  1. 代码中创建Class文件,加载这个so库文件,并定义打开、关闭串口的方法

CMake方式引入

  1. main目录下新建cpp文件夹,将c源文件置于此
  2. 配置CMakeLists.txt文件
cmake_minimum_required(VERSION 3.4.1)

# 查找cpp目录下的所有源文件
# 并将名称保存到 DIR_LIB_SRC 变量
aux_source_directory(src/main/cpp/ DIR_LIB_SRC)

#设置源文件
add_library(SerialPort SHARED ${DIR_LIB_SRC})

#表明头文件位置
#include_directories(src/main/cpp/)

find_library( # Sets the name of the path variable.
        log-lib
        log)
target_link_libraries( # Specifies the target library.
        SerialPort
        ${log-lib})
  1. 配置app下gradle文件
android {   
    defaultConfig {
        ...
        externalNativeBuild {
            cmake {
                cppFlags ""
            }
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}
  1. 代码中创建Class文件,加载这个so库文件,并定义打开、关闭串口的方法

打开/关闭串口

jni中的打开关闭

C文件中定义了两个方法,分别为打开、关闭串口。打开串口方法具有返回值,返回值为FileDescriptor对象,我们通过这个对象实现对串口的数据读取和写入操作

打开串口

查询串口

一些定制的设备可能会有很多串口,我们应用究竟需要打开哪一个串口呢?一方面,可以询问设备厂商我们需要连接的串口名,另一方面就只能列出所有串口然后一个一个试了

查询所有串口:
定义SerialPortFinder类,通过读取/proc/tty/drivers文件,列举出设备的所有串口和路径

方法:SerialPortFinder中的getAllDevices()方法,具体实现见代码SerialPortFinder

打开

找到相应的串口后,调用jni方法打开串口,打开时需要传入三个参数,分别是串口路径,波特率,打开方式

public SerialPort getSerialPort() throws SecurityException, IOException, InvalidParameterException {
    if (mSerialPort == null) {
        //这里是打开了/dev/ttyS3 串口,波特率9600,
        mSerialPort = new SerialPort(new File("/dev/ttyS3"), 9600, 0);
    }
    return mSerialPort;
}

通过打开串口,得到FileDescriptor对象,根据该对象创建一个输入输出流,从而实现读写操作。

关闭串口

使用完毕后、退出应用前,需要关闭串口。相当于文件操作一样需要关闭文件流,串口通信实质上也是文件操作,所以需要关闭

/**
 * 关闭串口
 * 使用结束时记得关闭串口
 */
public void closeSerialPort() {
    if (mSerialPort != null) {
        //调用native方法关闭串口
        mSerialPort.close();
        mSerialPort = null;
    }
}

发送/读取数据

打开串口时获取到了输入输出流,那么接下来的发送和读取就是对这个流进行操作了。

发送数据:

发送数据为byte数组,如果是字符串啊、十六进制指令啊等等,都需要转换成byte数组发送。向我们的输出流写数据即可。

public void sendBufferToSerial(byte[] data) {
    try {
        mFileOutputStream.write(data);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

读取数据:

读取数据相对于发送数据而言,相对步骤会多一点,因为我们不知道串口什么时候会给我发送数据过来,所以需要开启一个子线程不断地从输入流取数据,当取到数据长度不为0时,对数据进行解析,从而进行下一步操作

class ReadThread extends Thread {
    @Override
    public void run() {
        super.run();
        while (!isInterrupted()) {
            int size;
            final byte[] buffer;
            try {
                buffer = new byte[128];
                if (mFileInputStream == null) return;
                size = mFileInputStream.read(buffer);

                if (size > 0) {
                    //回调
                    if (listener != null)
                        listener.receiveData(size, buffer);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}


/**
 * 开始接收串口数据
 *
 * @param onReceiveDataListener 数据回调
 */
public void startReceiveData(OnReceiveDataListener onReceiveDataListener) {
    readThread = new ReadThread();
    readThread.start();
    listener = onReceiveDataListener;
}

/**
 * 停止接收数据
 */
public void stopReceiveData() {
    if (readThread != null) {
        if (readThread.isInterrupted())
            readThread.interrupt();
    }
    readThread = null;
    listener = null;
}

读取到的同样是byte数组的数据,需要对数据进行解析,一般设备供应商会提供相应的解析方式。

串口的读取、发送数据,都会有2-5个字节作为识别位,一般都是0xFF开头,每个包都是统一的,发送和解析数据时需要注意

使用

可以选择在Application中打开串口,然后在项目的BaseActivity或者BaseFragment中定义好发送、读取数据的方法,需要使用的地方直接调用

C源文件:

termios.h

SerialPort.h

SerialPort.c

Github

猜你喜欢

转载自blog.csdn.net/lizebin_bin/article/details/86159524
今日推荐