Androidシリアル通信:シリアル読み書き

29531 ワード

会社にはシリアル通信を使うプロジェクトがあります.同僚はDEMOを書いています.使うときに問題があることに気づきました.jniからシリアルデータを読むとき、よく遮断されます.selectの遅延を修正しても始まらないので、JAVAで直接シリアルファイルを読む/書くことを考えました.yeiteで検索したブログ1は需要を満たすことができますが、次のメッセージを見て問題があると言いました.自分で试してみたのは确かに问题があって、いくつかの小さい修正を経て検证した后に问题がなくて、そして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;
    }

}

まとめ:
  • CPPのファイル名と関数名は必ずJAVAのパッケージ名とクラス名に対応する
  • シリアルポート読み出し動作時に1つのスレッド
  • が使用する.
  • シリアルポートを書くときは最終的にbyteタイプであり、シンボルビットの問題
  • に注意する.
    DEMOダウンロードアドレス:http://download.csdn.net/detail/burly/9402739