这里讲的是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
#include
#include
#include
#include
#include
#include
#include
#include
#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 \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)