当前位置: 代码迷 >> Android >> Android串口通信:串口读写范例
  详细解决方案

Android串口通信:串口读写范例

热度:57   发布时间:2016-04-28 02:07:33.0
Android串口通信:串口读写实例
在Android串口通信:基本知识梳理(http://gqdy365.iteye.com/admin/blogs/2188846)的基础上,我结合我项目中使用串口的实例,进行总结;

Android使用jni直接进行串口设备的读写网上已经有开源项目了,本文是基于网上的开源项目在实际项目中的使用做的调整和优化;
Google串口开源项目见:https://code.google.com/p/android-serialport-api/

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

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-3LOCAL_MODULE    := serial_portLOCAL_SRC_FILES := SerialPort.cppLOCAL_LDLIBS    := -lloginclude $(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得对应;
  相关解决方案