1. 程式人生 > >Android串列埠通訊:串列埠讀寫例項

Android串列埠通訊:串列埠讀寫例項

轉自:http://gqdy365.iteye.com/blog/2188906

在Android串列埠通訊:基本知識梳理(http://gqdy365.iteye.com/admin/blogs/2188846)的基礎上,我結合我專案中使用串列埠的例項,進行總結;


Android使用jni直接進行串列埠裝置的讀寫網上已經有開源專案了,本文是基於網上的開源專案在實際專案中的使用做的調整和優化;
Google串列埠開源專案見:https://code.google.com/p/android-serialport-api/

下面是我專案中的相關程式碼及介紹:

1、SerialPort.cpp
Java程式碼  收藏程式碼
  1. /* 
  2.  * Copyright 2009 Cedric Priscal
     
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  * http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
     
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */
  16. #include <stdlib.h>  
  17. #include <stdio.h>  
  18. #include <jni.h>  
  19. #include <assert.h>  
  20. #include <termios.h>  
  21. #include <unistd.h>  
  22. #include <sys/types.h>  
  23. #include <sys/stat.h>  
  24. #include <fcntl.h>  
  25. #include <string.h>  
  26. #include <jni.h>  
  27. #include "android/log.h"
  28. staticconstchar *TAG = "serial_port";  
  29. #define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO,  TAG, fmt, ##args)  
  30. #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)  
  31. #define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)  
  32. static speed_t getBaudrate(jint baudrate) {  
  33.     switch (baudrate) {  
  34.     case0:  
  35.         return B0;  
  36.     case50:  
  37.         return B50;  
  38.     case75:  
  39.         return B75;  
  40.     case110:  
  41.         return B110;  
  42.     case134:  
  43.         return B134;  
  44.     case150:  
  45.         return B150;  
  46.     case200:  
  47.         return B200;  
  48.     case300:  
  49.         return B300;  
  50.     case600:  
  51.         return B600;  
  52.     case1200:  
  53.         return B1200;  
  54.     case1800:  
  55.         return B1800;  
  56.     case2400:  
  57.         return B2400;  
  58.     case4800:  
  59.         return B4800;  
  60.     case9600:  
  61.         return B9600;  
  62.     case19200:  
  63.         return B19200;  
  64.     case38400:  
  65.         return B38400;  
  66.     case57600:  
  67.         return B57600;  
  68.     case115200:  
  69.         return B115200;  
  70.     case230400:  
  71.         return B230400;  
  72.     case460800:  
  73.         return B460800;  
  74.     case500000:  
  75.         return B500000;  
  76.     case576000:  
  77.         return B576000;  
  78.     case921600:  
  79.         return B921600;  
  80.     case1000000:  
  81.         return B1000000;  
  82.     case1152000:  
  83.         return B1152000;  
  84.     case1500000:  
  85.         return B1500000;  
  86.     case2000000:  
  87.         return B2000000;  
  88.     case2500000:  
  89.         return B2500000;  
  90.     case3000000:  
  91.         return B3000000;  
  92.     case3500000:  
  93.         return B3500000;  
  94.     case4000000:  
  95.         return B4000000;  
  96.     default:  
  97.         return -1;  
  98.     }  
  99. }  
  100. /* 
  101.  * Class:     cedric_serial_SerialPort 
  102.  * Method:    open 
  103.  * Signature: (Ljava/lang/String;)V 
  104.  */
  105. JNIEXPORT jobject JNICALL native_open(JNIEnv *env, jobject thiz, jstring path,jint baudrate) {  
  106.     int fd;  
  107.     speed_t speed;  
  108.     jobject mFileDescriptor;  
  109.     LOGD("init native Check arguments");  
  110.     /* Check arguments */
  111.     {  
  112.         speed = getBaudrate(baudrate);  
  113.         if (speed == -1) {  
  114.             /* TODO: throw an exception */
  115.             LOGE("Invalid baudrate");  
  116.             return NULL;  
  117.         }  
  118.     }  
  119.     LOGD("init native Opening device!");  
  120.     /* Opening device */
  121.     {  
  122.         jboolean iscopy;  
  123.         constchar *path_utf = env->GetStringUTFChars(path, &iscopy);  
  124.         LOGD("Opening serial port %s", path_utf);  
  125. //      fd = open(path_utf, O_RDWR | O_DIRECT | O_SYNC);
  126.         fd = open(path_utf, O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY);  
  127.         LOGD("open() fd = %d", fd);  
  128.         env->ReleaseStringUTFChars(path, path_utf);  
  129.         if (fd == -1) {  
  130.             /* Throw an exception */
  131.             LOGE("Cannot open port %d",baudrate);  
  132.             /* TODO: throw an exception */
  133.             return NULL;  
  134.         }  
  135.     }  
  136.     LOGD("init native Configure device!");  
  137.     /* Configure device */
  138.     {  
  139.         struct termios cfg;  
  140.         if (tcgetattr(fd, &cfg)) {  
  141.             LOGE("Configure device tcgetattr() failed 1");  
  142.             close(fd);  
  143.             return NULL;  
  144.         }  
  145.         cfmakeraw(&cfg);  
  146.         cfsetispeed(&cfg, speed);  
  147.         cfsetospeed(&cfg, speed);  
  148.         if (tcsetattr(fd, TCSANOW, &cfg)) {  
  149.             LOGE("Configure device tcsetattr() failed 2");  
  150.             close(fd);  
  151.             /* TODO: throw an exception */
  152.             return NULL;  
  153.         }  
  154.     }  
  155.     /* Create a corresponding file descriptor */
  156.     {  
  157.         jclass cFileDescriptor = env->FindClass("java/io/FileDescriptor");  
  158.         jmethodID iFileDescriptor = env->GetMethodID(cFileDescriptor,"<init>""()V");  
  159.         jfieldID descriptorID = env->GetFieldID(cFileDescriptor,"descriptor""I");  
  160.         mFileDescriptor = env->NewObject(cFileDescriptor,iFileDescriptor);  
  161.         env->SetIntField(mFileDescriptor, descriptorID, (jint) fd);  
  162.     }  
  163.     return mFileDescriptor;  
  164. }  
  165. /* 
  166.  * Class:     cedric_serial_SerialPort 
  167.  * Method:    close 
  168.  * Signature: ()V 
  169.  */
  170. JNIEXPORT jint JNICALL native_close(JNIEnv * env, jobject thiz)  
  171. {  
  172.     jclass SerialPortClass = env->GetObjectClass(thiz);  
  173.     jclass FileDescriptorClass = env->FindClass("java/io/FileDescriptor");  
  174.     jfieldID mFdID = env->GetFieldID(SerialPortClass, "mFd""Ljava/io/FileDescriptor;");  
  175.     jfieldID descriptorID = env->GetFieldID(FileDescriptorClass, "descriptor""I");  
  176.     jobject mFd = env->GetObjectField(thiz, mFdID);  
  177.     jint descriptor = env->GetIntField(mFd, descriptorID);  
  178.     LOGD("close(fd = %d)", descriptor);  
  179.     close(descriptor);  
  180.     return1;  
  181. }  
  182. static JNINativeMethod gMethods[] = {  
  183.         { "open""(Ljava/lang/String;I)Ljava/io/FileDescriptor;",(void*) native_open },  
  184.         { "close""()I",(void*) native_close },  
  185. };  
  186. /* 
  187.  * 為某一個類註冊本地方法 
  188.  */
  189. staticint registerNativeMethods(JNIEnv* env, constchar* className,  
  190.         JNINativeMethod* gMethods, int numMethods) {  
  191.     jclass clazz;  
  192.     clazz = env->FindClass(className);  
  193.     if (clazz == NULL) {  
  194.         return JNI_FALSE;  
  195.     }  
  196.     if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {  
  197.         return JNI_FALSE;  
  198.     }  
  199.     return JNI_TRUE;  
  200. }  
  201. /* 
  202.  * 為所有類註冊本地方法 
  203.  */
  204. staticint registerNatives(JNIEnv* env) {  
  205.     constchar* kClassName = "com/jerome/serialport/SerialPort"//指定要註冊的類
  206.     return registerNativeMethods(env, kClassName, gMethods,  
  207.             sizeof(gMethods) / sizeof(gMethods[0]));  
  208. }  
  209. /* 
  210.  * System.loadLibrary("lib")時呼叫 
  211.  * 如果成功返回JNI版本, 失敗返回-1 
  212.  */
  213. JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {  
  214.     JNIEnv* env = NULL;  
  215.     jint result = -1;  
  216.     if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {  
  217.         return -1;  
  218.     }  
  219.     assert(env != NULL);  
  220.     if (!registerNatives(env)) { //註冊
  221.         return -1;  
  222.     }  
  223.     //成功
  224.     result = JNI_VERSION_1_4;  
  225.     return result;  
  226. }  


在編譯時注意修改const char* kClassName = "com/jerome/serialport/SerialPort";為你Java層與jni對應得包名;

2、Android.mk
Java程式碼  收藏程式碼
  1. LOCAL_PATH := $(call my-dir)  
  2. include $(CLEAR_VARS)  
  3. TARGET_PLATFORM := android-3
  4. LOCAL_MODULE    := serial_port  
  5. LOCAL_SRC_FILES := SerialPort.cpp  
  6. LOCAL_LDLIBS    := -llog  
  7. include $(BUILD_SHARED_LIBRARY)  


如果要修改生成so檔案的名稱,請修改LOCAL_MODULE    := serial_port

3、SerialPort.java
Java程式碼  收藏程式碼
  1. package com.jerome.serialport;  
  2. import java.io.File;  
  3. import java.io.FileDescriptor;  
  4. import java.io.FileInputStream;  
  5. import java.io.FileOutputStream;  
  6. import java.io.IOException;  
  7. import java.io.InputStream;  
  8. import java.io.OutputStream;  
  9. publicclass SerialPort {  
  10.     privatestaticfinal String TAG = "SerialPort";  
  11.     /* 
  12.      * Do not remove or rename the field mFd: it is used by native method close(); 
  13.      */
  14.     private FileDescriptor mFd;  
  15.     private FileInputStream mFileInputStream;  
  16.     private FileOutputStream mFileOutputStream;  
  17.     public SerialPort(File device, int baudrate) throws SecurityException, IOException {  
  18.         mFd = open(device.getAbsolutePath(), baudrate);  
  19.         if (mFd == null) {  
  20.             thrownew IOException();  
  21.         }  
  22.         mFileInputStream = new FileInputStream(mFd);  
  23.         mFileOutputStream = new FileOutputStream(mFd);  
  24.     }  
  25.     public InputStream getInputStream() {  
  26.         return mFileInputStream;  
  27.     }  
  28.     public OutputStream getOutputStream() {  
  29.         return mFileOutputStream;  
  30.     }  
  31.     privatenative FileDescriptor open(String path, int baudrate);  
  32.     publicnativeint close();  
  33.     static {  
  34.         System.loadLibrary("serial_port");  
  35.     }  
  36. }  


4、SerialPortUtil.java

Java程式碼  收藏程式碼
  1. package com.jerome.serialport;  
  2. import java.io.BufferedWriter;  
  3. import java.io.File;  
  4. import java.io.IOException;  
  5. import java.io.InputStream;  
  6. import java.io.OutputStream;  
  7. import java.io.OutputStreamWriter;  
  8. import java.io.PrintWriter;  
  9. /** 
  10.  * 串列埠操作類 
  11.  *  
  12.  * @author Jerome 
  13.  *  
  14.  */
  15. publicclass SerialPortUtil {  
  16.     private String TAG = SerialPortUtil.class.getSimpleName();  
  17.     private SerialPort mSerialPort;  
  18.     private OutputStream mOutputStream;  
  19.     private InputStream mInputStream;  
  20.     private ReadThread mReadThread;  
  21.     private String path = "/dev/ttyMT1";  
  22.     privateint baudrate = 115200;  
  23.     privatestatic SerialPortUtil portUtil;  
  24.     private OnDataReceiveListener onDataReceiveListener = null;  
  25.     privateboolean isStop = false;  
  26.     publicinterface OnDataReceiveListener {  
  27.         publicvoid onDataReceive(byte[] buffer, int size);  
  28.     }  
  29.     publicvoid setOnDataReceiveListener(  
  30.             OnDataReceiveListener dataReceiveListener) {  
  31.         onDataReceiveListener = dataReceiveListener;  
  32.     }  
  33.     publicstatic SerialPortUtil getInstance() {  
  34.         if (null == portUtil) {  
  35.             portUtil = new SerialPortUtil();  
  36.             portUtil.onCreate();  
  37.         }  
  38.         return portUtil;  
  39.     }  
  40.     /** 
  41.      * 初始化串列埠資訊 
  42.      */
  43.     publicvoid onCreate() {  
  44.         try {  
  45.             mSerialPort = new SerialPort(new File(path), baudrate);  
  46.             mOutputStream = mSerialPort.getOutputStream();  
  47.             mInputStream = mSerialPort.getInputStream();  
  48.             mReadThread = new ReadThread();  
  49.             isStop = false;  
  50.             mReadThread.start();  
  51.         } catch (Exception e) {  
  52.             e.printStackTrace();  
  53.         }  
  54.         initBle();  
  55.     }  
  56.     /** 
  57.      * 傳送指令到串列埠 
  58.      *  
  59.      * @param cmd 
  60.      * @return 
  61.      */
  62.     publicboolean sendCmds(String cmd) {  
  63.         boolean result = true;  
  64.         byte[] mBuffer = (cmd+"\r\n").getBytes();  
  65. //注意:我得專案中需要在每次傳送後面加\r\n,大家根據專案專案做修改,也可以去掉,直接傳送mBuffer
  66.         try {  
  67.             if (mOutputStream != null) {  
  68.                 mOutputStream.write(mBuffer);  
  69.             } else {  
  70.                 result = false;  
  71.             }  
  72.         } catch (IOException e) {  
  73.             e.printStackTrace();  
  74.             result = false;  
  75.         }  
  76.         return result;  
  77.     }  
  78.     publicboolean sendBuffer(byte[] mBuffer) {  
  79.         boolean result = true;  
  80.         String tail = "\r\n";  
  81.         byte[] tailBuffer = tail.getBytes();  
  82.         byte[] mBufferTemp = newbyte[mBuffer.length+tailBuffer.length];  
  83.         System.arraycopy(mBuffer, 0, mBufferTemp, 0, mBuffer.length);  
  84.         System.arraycopy(tailBuffer, 0, mBufferTemp, mBuffer.length, tailBuffer.length);  
  85. //注意:我得專案中需要在每次傳送後面加\r\n,大家根據專案專案做修改,也可以去掉,直接傳送mBuffer
  86.         try {  
  87.             if (mOutputStream != null) {  
  88.                 mOutputStream.write(mBufferTemp);  
  89.             } else {  
  90.                 result = false;  
  91.             }  
  92.         } catch (IOException e) {  
  93.             e.printStackTrace();  
  94.             result = false;  
  95.         }  
  96.         return result;  
  97.     }  
  98.     privateclass ReadThread extends Thread {  
  99.         @Override
  100.         publicvoid run() {  
  101.             super.run();  
  102.             while (!isStop && !isInterrupted()) {  
  103.                 int size;  
  104.                 try {  
  105.                     if (mInputStream == null)  
  106.                         return;  
  107.                     byte[] buffer = newbyte[512];  
  108.                     size = mInputStream.read(buffer);  
  109.                     if (size > 0) {  
  110.                         if(MyLog.isDyeLevel()){  
  111.                             MyLog.log(TAG, MyLog.DYE_LOG_LEVEL, "length is:"+size+",data is:"+new String(buffer, 0, size));  
  112.                         }  
  113.                         if (null != onDataReceiveListener) {  
  114.                             onDataReceiveListener.onDataReceive(buffer, size);  
  115.                         }  
  116.                     }  
  117.                     Thread.sleep(10);  
  118.                 } catch (Exception e) {  
  119.                     e.printStackTrace();  
  120.                     return;  
  121.                 }  
  122.             }  
  123.         }  
  124.     }  
  125.     /** 
  126.      * 關閉串列埠 
  127.      */
  128.     publicvoid closeSerialPort() {  
  129.         sendShellCommond1();  
  130.         isStop = true;  
  131.         if (mReadThread != null) {  
  132.             mReadThread.interrupt();  
  133.         }  
  134.         if (mSerialPort != null) {  
  135.             mSerialPort.close();  
  136.         }  
  137.     }  
  138. }  


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得對應;