Android串口通信:串口读写

公司有个项目要用到串口通信,同事有写好一个DEMO,用的时候发现会有问题,从jni读串口数据时,经常会被截断,修改select延时还是无济于事,于是想到用JAVA直接去读/写串口文件,经过搜索在iteye上的一篇博客1可以满足需求,但看到下面留言说有问题,自己试了下确实是有问题,经过一些小小的修改验证后没问题,并整理了一个DEMO。

项目主要代码

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   
#include   
#include   
#include   

#include   
#include   
#include   
#include   
#include   
#include   
#include "log.h"
#include "com_skyworth_splicing_SerialPort.h"  
#include   
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 Java_com_skyworth_splicing_SerialPort_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,"", "()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 Java_com_skyworth_splicing_SerialPort_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/skyworth/splicing/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_6;
    //result = JNI_VERSION_1_4;  

    return result;  
}  

我把它修改成JNI通用的那种模式,把文件名和函数名改成对应JAVA文件的包名和类名,这个一定要一致,不然无法调用成功,并增加.H文件见DEMO源码的jni目录。CPP的编译直接在源码服务器编译的,没有在WINDOWS NDK下编译,有源码就没去折腾了。

JAVA代码

JAVA相关基本没改,去掉一些业务相关的代码:

SerialPort.java

package com.skyworth.splicing;

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;
/**
 * 串口操作
 * 
 * @author guoxiao
 * 
 */
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");
    }
}

SerialPortUtil.java

package com.skyworth.splicing;


import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * 串口操作
 * 
 * @author guoxiao
 * 
 */
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/ttyS3";
    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;
    }

    /**
     * 初始化串口通信
     */
    private 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();
        }
    }

    /**
     * 发送指令到串口
     * 
     * @param cmd
     * @return
     */
    public boolean sendCmds(String cmd) {
        boolean result = true;
        byte[] mBuffer = cmd.getBytes();
        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 = "";
        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);
        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) {
//                          String str = new String(buffer, 0, size);
//                          Logger.d("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() {
        isStop = true;
        if (mReadThread != null) {
            mReadThread.interrupt();
        }
        if (mSerialPort != null) {
            mSerialPort.close();
        }
    }

}

串口工具类的调用:

package com.example.serialutil;

import com.skyworth.splicing.SerialPortUtil;
import com.skyworth.splicing.SerialPortUtil.OnDataReceiveListener;

import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.View;
/*
 * @author guoxiao
 * 
 */
public class SerialActivity extends Activity {

    SerialPortUtil mSerialPortUtil;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_hello_world);
        //单模式
        mSerialPortUtil = SerialPortUtil.getInstance();
        mSerialPortUtil.setOnDataReceiveListener(new OnDataReceiveListener() {

            @Override
            public void onDataReceive(byte[] buffer, int size) {
                // TODO Auto-generated method stub
                Log.d("[gx]", " DataReceive:" + new String(buffer,0,size));
            }
        });
    }

    public void onWrite(View view){
        mSerialPortUtil.sendCmds("");
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.hello_world, menu);
        return true;
    }

}

总结:


  1. CPP的文件名和函数名一定要和JAVA的包名和类名对应
  2. 串口读操作时使用了一个线程
  3. 写串口时最终是byte类型,注意符号位的问题

DEMO下载地址:http://download.csdn.net/detail/burly/9402739


你可能感兴趣的:(Andrdoid)