溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Android中怎么實(shí)現(xiàn)串口讀寫

發(fā)布時(shí)間:2021-07-12 11:48:18 來源:億速云 閱讀:148 作者:Leah 欄目:移動(dòng)開發(fā)

這篇文章給大家介紹Android中怎么實(shí)現(xiàn)串口讀寫,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。

1、SerialPort.cpp

#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 }, 
}; 
 
/* 
 * 為某一個(gè)類注冊(cè)本地方法 
 */ 
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; 
} 
 
/* 
 * 為所有類注冊(cè)本地方法 
 */ 
static int registerNatives(JNIEnv* env) { 
 const char* kClassName = "com/jerome/serialport/SerialPort"; //指定要注冊(cè)的類 
 return registerNativeMethods(env, kClassName, gMethods, 
   sizeof(gMethods) / sizeof(gMethods[0])); 
} 
 
/* 
 * System.loadLibrary("lib")時(shí)調(diào)用 
 * 如果成功返回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)) { //注冊(cè) 
  return -1; 
 } 
 //成功 
 result = JNI_VERSION_1_4; 
 
 return result; 
}

在編譯時(shí)注意修改const char* kClassName = "com/jerome/serialport/SerialPort";為你Java層與jni對(duì)應(yīng)得包名;

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文件的名稱,請(qǐng)修改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(); 
 } 
 
 /** 
  * 發(fā)送指令到串口 
  * 
  * @param cmd 
  * @return 
  */ 
 public boolean sendCmds(String cmd) { 
  boolean result = true; 
  byte[] mBuffer = (cmd+"\r\n").getBytes(); 
//注意:我得項(xiàng)目中需要在每次發(fā)送后面加\r\n,大家根據(jù)項(xiàng)目項(xiàng)目做修改,也可以去掉,直接發(fā)送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); 
//注意:我得項(xiàng)目中需要在每次發(fā)送后面加\r\n,大家根據(jù)項(xiàng)目項(xiàng)目做修改,也可以去掉,直接發(fā)送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; 
    } 
   } 
  } 
 } 
 
 /** 
  * 關(guān)閉串口 
  */ 
 public void closeSerialPort() { 
  sendShellCommond1(); 
  isStop = true; 
  if (mReadThread != null) { 
   mReadThread.interrupt(); 
  } 
  if (mSerialPort != null) { 
   mSerialPort.close(); 
  } 
 } 
  
}

5、使用方法:

a、配置ndk開發(fā)環(huán)境,具體百度一下;
b、工程根目錄下新建jni文件夾,將Android.mk和SerialPort.cpp放進(jìn)去;
c、ndk中進(jìn)入jni目錄,編譯生成so文件,默認(rèn)so生成在libs/armeabi下;
d、新建com.jerom.serialport目錄,將SerialPort和SerialPortUtil放進(jìn)去;
 f、在你要使用的地方初始化SerialPortUtil,實(shí)現(xiàn)回調(diào)接口OnDataReceiveListener即可接受數(shù)據(jù);

關(guān)于Android中怎么實(shí)現(xiàn)串口讀寫就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI