Android串口通信之串口读写实例

Android串口通信:基本知识梳理的基础上,我结合我项目中使用串口的实例,进行总结;

Android使用jni直接进行串口设备的读写网上已经有开源项目了,本文是基于网上的开源项目在实际项目中的使用做的调整和优化;

Google串口开源项目

下面是我项目中的相关代码及介绍:

1、SerialPort.cpp

/*

* Copyright 2009 Cedric Priscal

*

* Licensed under the Apache License, Version 2.0 (the "License");

* you may not use this file except in compliance with the License.

* You may obtain a copy of the License at

*

* http://www.apache.org/licenses/LICENSE-2.0

*

* Unless required by applicable law or agreed to in writing, software

* distributed under the License is distributed on an "AS IS" BASIS,

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

* See the License for the specific language governing permissions and

* limitations under the License.

*/

#include <stdlib.h>

#include <stdio.h>

#include <jni.h>

#include <assert.h>

#include <termios.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <string.h>

#include <jni.h>

#include "android/log.h"

static const char *TAG = "serial_port";

#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: cedric_serial_SerialPort

* Method: open

* Signature: (Ljava/lang/String;)V

*/

JNIEXPORT jobject JNICALL native_open(JNIEnv *env, jobject thiz, jstring path,jint baudrate) {

int fd;

speed_t speed;

jobject mFileDescriptor;

LOGD("init native Check arguments");

/* Check arguments */

{

speed = getBaudrate(baudrate);

if (speed == -1) {

/* TODO: throw an exception */

LOGE("Invalid baudrate");

return NULL;

}

}

LOGD("init native Opening device!");

/* Opening device */

{

jboolean iscopy;

const char *path_utf = env->GetStringUTFChars(path, &iscopy);

LOGD("Opening serial port %s", path_utf);

// fd = open(path_utf, O_RDWR | O_DIRECT | O_SYNC);

fd = open(path_utf, O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY);

LOGD("open() fd = %d", fd);

env->ReleaseStringUTFChars(path, path_utf);

if (fd == -1) {

/* Throw an exception */

LOGE("Cannot open port %d",baudrate);

/* TODO: throw an exception */

return NULL;

}

}

LOGD("init native Configure device!");

/* Configure device */

{

struct termios cfg;

if (tcgetattr(fd, &cfg)) {

LOGE("Configure device tcgetattr() failed 1");

close(fd);

return NULL;

}

cfmakeraw(&cfg);

cfsetispeed(&cfg, speed);

cfsetospeed(&cfg, speed);

if (tcsetattr(fd, TCSANOW, &cfg)) {

LOGE("Configure device tcsetattr() failed 2");

close(fd);

/* TODO: throw an exception */

return NULL;

}

}

/* Create a corresponding file descriptor */

{

jclass cFileDescriptor = env->FindClass("java/io/FileDescriptor");

jmethodID iFileDescriptor = env->GetMethodID(cFileDescriptor,"<init>", "()V");

jfieldID descriptorID = env->GetFieldID(cFileDescriptor,"descriptor", "I");

mFileDescriptor = env->NewObject(cFileDescriptor,iFileDescriptor);

env->SetIntField(mFileDescriptor, descriptorID, (jint) fd);

}

return mFileDescriptor;

}

/*

* Class: cedric_serial_SerialPort

* Method: close

* Signature: ()V

*/

JNIEXPORT jint JNICALL native_close(JNIEnv * env, jobject thiz)

{

jclass SerialPortClass = env->GetObjectClass(thiz);

jclass FileDescriptorClass = env->FindClass("java/io/FileDescriptor");

jfieldID mFdID = env->GetFieldID(SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");

jfieldID descriptorID = env->GetFieldID(FileDescriptorClass, "descriptor", "I");

jobject mFd = env->GetObjectField(thiz, mFdID);

jint descriptor = env->GetIntField(mFd, descriptorID);

LOGD("close(fd = %d)", descriptor);

close(descriptor);

return 1;

}

static JNINativeMethod gMethods[] = {

{ "open", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;",(void*) native_open },

{ "close", "()I",(void*) native_close },

};

/*

* 为某一个类注册本地方法

*/

static int registerNativeMethods(JNIEnv* env, const char* className,

JNINativeMethod* gMethods, int numMethods) {

jclass clazz;

clazz = env->FindClass(className);

if (clazz == NULL) {

return JNI_FALSE;

}

if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {

return JNI_FALSE;

}

return JNI_TRUE;

}

/*

* 为所有类注册本地方法

*/

static int registerNatives(JNIEnv* env) {

const char* kClassName = "com/jerome/serialport/SerialPort"; //指定要注册的类

return registerNativeMethods(env, kClassName, gMethods,

sizeof(gMethods) / sizeof(gMethods[0]));

}

/*

* System.loadLibrary("lib")时调用

* 如果成功返回JNI版本, 失败返回-1

*/

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {

JNIEnv* env = NULL;

jint result = -1;

if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {

return -1;

}

assert(env != NULL);

if (!registerNatives(env)) { //注册

return -1;

}

//成功

result = JNI_VERSION_1_4;

return result;

}

在编译时注意修改const char* kClassName = "com/jerome/serialport/SerialPort";为你Java层与jni对应得包名;

2、Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

TARGET_PLATFORM := android-3

LOCAL_MODULE := serial_port

LOCAL_SRC_FILES := SerialPort.cpp

LOCAL_LDLIBS := -llog

include $(BUILD_SHARED_LIBRARY)

如果要修改生成so文件的名称,请修改LOCAL_MODULE    := serial_port

3、SerialPort.java

package com.jerome.serialport;

import java.io.File;

import java.io.FileDescriptor;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

public class SerialPort {

private static final String TAG = "SerialPort";

/*

* Do not remove or rename the field mFd: it is used by native method close();

*/

private FileDescriptor mFd;

private FileInputStream mFileInputStream;

private FileOutputStream mFileOutputStream;

public SerialPort(File device, int baudrate) throws SecurityException, IOException {

mFd = open(device.getAbsolutePath(), baudrate);

if (mFd == null) {

throw new IOException();

}

mFileInputStream = new FileInputStream(mFd);

mFileOutputStream = new FileOutputStream(mFd);

}

public InputStream getInputStream() {

return mFileInputStream;

}

public OutputStream getOutputStream() {

return mFileOutputStream;

}

private native FileDescriptor open(String path, int baudrate);

public native int close();

static {

System.loadLibrary("serial_port");

}

}

4、SerialPortUtil.java

package com.jerome.serialport;

import java.io.BufferedWriter;

import java.io.File;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.io.OutputStreamWriter;

import java.io.PrintWriter;

/**

* 串口操作类

*

* @author Jerome

*

*/

public class SerialPortUtil {

private String TAG = SerialPortUtil.class.getSimpleName();

private SerialPort mSerialPort;

private OutputStream mOutputStream;

private InputStream mInputStream;

private ReadThread mReadThread;

private String path = "/dev/ttyMT1";

private int baudrate = 115200;

private static SerialPortUtil portUtil;

private OnDataReceiveListener onDataReceiveListener = null;

private boolean isStop = false;

public interface OnDataReceiveListener {

public void onDataReceive(byte[] buffer, int size);

}

public void setOnDataReceiveListener(

OnDataReceiveListener dataReceiveListener) {

onDataReceiveListener = dataReceiveListener;

}

public static SerialPortUtil getInstance() {

if (null == portUtil) {

portUtil = new SerialPortUtil();

portUtil.onCreate();

}

return portUtil;

}

/**

* 初始化串口信息

*/

public void onCreate() {

try {

mSerialPort = new SerialPort(new File(path), baudrate);

mOutputStream = mSerialPort.getOutputStream();

mInputStream = mSerialPort.getInputStream();

mReadThread = new ReadThread();

isStop = false;

mReadThread.start();

} catch (Exception e) {

e.printStackTrace();

}

initBle();

}

/**

* 发送指令到串口

*

* @param cmd

* @return

*/

public boolean sendCmds(String cmd) {

boolean result = true;

byte[] mBuffer = (cmd+"\r\n").getBytes();

//注意:我得项目中需要在每次发送后面加\r\n,大家根据项目项目做修改,也可以去掉,直接发送mBuffer

try {

if (mOutputStream != null) {

mOutputStream.write(mBuffer);

} else {

result = false;

}

} catch (IOException e) {

e.printStackTrace();

result = false;

}

return result;

}

public boolean sendBuffer(byte[] mBuffer) {

boolean result = true;

String tail = "\r\n";

byte[] tailBuffer = tail.getBytes();

byte[] mBufferTemp = new byte[mBuffer.length+tailBuffer.length];

System.arraycopy(mBuffer, 0, mBufferTemp, 0, mBuffer.length);

System.arraycopy(tailBuffer, 0, mBufferTemp, mBuffer.length, tailBuffer.length);

//注意:我得项目中需要在每次发送后面加\r\n,大家根据项目项目做修改,也可以去掉,直接发送mBuffer

try {

if (mOutputStream != null) {

mOutputStream.write(mBufferTemp);

} else {

result = false;

}

} catch (IOException e) {

e.printStackTrace();

result = false;

}

return result;

}

private class ReadThread extends Thread {

@Override

public void run() {

super.run();

while (!isStop && !isInterrupted()) {

int size;

try {

if (mInputStream == null)

return;

byte[] buffer = new byte[512];

size = mInputStream.read(buffer);

if (size > 0) {

if(MyLog.isDyeLevel()){

MyLog.log(TAG, MyLog.DYE_LOG_LEVEL, "length is:"+size+",data is:"+new String(buffer, 0, size));

}

if (null != onDataReceiveListener) {

onDataReceiveListener.onDataReceive(buffer, size);

}

}

Thread.sleep(10);

} catch (Exception e) {

e.printStackTrace();

return;

}

}

}

}

/**

* 关闭串口

*/

public void closeSerialPort() {

sendShellCommond1();

isStop = true;

if (mReadThread != null) {

mReadThread.interrupt();

}

if (mSerialPort != null) {

mSerialPort.close();

}

}

}

5、使用方法:

a、配置ndk开发环境,具体百度一下;

b、工程根目录下新建jni文件夹,将Android.mk和SerialPort.cpp放进去;

c、ndk中进入jni目录,编译生成so文件,默认so生成在libs/armeabi下;

d、新建com.jerom.serialport目录,将SerialPort和SerialPortUtil放进去;

 f、在你要使用的地方初始化SerialPortUtil,实现回调接口OnDataReceiveListener即可接受数据;

总结:

1、串口发送实质就是向串口设备(类似于文件操作)写入字节流,串口读取也是一样;

2、主要jni与Java native得对应;

以上是 Android串口通信之串口读写实例 的全部内容, 来源链接: utcz.com/z/324385.html

回到顶部