您的位置:首页 > 移动开发 > Android开发

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

2017-06-24 13:45 302 查看
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代码  


/* 

 * 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 

Java代码  


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 

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 

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(); //原作者用来初始化Ble?? 

    }  

  

    /** 

     * 发送指令到串口 

     *  

     * @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();  //注释掉 估计是原作者用来执行一些shell命令

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

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

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

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

(串口对应的波特率)需要根据自己的项目需要进行客制化
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 串口通信