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

来源:互联网 发布:淘宝达人直播申请 编辑:程序博客网 时间:2024/04/19 23:34

FROM: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.   
  21. #include <termios.h>  
  22. #include <unistd.h>  
  23. #include <sys/types.h>  
  24. #include <sys/stat.h>  
  25. #include <fcntl.h>  
  26. #include <string.h>  
  27. #include <jni.h>  
  28.   
  29. #include "android/log.h"  
  30. static const char *TAG = "serial_port";  
  31. #define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO,  TAG, fmt, ##args)  
  32. #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)  
  33. #define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)  
  34.   
  35. static speed_t getBaudrate(jint baudrate) {  
  36.     switch (baudrate) {  
  37.     case 0:  
  38.         return B0;  
  39.     case 50:  
  40.         return B50;  
  41.     case 75:  
  42.         return B75;  
  43.     case 110:  
  44.         return B110;  
  45.     case 134:  
  46.         return B134;  
  47.     case 150:  
  48.         return B150;  
  49.     case 200:  
  50.         return B200;  
  51.     case 300:  
  52.         return B300;  
  53.     case 600:  
  54.         return B600;  
  55.     case 1200:  
  56.         return B1200;  
  57.     case 1800:  
  58.         return B1800;  
  59.     case 2400:  
  60.         return B2400;  
  61.     case 4800:  
  62.         return B4800;  
  63.     case 9600:  
  64.         return B9600;  
  65.     case 19200:  
  66.         return B19200;  
  67.     case 38400:  
  68.         return B38400;  
  69.     case 57600:  
  70.         return B57600;  
  71.     case 115200:  
  72.         return B115200;  
  73.     case 230400:  
  74.         return B230400;  
  75.     case 460800:  
  76.         return B460800;  
  77.     case 500000:  
  78.         return B500000;  
  79.     case 576000:  
  80.         return B576000;  
  81.     case 921600:  
  82.         return B921600;  
  83.     case 1000000:  
  84.         return B1000000;  
  85.     case 1152000:  
  86.         return B1152000;  
  87.     case 1500000:  
  88.         return B1500000;  
  89.     case 2000000:  
  90.         return B2000000;  
  91.     case 2500000:  
  92.         return B2500000;  
  93.     case 3000000:  
  94.         return B3000000;  
  95.     case 3500000:  
  96.         return B3500000;  
  97.     case 4000000:  
  98.         return B4000000;  
  99.     default:  
  100.         return -1;  
  101.     }  
  102. }  
  103.   
  104. /* 
  105.  * Class:     cedric_serial_SerialPort 
  106.  * Method:    open 
  107.  * Signature: (Ljava/lang/String;)V 
  108.  */  
  109. JNIEXPORT jobject JNICALL native_open(JNIEnv *env, jobject thiz, jstring path,jint baudrate) {  
  110.     int fd;  
  111.     speed_t speed;  
  112.     jobject mFileDescriptor;  
  113.   
  114.     LOGD("init native Check arguments");  
  115.     /* Check arguments */  
  116.     {  
  117.         speed = getBaudrate(baudrate);  
  118.         if (speed == -1) {  
  119.             /* TODO: throw an exception */  
  120.             LOGE("Invalid baudrate");  
  121.             return NULL;  
  122.         }  
  123.     }  
  124.   
  125.     LOGD("init native Opening device!");  
  126.     /* Opening device */  
  127.     {  
  128.         jboolean iscopy;  
  129.         const char *path_utf = env->GetStringUTFChars(path, &iscopy);  
  130.         LOGD("Opening serial port %s", path_utf);  
  131. //      fd = open(path_utf, O_RDWR | O_DIRECT | O_SYNC);  
  132.         fd = open(path_utf, O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY);  
  133.         LOGD("open() fd = %d", fd);  
  134.         env->ReleaseStringUTFChars(path, path_utf);  
  135.         if (fd == -1) {  
  136.             /* Throw an exception */  
  137.             LOGE("Cannot open port %d",baudrate);  
  138.             /* TODO: throw an exception */  
  139.             return NULL;  
  140.         }  
  141.     }  
  142.   
  143.     LOGD("init native Configure device!");  
  144.     /* Configure device */  
  145.     {  
  146.         struct termios cfg;  
  147.         if (tcgetattr(fd, &cfg)) {  
  148.             LOGE("Configure device tcgetattr() failed 1");  
  149.             close(fd);  
  150.             return NULL;  
  151.         }  
  152.   
  153.         cfmakeraw(&cfg);  
  154.         cfsetispeed(&cfg, speed);  
  155.         cfsetospeed(&cfg, speed);  
  156.   
  157.         if (tcsetattr(fd, TCSANOW, &cfg)) {  
  158.             LOGE("Configure device tcsetattr() failed 2");  
  159.             close(fd);  
  160.             /* TODO: throw an exception */  
  161.             return NULL;  
  162.         }  
  163.     }  
  164.   
  165.     /* Create a corresponding file descriptor */  
  166.     {  
  167.         jclass cFileDescriptor = env->FindClass("java/io/FileDescriptor");  
  168.         jmethodID iFileDescriptor = env->GetMethodID(cFileDescriptor,"<init>""()V");  
  169.         jfieldID descriptorID = env->GetFieldID(cFileDescriptor,"descriptor""I");  
  170.         mFileDescriptor = env->NewObject(cFileDescriptor,iFileDescriptor);  
  171.         env->SetIntField(mFileDescriptor, descriptorID, (jint) fd);  
  172.     }  
  173.   
  174.     return mFileDescriptor;  
  175. }  
  176.   
  177. /* 
  178.  * Class:     cedric_serial_SerialPort 
  179.  * Method:    close 
  180.  * Signature: ()V 
  181.  */  
  182. JNIEXPORT jint JNICALL native_close(JNIEnv * env, jobject thiz)  
  183. {  
  184.     jclass SerialPortClass = env->GetObjectClass(thiz);  
  185.     jclass FileDescriptorClass = env->FindClass("java/io/FileDescriptor");  
  186.   
  187.     jfieldID mFdID = env->GetFieldID(SerialPortClass, "mFd""Ljava/io/FileDescriptor;");  
  188.     jfieldID descriptorID = env->GetFieldID(FileDescriptorClass, "descriptor""I");  
  189.   
  190.     jobject mFd = env->GetObjectField(thiz, mFdID);  
  191.     jint descriptor = env->GetIntField(mFd, descriptorID);  
  192.   
  193.     LOGD("close(fd = %d)", descriptor);  
  194.     close(descriptor);  
  195.     return 1;  
  196. }  
  197.   
  198. static JNINativeMethod gMethods[] = {  
  199.         { "open""(Ljava/lang/String;I)Ljava/io/FileDescriptor;",(void*) native_open },  
  200.         { "close""()I",(void*) native_close },  
  201. };  
  202.   
  203. /* 
  204.  * 为某一个类注册本地方法 
  205.  */  
  206. static int registerNativeMethods(JNIEnv* env, const char* className,  
  207.         JNINativeMethod* gMethods, int numMethods) {  
  208.     jclass clazz;  
  209.     clazz = env->FindClass(className);  
  210.     if (clazz == NULL) {  
  211.         return JNI_FALSE;  
  212.     }  
  213.     if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {  
  214.         return JNI_FALSE;  
  215.     }  
  216.   
  217.     return JNI_TRUE;  
  218. }  
  219.   
  220. /* 
  221.  * 为所有类注册本地方法 
  222.  */  
  223. static int registerNatives(JNIEnv* env) {  
  224.     const char* kClassName = "com/jerome/serialport/SerialPort"//指定要注册的类  
  225.     return registerNativeMethods(env, kClassName, gMethods,  
  226.             sizeof(gMethods) / sizeof(gMethods[0]));  
  227. }  
  228.   
  229. /* 
  230.  * System.loadLibrary("lib")时调用 
  231.  * 如果成功返回JNI版本, 失败返回-1 
  232.  */  
  233. JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {  
  234.     JNIEnv* env = NULL;  
  235.     jint result = -1;  
  236.   
  237.     if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {  
  238.         return -1;  
  239.     }  
  240.     assert(env != NULL);  
  241.   
  242.     if (!registerNatives(env)) { //注册  
  243.         return -1;  
  244.     }  
  245.     //成功  
  246.     result = JNI_VERSION_1_4;  
  247.   
  248.     return result;  
  249. }  


在编译时注意修改const char* kClassName = "com/jerome/serialport/SerialPort";为你Java层与jni对应得包名; 

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


如果要修改生成so文件的名称,请修改LOCAL_MODULE    := serial_port 

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


4、SerialPortUtil.java 

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


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


PS:以上代码可以直接拷贝粘贴使用,移植时需要注意

SerialPortUtil.java的initBle(); 和sendShellCommond1(); 需要注释掉 或者自己定义

SerialPortUtil.java的private String path = "/dev/ttyMT1";(串口对应的设备文件)和 private int baudrate = 115200;

(串口对应的波特率)需要根据自己的项目需要进行客制化

原创粉丝点击