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

作者:Chen_xiaobao 时间:2023-11-04 00:11:03 

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

来源:https://blog.csdn.net/Chen_xiaobao/article/details/76153900

标签:Android,串口,通信
0
投稿

猜你喜欢

  • Android平台基于Pull方式对XML文件解析与写入方法详解

    2021-07-21 01:52:52
  • Java多线程的用法详解

    2021-10-29 19:20:59
  • Java受检异常的一些思考

    2021-06-08 08:22:16
  • C语言中数据是如何存储在内存中的

    2021-07-03 02:13:34
  • Java中实现简单的Excel导出

    2021-10-21 07:23:25
  • Springboot启动执行特定代码的方式汇总

    2023-08-06 04:42:24
  • AndroidStudio报错Emulator:PANIC:Cannot find AVD system path. Please define ANDROID_SDK_ROOT(解决方案)

    2023-11-27 01:33:28
  • Spring Boot启动过程完全解析(二)

    2022-06-17 14:40:59
  • java实现http的Post、Get、代理访问请求

    2021-10-30 08:13:47
  • Android中的HOOK技术是什么

    2022-06-19 18:18:24
  • Java对字符串进行加密解密

    2022-10-09 05:39:48
  • Android Gradle依赖管理、去除重复依赖、忽略的方式

    2023-06-08 16:07:12
  • 从源码编译Android系统的Java类库和JNI动态库的方法

    2022-05-01 17:55:44
  • c#实现winform屏幕截图并保存的示例

    2022-08-09 09:06:33
  • springboot手写一个自己的starter源码

    2021-07-31 10:18:14
  • Java案例使用比较排序器comparator实现成绩排序

    2023-10-16 01:37:24
  • Java发送http请求的示例(get与post方法请求)

    2021-09-29 23:21:40
  • Android中使用开源框架eventbus3.0实现fragment之间的通信交互

    2021-06-30 09:23:07
  • Spring超详细讲解IOC与解耦合

    2023-06-16 12:32:50
  • 深入了解C#多线程安全

    2023-08-08 19:43:32
  • asp之家 软件编程 m.aspxhome.com