当前位置: 首页 > 编程笔记 >

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

丌官高远
2023-03-14
本文向大家介绍Android串口通信之串口读写实例,包括了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得对应;

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。

 类似资料:
  • 通过serial接口可以使Chrome应用进行串口通信。使用serial接口需要在Manifest中声明serial权限: "permissions": [ "serial" ] 本章内容参考自https://crxdoc-zh.appspot.com/apps/serial。

  • 设备通过串口RX,TX和OBLOQ进行通信,因为波特率的不同,发送的数据可能会乱码或者丢包,设备通过发送这条指令,检测设备串口和OBLOQ串口是否已经正常通信。建议设备通信之前先通过这条指令检查串口连接状态。 请求方式: "|1|1|\r" 返回值: "|1|1|\r"表示已经建立串口通信 Arduino样例: softSerial.print("|1|1|\r");

  • 本文向大家介绍C#串口通信实现方法,包括了C#串口通信实现方法的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了C#串口通信实现方法。分享给大家供大家参考。具体方法如下: 通过COM1发送数据,COM2接收数据。当COM2接收完本次发送的数据后,向COM1发送信息通知COM1本次数据已发完,COM1接到通知后,再发下一段数据。这样可以确保每次发送的数据都可以被正确接收。 代码如下: 辅助代码

  • 本文向大家介绍Android串口通信封装之OkUSB的示例代码,包括了Android串口通信封装之OkUSB的示例代码的使用技巧和注意事项,需要的朋友参考一下 本文介绍了Android串口通信封装之OkUSB的示例代码,分享给大家。具体如下: Github传送门:OkUSB OkUSB 一个简洁的Android串口通信框架。 功能简介 支持设置波特率 支持设置数据位 支持设置停止位 支持设置校验位

  • 本文向大家介绍Python 串口读写的实现方法,包括了Python 串口读写的实现方法的使用技巧和注意事项,需要的朋友参考一下 1.安装pyserial https://pypi.python.org/pypi/pyserial Doc:http://pythonhosted.org/pyserial/ 使用Python Package Index (PyPi) 2. Demo 3.结果 以上这篇

  • 我试图读取3温度设备使用WinForm和Modbus 485协议。基本上,我必须定期向每个设备写入命令,等待响应,当我得到响应时,处理它。每个设备都有一个唯一的通信地址。为了定期发送命令,我使用了计时器。这就是我发送命令的方式和处理响应的地方: 对于每个设备,等等。这是我的serialport数据接收事件: 所以我的沟通应该是: 然后再次发送命令device1。问题是,我有时得到通信超时错误,我知