Android讀寫串口的時候需要jni調用c代碼來實現,在開始之前需要下載ndk並配置好環境變量,也就是path裏面加上ndk的bin路徑。下面是具體的步驟
1、建一個包含jni方法的串口操作類:
package com.luoye.frigo.device;
public class SerialPortReaderWriter {
protected native int open(String path, int baudrate, int parity,int stopBites,int dataBites);
protected native void close(int fd);
protected native int read(int fd,byte[] bytes,int len);
protected native int write(int fd,byte[] bytes,int len);
}
2、在java目錄下編譯SerialPortReaderWriter這個類:
D:\myprojects\android\LibTestApp\device\src\main\java>javac -d ../jni com/luoye/frigo/device/SerialPortReaderWriter.java
3、在jni目錄下生成c頭文件SerialPortReaderWriter.h:
D:\myprojects\android\LibTestApp\device\src\main\jni>javah -o SerialPortReaderWriter.h -jni com.luoye.frigo.device.SerialPortReaderWriter
4、在jni目錄下添加c文件SerialPortReaderWriter.c,在這個文件中實現串口操作的邏輯:
//
// Created by dave on 2020/4/28.
//
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <jni.h>
#include "SerialPortReaderWriter.h"
#include "android/log.h"
static const char *TAG="serial_port_reader_writer";
#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args)
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)
static speed_t getBaudrate(jint 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;
}
}
/*
* Class: com_tricent_frigo_device_SerialPortReaderWriter
* Method: open
* Signature: (Ljava/lang/String;IIII)I
*/
JNIEXPORT jint JNICALL Java_com_luoye_frigo_device_SerialPortReaderWriter_open
(JNIEnv *env, jobject thiz, jstring path, jint baudrate, jint parity, jint stop_bites, jint data_bites){
int fd=0;
speed_t speed;
jobject mFileDescriptor;
/* Check arguments */
{
speed = getBaudrate(baudrate);
if (speed == -1) {
/* TODO: throw an exception */
LOGE("Invalid baudrate");
return -1;
}
if(parity>5||parity<1){
LOGE("Invalid parity");
return -2;
}
if(stop_bites>3||stop_bites<1){
LOGE("Invalid stop_bites");
return -3;
}
if(data_bites>8||data_bites<5){
LOGE("Invalid data_bites");
return -4;
}
}
/* Opening device */
{
jboolean iscopy;
const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy);
LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | O_NOCTTY | O_NDELAY);
fd = open(path_utf, O_RDWR | O_NOCTTY | O_NDELAY);
LOGD("open() fd = %d", fd);
(*env)->ReleaseStringUTFChars(env, path, path_utf);
if (fd == -1)
{
/* Throw an exception */
LOGE("Cannot open port");
/* TODO: throw an exception */
return -5;
}
}
/* Configure device */
{
struct termios cfg;
LOGD("Configuring serial port");
if (tcgetattr(fd, &cfg))
{
LOGE("tcgetattr() failed");
close(fd);
/* TODO: throw an exception */
return -6;
}
//cfmakeraw(&cfg);
/*設置波特率*/
//tcflush(fd,TCIOFLUSH);
cfsetispeed(&cfg,speed);
/*設置數據位*/
cfg.c_cflag &= ~CSIZE;
switch (data_bites) /*設置數據位數*/
{
case 5:
cfg.c_cflag |= CS5;
break;
case 6:
cfg.c_cflag |= CS6;
break;
case 7:
cfg.c_cflag |= CS7;
break;
case 8:
cfg.c_cflag |= CS8;
break;
}
/*設置奇偶校驗率*/
switch (parity)
{
case 1:
cfg.c_cflag &= ~PARENB; /* Clear parity enable */
cfg.c_iflag &= ~INPCK; /* Enable parity checking */
break;
case 2:
cfg.c_cflag |= (PARODD | PARENB); /* 設置爲奇效驗*/
cfg.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 3:
cfg.c_cflag |= PARENB; /* Enable parity */
cfg.c_cflag &= ~PARODD; /* 轉換爲偶效驗*/
cfg.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 4: /*as no parity*/
cfg.c_cflag &= ~PARENB;
cfg.c_cflag &= ~CSTOPB;
break;
case 5:
cfg.c_cflag &= ~PARENB;
cfg.c_cflag &= CSTOPB;
break;
}
/* 設置停止位*/
switch (stop_bites)
{
case 1:
cfg.c_cflag &= ~CSTOPB;
break;
case 2:
cfg.c_cflag |= CSTOPB;
break;
}
cfg.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
//屏蔽軟件流控,讓X-ON X-OFF可以發送出去
cfg.c_iflag &= ~ (IXON | IXOFF | IXANY | ICRNL);
cfg.c_oflag &= ~OPOST;
cfg.c_cc[VTIME] = 150; /* 設置超時15 seconds*/
cfg.c_cc[VMIN] = 0;
tcflush(fd,TCIFLUSH);/* Update the cfg and do it NOW */
//立即將新屬性賦予串口
if (tcsetattr(fd, TCSANOW, &cfg))
{
LOGE("tcsetattr() failed");
close(fd);
/* TODO: throw an exception */
return -7;
}
}
return fd;
}
/*
* Class: com_tricent_frigo_device_SerialPortReaderWriter
* Method: close
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_com_luoye_frigo_device_SerialPortReaderWriter_close
(JNIEnv *env, jobject thiz, jint fd){
LOGD("close(fd = %d)", fd);
close(fd);
}
/*
* Class: com_tricent_frigo_device_SerialPortReaderWriter
* Method: read
* Signature: (I[BI)I
*/
JNIEXPORT jint JNICALL Java_com_luoye_frigo_device_SerialPortReaderWriter_read
(JNIEnv *env, jobject thiz, jint fd, jbyteArray byteArray, jint len){
//方式1
jbyte* byte = (*env)->GetByteArrayElements(env,byteArray, 0);
int nRet = read(fd,byte,len);
LOGD("read(nRet = %d)", nRet);
//方式2
//char chTmp[len];
//memset(chTmp, 0, len);
//int nRet = read(fd,chTmp,len);
//LOGD("read(nRet = %d)", nRet);
//(*env)->SetByteArrayRegion(env,byteArray, 0, len, chTmp);
return nRet;
}
/*
* Class: com_tricent_frigo_device_SerialPortReaderWriter
* Method: write
* Signature: (I[BI)I
*/
JNIEXPORT jint JNICALL Java_com_luoye_frigo_device_SerialPortReaderWriter_write
(JNIEnv *env, jobject thiz, jint fd, jbyteArray byteArray, jint len){
jbyte* byte = (*env)->GetByteArrayElements(env,byteArray, 0);
int nRet = write(fd,byte,len);
LOGD("write(nRet = %d)", nRet);
(*env)->ReleaseByteArrayElements(env, byteArray, byte, 0);
return nRet;
}
5、在jni目錄下添加makefile文件Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := dev-jni
LOCAL_SRC_FILES := SerialPortReaderWriter.c
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
6、在jni目錄下執行ndk-build命令:
D:\myprojects\android\LibTestApp\device\src\main\jni>ndk-build
7、將生成的libs目錄拷貝到項目根目錄
8、在Java類SerialPortReaderWriter中添加:
static {
System.loadLibrary("dev-jni");
}
完善後的完整代碼:
package com.luoye.frigo.device;
public class SerialPortReaderWriter {
static {
System.loadLibrary("dev-jni");
}
protected native int open(String path, int baudrate, int parity,int stopBites,int dataBites);
protected native void close(int fd);
protected native int read(int fd,byte[] bytes,int len);
protected native int write(int fd,byte[] bytes,int len);
private int fd=-1;
public void openSerialPort(String path, int baudrate, int parity,int stopBites,int dataBites){
fd=open(path,baudrate,parity,stopBites,dataBites);
}
public void openSerialPort(String path, int baudrate){
openSerialPort(path,baudrate,1,1,8);
}
public void closeSerialPort(){
close(fd);
fd=-1;
}
public int read(byte[] bytes){
if(bytes==null||fd==-1){
return 0;
}
int len=bytes.length;
return read(fd,bytes,len);
}
public int write(byte[] bytes){
if(bytes==null||fd==-1){
return 0;
}
int len=bytes.length;
return write(fd,bytes,len);
}
}
9、使用示例:
SerialPortReaderWriter serialPortReaderWriter=new SerialPortReaderWriter();
serialPortReaderWriter.openSerialPort("/dev/ttymxc3",9600);
serialPortReaderWriter.write("hello world".getBytes());
byte bs[]=new byte[32];
int r=serialPortReaderWriter.read(bs);
etr.setText(r+":"+new String(bs));
serialPortReaderWriter.closeSerialPort();