美文网首页
Android串口通信:串口读写

Android串口通信:串口读写

作者: Orz013 | 来源:发表于2017-06-29 11:45 被阅读0次

在不久前,接触到了物联网的开发,当时一脸懵逼了,后来问了度娘和有幸遇到大神的指导,最终实现了功能。

首先先弄懂跟硬件交互时的一些协议(这协议都是硬件公司提供的),就在jni直接进行串口设备的读写,而且Android使用jni直接进行串口设备的读写网上已经有开源项目了,本文是基于网上的开源项目在实际项目中的使用做的调整和优化;

Google串口开源项目见:https://code.google.com/p/android-serialport-api/

下面是我项目中的相关代码及介绍:(因GitHub账号最近有些问题,只能直接把代码贴上来)

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

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#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)

staticspeed_t getBaudrate(jint baudrate) {

switch(baudrate) {

case0:

returnB0;

case50:

returnB50;

case75:

returnB75;

case110:

returnB110;

case134:

returnB134;

case150:

returnB150;

case200:

returnB200;

case300:

returnB300;

case600:

returnB600;

case1200:

returnB1200;

case1800:

returnB1800;

case2400:

returnB2400;

case4800:

returnB4800;

case9600:

returnB9600;

case19200:

returnB19200;

case38400:

returnB38400;

case57600:

returnB57600;

case115200:

returnB115200;

case230400:

returnB230400;

case460800:

returnB460800;

case500000:

returnB500000;

case576000:

returnB576000;

case921600:

returnB921600;

case1000000:

returnB1000000;

case1152000:

returnB1152000;

case1500000:

returnB1500000;

case2000000:

returnB2000000;

case2500000:

returnB2500000;

case3000000:

returnB3000000;

case3500000:

returnB3500000;

case4000000:

returnB4000000;

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) {

intfd;

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");

returnNULL;

}

}

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 */

returnNULL;

}

}

LOGD("init native Configure device!");

/* Configure device */

{

struct termios cfg;

if(tcgetattr(fd,&cfg)) {

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

close(fd);

returnNULL;

}

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 */

returnNULL;

}

}

/* Create a corresponding file descriptor */

{

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

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

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

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

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

}

returnmFileDescriptor;

}

/*

* 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);

return1;

}

staticJNINativeMethod gMethods[] = {

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

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

};

/*

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

*/

static intregisterNativeMethods(JNIEnv* env, const char* className,

JNINativeMethod* gMethods, intnumMethods) {

jclass clazz;

clazz = env->FindClass(className);

if(clazz == NULL) {

returnJNI_FALSE;

}

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

returnJNI_FALSE;

}

returnJNI_TRUE;

}

/*

*为所有类注册本地方法

*/

static intregisterNatives(JNIEnv* env) {

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

returnregisterNativeMethods(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;

returnresult;

}

SerialPort.cpp部分代码片

在编译时注意修改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)

Android.mk

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

3、SerialPort.java

importjava.io.File;

importjava.io.FileDescriptor;

importjava.io.FileInputStream;

importjava.io.FileOutputStream;

importjava.io.IOException;

importjava.io.InputStream;

importjava.io.OutputStream;

public classSerialPort {

private static finalStringTAG="SerialPort";

/*

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

*/

privateFileDescriptormFd;

privateFileInputStreammFileInputStream;

privateFileOutputStreammFileOutputStream;

publicSerialPort(File device, intbaudrate)throwsSecurityException,IOException {

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

if(mFd==null) {

throw newIOException();

}

mFileInputStream=newFileInputStream(mFd);

mFileOutputStream=newFileOutputStream(mFd);

}

publicInputStreamgetInputStream() {

returnmFileInputStream;

}

publicOutputStreamgetOutputStream() {

returnmFileOutputStream;

}

private nativeFileDescriptoropen(String path, intbaudrate);

public native intclose();

static{

System.loadLibrary("serial_port");

}

}

4、SerialPortUtil.java

importjava.io.BufferedWriter;

importjava.io.File;

importjava.io.IOException;

importjava.io.InputStream;

importjava.io.OutputStream;

importjava.io.OutputStreamWriter;

importjava.io.PrintWriter;

/**

*串口操作类

*

*@authorJerome

*

*/

public classSerialPortUtil {

privateStringTAG= SerialPortUtil.class.getSimpleName();

privateSerialPortmSerialPort;

privateOutputStreammOutputStream;

privateInputStreammInputStream;

privateReadThreadmReadThread;

privateStringpath="/dev/ttyMT1";

private intbaudrate=115200;

private staticSerialPortUtilportUtil;

privateOnDataReceiveListeneronDataReceiveListener=null;

private booleanisStop=false;

public interfaceOnDataReceiveListener {

public voidonDataReceive(byte[] buffer, intsize);

}

public voidsetOnDataReceiveListener(

OnDataReceiveListener dataReceiveListener) {

onDataReceiveListener= dataReceiveListener;

}

public staticSerialPortUtilgetInstance() {

if(null==portUtil) {

portUtil=newSerialPortUtil();

portUtil.onCreate();

}

returnportUtil;

}

/**

*初始化串口信息

*/

public voidonCreate() {

try{

mSerialPort=newSerialPort(newFile(path),baudrate);

mOutputStream=mSerialPort.getOutputStream();

mInputStream=mSerialPort.getInputStream();

mReadThread=newReadThread();

isStop=false;

mReadThread.start();

}catch(Exception e) {

e.printStackTrace();

}

initBle();

}

/**

*发送指令到串口

*

*@paramcmd

*@return

*/

public booleansendCmds(String cmd) {

booleanresult =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;

}

returnresult;

}

public booleansendBuffer(byte[] mBuffer) {

booleanresult =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;

}

returnresult;

}

private classReadThreadextendsThread {

@Override

public voidrun() {

super.run();

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

intsize;

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:"+newString(buffer,0,size));

}

if(null!=onDataReceiveListener) {

onDataReceiveListener.onDataReceive(buffer,size);

}

}

Thread.sleep(10);

}catch(Exception e) {

e.printStackTrace();

return;

}

}

}

}

/**

*关闭串口

*/

public voidcloseSerialPort() {

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即可接受数据;

6、温馨提醒:在使用流的时候记得要及时关闭流,没有关闭的话因为流资源就会被他一直占用,这样别人想用就没有办法用了,所以这回造成资源浪费,而且要关闭资源,最好写在finally中,如果这个东西出现一个异常,你就关不掉了。

相关文章

网友评论

      本文标题:Android串口通信:串口读写

      本文链接:https://www.haomeiwen.com/subject/quwucxtx.html