首页 > 系统 > Android > 正文

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

2019-10-22 18:12:49
字体:
来源:转载
供稿:网友

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得对应;

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持VEVB武林网。


注:相关教程知识阅读请移步到Android开发频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表