这里讲的是android通过JNI方式调用cpp编写的so库完成串口通信功能。

1:java层直接封装几个native方法供调用即可,比如打开串口,设置串口属性,发送内容,接收内容,关闭串口等。这样就可以通过串口进行基本的通信了。

类似下面这样:

public native long openPort(String name, long lBaudRate);

public native boolean setParams(long fd, int baudRate, int byteSize, int stopBits, int parity, boolean setRTS, boolean setDTR, int flags);

public native byte[] readBytes(long fd, int bytecount);

public native boolean writeBytes(long fd, byte[] bytes);

public native int[] getBuffersBytesCount(long fd);

public native boolean closePort(long id);

这就是一个基本串口的调用。接下来需要编写cpp代码,可以创建.h 和 .cpp文件。h文件声明方法和变量。cpp文件进行实现相关内容。

这里不详细讲解如何实现,主要说明大概思路:

打开串口通过以下实现:

jlong hComm = open(port, O_RDWR | O_NOCTTY | O_NDELAY);

设置波特率通过以下实现:

cfmakeraw(settings);
cfsetspeed(settings, baudRateValue);

另外还需要设置串口的其他属性:

termios *settings = new termios();
tcgetattr(hComm, settings);
settings->c_cflag = CS8 | CLOCAL | CREAD;
settings->c_iflag = 0;
settings->c_oflag = 0;
settings->c_lflag = 0;
settings->c_cc[VMIN] = 1;
settings->c_cc[VTIME] = 0;
tcsetattr(hComm, TCSANOW, settings);

具体属性的解释,如cflag为控制流属性,iflag为输入流属性,oflag为输出流属性等等,可以查找termios相关解释。

对于termios的详细解释也可以参考下面这篇文章介绍:

termios 详解_mr_raptor的博客-CSDN博客

关闭串口通过以下实现:

close(portHandle)

读取数据则通过以下实现:

int result = read(portHandle, lpBuffer + byteCount, byteCount);

其中lpBuffer是要读取内容的指针,准备存放读取的内容:

jbyte *lpBuffer = new jbyte[byteCount];

注意这里在读取前最好读取下输入缓存区内容大小,不然直接读取可能会卡住如果你不知道byteCount是多少的话。

读取输入缓冲区大小为:

jint ibuf = -1;
ioctl(mPortHandle, FIONREAD, &ibuf);

同样,读取输出去缓冲区大小为:

jint obuf = -1;

ioctl(portHandle, TIOCOUTQ, &obuf);

写入数据则通过以下实现:

write(portHandle, buffer, bufferSize)

这里贴一下读写的调用代码,文件名是unistd.h,路径在sysroot\usr\include\bits\fortify 目录下,

__BIONIC_FORTIFY_INLINE
ssize_t read(int fd, void* const __pass_object_size0 buf, size_t count)
        __overloadable
        __error_if_overflows_ssizet(count, read)
        __error_if_overflows_objectsize(count, __bos0(buf), read) {
#if __ANDROID_API__ >= __ANDROID_API_L__
    size_t bos = __bos0(buf);

    if (!__bos_trivially_ge_no_overflow(bos, count)) {
        return __read_chk(fd, buf, count, bos);
    }
#endif /* __ANDROID_API__ >= __ANDROID_API_L__ */
    return __call_bypassing_fortify(read)(fd, buf, count);
}

__BIONIC_FORTIFY_INLINE
ssize_t write(int fd, const void* const __pass_object_size0 buf, size_t count)
        __overloadable
        __error_if_overflows_ssizet(count, write)
        __error_if_overflows_objectsize(count, __bos0(buf), write) {
#if __ANDROID_API__ >= __ANDROID_API_N__
    size_t bos = __bos0(buf);

    if (!__bos_trivially_ge_no_overflow(bos, count)) {
        return __write_chk(fd, buf, count, bos);
    }
#endif /* __ANDROID_API__ >= __ANDROID_API_N__ */
    return __call_bypassing_fortify(write)(fd, buf, count);
}

这些相关的函数所在文件都在:

androidstudio\sdk\ndk\21.1.6352462\toolchains\llvm\prebuilt\windows-x86_64\sysroot\usr\include\bits\fortify 目录下,可以参考。

那么基本的调用次序就是,打开串口,设置串口参数,然后往串口中写入数据,然后读取输入缓冲区大小,得到大小后,进行数据的读取,完成串口操作后,关闭串口。

2:这里贴一段其他链接的串口操作样例代码供参考,链接为:C ioctl(fd, TIOCEXCL);

这个链接下有很多类似的代码可以参考,可以顺便浏览下该链接下其他示例。在这里关于串口的操作实现是类似的:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <time.h>
#include <sys/ioctl.h>

#define BUFSIZE (65536+100)
unsigned char readbuf[BUFSIZE];

static struct termios term;
static struct termios gOriginalTTYAttrs;

void sendCmd(int fd, void *buf, size_t size);
void sendStrCmd(int fd, char *buf);
int readResp(int fd);
int initConn(int speed);
void closeConn(int fd);
void sendAt(int fd);
void at(int fd);

void sendCmd(int fd, void *buf, size_t size) {
    if(write(fd, buf, size) == -1) {
        fprintf(stderr, "sendCmd error. %s\n", strerror(errno));
        exit(1);
    }
}

void sendStrCmd(int fd, char *buf) {
    sendCmd(fd, buf, strlen(buf));
}

int readResp(int fd) {
    int len = 0;
    struct timeval timeout;
    int nfds = fd + 1;
    fd_set readfds;
    int select_ret;
    
    FD_ZERO(&readfds);
    FD_SET(fd, &readfds);
    
    // Wait a second
    timeout.tv_sec = 1;
    timeout.tv_usec = 500000;
    
    while ((select_ret = select(nfds, &readfds, NULL, NULL, &timeout)) > 0) {
        len += read(fd, readbuf + len, BUFSIZE - len);
        FD_ZERO(&readfds);
        FD_SET(fd, &readfds);
        timeout.tv_sec = 0;
        timeout.tv_usec = 500000;
    }
    if (len > 0) {
    }
    readbuf[len] = 0;
    fprintf(stderr,"%s",readbuf);
    return len;
}

int initConn(int speed) {
    int fd = open("/dev/dlci.spi-baseband.extra_0", O_RDWR | O_NOCTTY);
    
    if(fd == -1) {
        fprintf(stderr, "%i(%s)\n", errno, strerror(errno));
        exit(1);
    }
    
    ioctl(fd, TIOCEXCL);
    fcntl(fd, F_SETFL, 0);
    
    tcgetattr(fd, &term);
    gOriginalTTYAttrs = term;
    
    cfmakeraw(&term);
    cfsetspeed(&term, speed);
    term.c_cflag = CS8 | CLOCAL | CREAD;
    term.c_iflag = 0;
    term.c_oflag = 0;
    term.c_lflag = 0;
    term.c_cc[VMIN] = 0;
    term.c_cc[VTIME] = 0;
    tcsetattr(fd, TCSANOW, &term);
    
    return fd;
}

void closeConn(int fd) {
    tcdrain(fd);
    tcsetattr(fd, TCSANOW, &gOriginalTTYAttrs);
    close(fd);
}

void sendAt(int fd) {
    char cmd[5];
    sprintf(cmd,"AT\r");
    sendCmd(fd, cmd, strlen(cmd));
}

void at(int fd) {
    sendAt(fd);
    for (;;) {
        if(readResp(fd) != 0) {
            if(strstr((const char *)readbuf,"OK") != NULL) {
                break;
            }
        }
        sendAt(fd);
    }
}

int main(int argc, char **argv) {
    int fd;
    char cmd[1024];
    if(argc < 2) {
        fprintf(stderr,"usage: %s <pdu data>\n",argv[0]);
        exit(1);
    }
    fd = initConn(115200);
    at(fd);
    sendStrCmd(fd, "AT+CMGF=0\r");
    readResp(fd);
    sprintf(cmd, "AT+CMGS=%ld\r", strlen(argv[1])/2 - 1);
    sendStrCmd(fd, cmd);
    readResp(fd);
    sprintf(cmd,"%s\032",argv[1]);
    sendStrCmd(fd, cmd);
    readResp(fd);
    closeConn(fd);
    return 0;
}

通过上述的描述,就可以实现串口的cpp端通信,可以封装so供java层调用。

QQ:(504490738)

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐