Android 开发笔记 —— 串口通信(非谷歌开源框架 serialPort api)

声明:如果项目对串口的读取频率要求不高,请参考 serialPort api 框架的串口通信文章。该文章使用的是 Linux 读取串口设备文件的方式,工具为 Android Studio 3.3。

创建调用设备的头文件和源文件

首先创建一个支持 c++ 的项目,在 cpp 文件夹下创建 include 头文件夹。新建一个头文件,如下图:
这里写图片描述
注意:这里的头文件最好和源文件名称保持一致(规范,利人利己)。
这里是代码,如果有不了解的地方请留言,有必要的地方都有注释(为什么看起来像 Java 写法?别在意,笔者就是一个写 Java 的。注释为什么是这么蹩脚的英文?大家能看懂就好):

DevUart.h

//
// Created by BevisWang on 2018/7/17.
//

#ifndef BLOGPROJECT_DEV_UART_H
#define BLOGPROJECT_DEV_UART_H

#include <sys/ioctl.h>

#include "android/log.h"

typedef unsigned char BYTE;

static const char *DEV_UART_TAG = "UartLib";
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, DEV_UART_TAG, fmt, ##args)
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, DEV_UART_TAG, fmt, ##args)

/** define states. */
#define FALSE  0
#define TRUE   1

/** define device path. */
#define UART_DEVICE_4     "/dev/ttyS4"
#define UART_DEVICE_6     "/dev/ttyS6"

/** Serial port config param. */
struct UartConfig {
    int baudrate;   // read speed
    int databits;   // one of 7,8
    int stopbits;   // one of 1,2
    int parity;     // one of N,E,O,S
};

/** Serial port device UART class. */
class DevUart {
private:
    const char *path;  // device path
    int fd;             // device
    bool isClose;       // is close fd

    /**
     * @brief  Set serial port speed (baudrate).
     * @param  fd     type int device file
     * @param  speed  type int serial port baurate
     * @return  is success
     */
    int setSpeed(int fd, int speed);

    /**
     * @brief   Set data bits,stop bits and parity.
     * @param  fd       type int device file
     * @param  databits type int data bits,one of 7,8
     * @param  stopbits type int stop bits,one of 1,2
     * @param  parity   type int parity,one of N,E,O,S
    */
    int setParity(int fd, int databits, int stopbits, int parity);

public:
    DevUart();

    /**
     * @brief Construction method.
     * @param path serial port device path
     */
    DevUart(const char *path);

    /**
     * @brief Open serial port device.
     * @param config serial port device config param
     * @return is success
     */
    int openUart(UartConfig config);

    /**
     * @brief Read device data.
     * @param data read data
     * @param timeval read time
     * @return data length
     */
    int readData(BYTE *data,int size);

    /**
     * @brief Write serial port data.
     * @param data serial port data
     * @return is success
     */
    int writeData(BYTE *data, int len);

    /** @brief Close serial port device. */
    void closeUart();

    /**
     * @brief Get baudrate by int.
     * @param baudrate int
     * @return baudrate
     */
    speed_t getBaudrate(int baudrate);
};

#endif //BLOGPROJECT_DEV_UART_H

DevUart.cpp

#include <fcntl.h>
#include <termios.h>
#include <unistd.h>

#include "include/DevUart.h"

/// serial port operator class.
/// \author BevisWang
/// \param path
DevUart::DevUart() {}

DevUart::DevUart(const char *path) {
    DevUart::path = path;
}

speed_t DevUart::getBaudrate(int baudrate) {
    switch (baudrate) {
        case 0:
            return B0;
        case 50:
            return B50;
        case 75:
            return B75;
        case 110:
            return B110;
        case 134:
            return B134;
        case 150:
            return B150;
        case 200:
            return B200;
        case 300:
            return B300;
        case 600:
            return B600;
        case 1200:
            return B1200;
        case 1800:
            return B1800;
        case 2400:
            return B2400;
        case 4800:
            return B4800;
        case 9600:
            return B9600;
        case 19200:
            return B19200;
        case 38400:
            return B38400;
        case 57600:
            return B57600;
        case 115200:
            return B115200;
        case 230400:
            return B230400;
        case 460800:
            return B460800;
        case 500000:
            return B500000;
        case 576000:
            return B576000;
        case 921600:
            return B921600;
        case 1000000:
            return B1000000;
        case 1152000:
            return B1152000;
        case 1500000:
            return B1500000;
        case 2000000:
            return B2000000;
        case 2500000:
            return B2500000;
        case 3000000:
            return B3000000;
        case 3500000:
            return B3500000;
        case 4000000:
            return B4000000;
        default:
            return -1;
    }
}

int DevUart::setSpeed(int fd, int speed) {
    speed_t b_speed;
    struct termios cfg;
    b_speed = getBaudrate(speed);
    if (tcgetattr(fd, &cfg)) {
        LOGE("tcgetattr invocation method failed!");
        close(fd);
        return FALSE;
    }

    cfmakeraw(&cfg);
    cfsetispeed(&cfg, b_speed);
    cfsetospeed(&cfg, b_speed);

    if (tcsetattr(fd, TCSANOW, &cfg)) {
        LOGE("tcsetattr invocation method failed!");
        close(fd);
        return FALSE;
    }
    return TRUE;
}

int DevUart::setParity(int fd, int databits, int stopbits, int parity) {
    struct termios options;
    if (tcgetattr(fd, &options) != 0) {
        LOGE("The method tcgetattr exception!");
        return FALSE;
    }
    options.c_cflag &= ~CSIZE;
    switch (databits)                                           /* Set data bits */
    {
        case 7:
            options.c_cflag |= CS7;
            break;
        case 8:
            options.c_cflag |= CS8;
            break;
        default:
            LOGE("Unsupported data size!");
            return FALSE;
    }
    switch (parity) {
        case 'n':
        case 'N':
            options.c_cflag &= ~PARENB;                         /* Clear parity enable */
            options.c_iflag &= ~INPCK;                          /* Enable parity checking */
            break;
        case 'o':
        case 'O':
            options.c_cflag |= (PARODD | PARENB);               /* Set odd checking */
            options.c_iflag |= INPCK;                           /* Disnable parity checking */
            break;
        case 'e':
        case 'E':
            options.c_cflag |= PARENB;                          /* Enable parity */
            options.c_cflag &= ~PARODD;                         /* Transformation even checking */
            options.c_iflag |= INPCK;                           /* Disnable parity checking */
            break;
        case 'S':
        case 's':  /*as no parity*/
            options.c_cflag &= ~PARENB;
            options.c_cflag &= ~CSTOPB;
            break;
        default:
            LOGE("Unsupported parity!");
            return FALSE;
    }
    /* 设置停止位*/
    switch (stopbits) {
        case 1:
            options.c_cflag &= ~CSTOPB;
            break;
        case 2:
            options.c_cflag |= CSTOPB;
            break;
        default:
            LOGE("Unsupported stop bits!");
            return FALSE;
    }
    /* Set input parity option */
    if (parity != 'n')
        options.c_iflag |= INPCK;
    tcflush(fd, TCIFLUSH);
    options.c_cc[VTIME] = 150;                                  /* Set timeout to 15 seconds */
    options.c_cc[VMIN] = 0;                                     /* Update the options and do it NOW */
    if (tcsetattr(fd, TCSANOW, &options) != 0) {
        LOGE("The method tcsetattr exception!");
        return FALSE;
    }
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);         /* Input */
    options.c_oflag &= ~OPOST;                                  /* Output */
    return TRUE;
}

int DevUart::openUart(UartConfig config) {
    LOGD("Open device!");
    isClose = false;
    fd = open(path, O_RDWR);
    if (fd < 0) {
        LOGE("Error to read %s port file!", path);
        return FALSE;
    }
    if (!setSpeed(fd, config.baudrate)) {
        LOGE("Set Speed Error!");
        return FALSE;
    }
    if (!setParity(fd, config.databits, config.stopbits, config.parity)) {
        LOGE("Set Parity Error!");
        return FALSE;
    }
    LOGD("Open Success!");
    return TRUE;
}

int DevUart::readData(BYTE *data, int size) {
    int ret, retval;
    fd_set rfds;
    ret = 0;
    if (isClose) return 0;
    for (int i = 0; i < size; i++) {
        data[i] = static_cast<char>(0xFF);
    }
    FD_ZERO(&rfds);     // Clear device file desc.
    FD_SET(fd, &rfds);  // Reset device file desc.
    // TODO Async operation. Thread blocking.
    if (FD_ISSET(fd, &rfds)) {
        FD_ZERO(&rfds);
        FD_SET(fd, &rfds);
        retval = select(fd + 1, &rfds, NULL, NULL, NULL);
        if (retval == -1) {
            LOGE("Select error!");
        } else if (retval) {
            LOGD("This device has data!");
            ret = static_cast<int>(read(fd, data, static_cast<size_t>(size)));
        } else {
            LOGE("Select timeout!");
        }
    }
    if (isClose) close(fd);
    return ret;
}

int DevUart::writeData(BYTE *data, int len) {
    int result;
    result = static_cast<int>(write(fd, data, static_cast<size_t>(len)));
    LOGE("Write result:%d", result);
    return TRUE;
}

void DevUart::closeUart() {
    LOGD("Close device!");
    isClose = true;
}

创建本地方法接入头文件和源文件

文件创建位置同上,注意保证头文件和源文件名称一致,不然容易乱套。

UartLib.h

//
// Created by BevisWang on 2018/7/17.
//

#ifndef BLOGPROJECT_DEV_UART_H
#define BLOGPROJECT_DEV_UART_H

#include <jni.h>
#include "DevUart.h"

#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jint JNICALL Java_com_beviswang_blogproject_ndk_UART4Operator_openPort(
        JNIEnv *env, jobject thiz, jstring path, jint baudRate, jint dataBits, jint stopBits,
        jchar parity);

JNIEXPORT jbyteArray JNICALL
Java_com_beviswang_blogproject_ndk_UART4Operator_readPort(JNIEnv *env, jobject thiz, jint maxSize);

JNIEXPORT void JNICALL
Java_com_beviswang_blogproject_ndk_UART4Operator_writePort(JNIEnv *env, jobject thiz, jbyteArray data);

JNIEXPORT void JNICALL
Java_com_beviswang_blogproject_ndk_UART4Operator_closePort(JNIEnv *env, jobject thiz);

JNIEXPORT jint JNICALL Java_com_beviswang_blogproject_ndk_UART6Operator_openPort(
        JNIEnv *env, jobject thiz, jstring path, jint baudRate, jint dataBits, jint stopBits,
        jchar parity);

JNIEXPORT jbyteArray JNICALL
Java_com_beviswang_blogproject_ndk_UART6Operator_readPort(JNIEnv *env, jobject thiz, jint maxSize);

JNIEXPORT void JNICALL
Java_com_beviswang_blogproject_ndk_UART6Operator_writePort(JNIEnv *env, jobject thiz, jbyteArray data);

JNIEXPORT void JNICALL
Java_com_beviswang_blogproject_ndk_UART6Operator_closePort(JNIEnv *env, jobject thiz);
#ifdef __cplusplus
}
#endif

#endif //BLOGPROJECT_DEV_UART_H

UartLib.cpp

#include <jni.h>

#include "include/UartLib.h"

///
/// @author BevisWang
///
DevUart devUart4;
DevUart devUart6;

////////////////////////////////// UART4 ////////////////////////////////////////

jint Java_com_beviswang_blogproject_ndk_UART4Operator_openPort(
        JNIEnv *env, jobject thiz, jstring path, jint baudRate, jint dataBits, jint stopBits,
        jchar parity) {
    try {
        UartConfig config;
        config = UartConfig();
        config.baudrate = baudRate;
        config.databits = dataBits;
        config.stopbits = stopBits;
        config.parity = parity;
        devUart4 = DevUart(env->GetStringUTFChars(path, 0));
        devUart4.openUart(config);
    } catch (char *exception) {
        LOGE("Open device is error! Message:%s", exception);
        return FALSE;
    }
    return TRUE;
}

jbyteArray Java_com_beviswang_blogproject_ndk_UART4Operator_readPort(
        JNIEnv *env, jobject thiz, jint maxSize) {
    BYTE buf[maxSize];
    int len;
    len = devUart4.readData(buf, maxSize);
    if(len < 1) return NULL;
    jbyteArray byteArray;
    jbyte *bytes = reinterpret_cast<jbyte *>(buf);
    byteArray = env->NewByteArray(len);
    env->SetByteArrayRegion(byteArray, 0, len, bytes);
    return byteArray;
}

void Java_com_beviswang_blogproject_ndk_UART4Operator_writePort(
        JNIEnv *env, jobject thiz, jbyteArray data) {
    jbyte *array = env->GetByteArrayElements(data, 0);
    BYTE *bytes = reinterpret_cast<BYTE *>(array);
    devUart4.writeData(bytes, 0);
}

void Java_com_beviswang_blogproject_ndk_UART4Operator_closePort(
        JNIEnv *env, jobject thiz) {
    devUart4.closeUart();
    devUart4 = NULL;
}

////////////////////////////////// UART6 ////////////////////////////////////////

jint Java_com_beviswang_blogproject_ndk_UART6Operator_openPort(
        JNIEnv *env, jobject thiz, jstring path, jint baudRate, jint dataBits, jint stopBits,
        jchar parity) {
    try {
        UartConfig config;
        config = UartConfig();
        config.baudrate = baudRate;
        config.databits = dataBits;
        config.stopbits = stopBits;
        config.parity = parity;
        devUart6 = DevUart(env->GetStringUTFChars(path, 0));
        devUart6.openUart(config);
    } catch (char *exception) {
        LOGE("Open device is error! Message:%s", exception);
        return FALSE;
    }
    return TRUE;
}

jbyteArray Java_com_beviswang_blogproject_ndk_UART6Operator_readPort(
        JNIEnv *env, jobject thiz, jint maxSize) {
    BYTE buf[maxSize];
    int len;
    len = devUart6.readData(buf, maxSize);
    if(len < 1) return NULL;
    jbyteArray byteArray;
    jbyte *bytes = reinterpret_cast<jbyte *>(buf);
    byteArray = env->NewByteArray(len);
    env->SetByteArrayRegion(byteArray, 0, len, bytes);
    return byteArray;
}

void Java_com_beviswang_blogproject_ndk_UART6Operator_writePort(
        JNIEnv *env, jobject thiz, jbyteArray data) {
    jbyte *array = env->GetByteArrayElements(data, 0);
    BYTE *bytes = reinterpret_cast<BYTE *>(array);
    devUart6.writeData(bytes, 0);
}

void Java_com_beviswang_blogproject_ndk_UART6Operator_closePort(
        JNIEnv *env, jobject thiz) {
    devUart6.closeUart();
    devUart6 = NULL;
}

创建 Native Java 类

根据以上接入函数名 Java_com_beviswang_blogproject_ndk_UART6Operator 和
Java_com_beviswang_blogproject_ndk_UART4Operator 创建本地 Java 类,结构如下图:
这里写图片描述

UART4Operator 和 UART6Operator 完全一样,改下路径即可,故只贴其中一个类的代码(Kotlin?不习惯就直接用 Java 代替,只要衔接一下本地方法就可以了):

UARTOperator.kt

package com.beviswang.blogproject.ndk

import java.io.Closeable

/**
 * @author BevisWang
 */
interface IUARTOperator : Closeable {
    fun open(path: String, baudRate: Int = 9600, dataBits: Int = 8,
             stopBits: Int = 1, parity: Char = 'N')

    fun write(data: ByteArray)

    /**
     * I/O operation.
     * @param maxSize max array size
     */
    fun read(maxSize: Int = 256): ByteArray?
}

UART4Operator.kt

package com.beviswang.blogproject.ndk

import android.util.Log

/**
 * UART4 operator.
 * @author BevisWang
 */
class UART4Operator : IUARTOperator {

    override fun open(path: String, baudRate: Int, dataBits: Int, stopBits: Int, parity: Char) {
        val result = openPort(path, baudRate, dataBits, stopBits, parity)
        if (result != 1) {
            Log.e("UART4","Serial port open failed!")
        }
    }

    override fun write(data: ByteArray) {
        writePort(data)
    }

    override fun read(maxSize: Int): ByteArray? {
        return readPort(maxSize)
    }

    override fun close() {
        closePort()
    }

    /**
     * 打开并设置串口数据位,停止位和效验位
     * @param path     设备路径
     * @param baudRate 波特率
     * @param dataBits 数据位,取值为7或者8
     * @param stopBits 停止位,取值为1或者2
     * @param parity   效验类型 取值为N,E,O,S
     * @return is success
     */
    private external fun openPort(path: String = PORT_PATH, baudRate: Int = 9600,
                                  dataBits: Int = 8, stopBits: Int = 1, parity: Char = 'N'): Int

    /**
     * 开始读取串口数据
     * @param maxSize 最大读取空间
     * @return 读取到的串口数据
     */
    private external fun readPort(maxSize: Int): ByteArray?

    /**
     * The data to write to serial port device.
     * @param data write data
     */
    private external fun writePort(data: ByteArray)

    /** Close serial port device. */
    private external fun closePort()

    companion object {
        private const val PORT_PATH = "/dev/ttyS4"

        init {
            System.loadLibrary("uart_lib")
        }
    }
}

最后编辑一下脚本文件

CMake 脚本真的方便了许多,以下为脚本代码(知道大概的用法就好,自动创建的脚本文件有说明):

CMakeLists.txt

cmake_minimum_required(VERSION 3.4.1)

add_library(uart_lib SHARED src/main/cpp/UartLib.cpp)
add_library(dev_uart SHARED src/main/cpp/DevUart.cpp)

find_library(log-lib log )

target_link_libraries(dev_uart ${log-lib} )
target_link_libraries(uart_lib dev_uart ${log-lib} )

以上,实在不懂的请留言,再次声明,本文章只是为了提升读取速度。在 serialPort api 框架中,使用的是 java 方法读取文件,速度实在慢(普通的 Android 设备性能跟不上,这里指的并非手机和平板)。说下笔者的项目大概情况,笔者的项目是车载设备,需要接入 OBD ,但是 OBD 串口数据发送频率非常之快,并且在车上每个操作的同时,也会实时返回串口信息,每条数据几乎都是实时的。因此,serialPort api 框架无法满足项目要求,只好重新使用 c++ 读取。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值