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

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 <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 "log.h" #include "com_skyworth_splicing_SerialPort.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 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,"<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 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("<open,1,0,3,0,0,0,0,0,0,1910,1070>");
    }

    @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型別,注意符號位的問題

相關推薦

Android串列通訊:串列

公司有個專案要用到串列埠通訊,同事有寫好一個DEMO,用的時候發現會有問題,從jni讀串列埠資料時,經常會被截斷,修改select延時還是無濟於事,於是想到用JAVA直接去讀/寫串列埠檔案,經過搜尋在iteye上的一篇部落格1可以滿足需求,但看到下面留言說有問題

Delphi使用SPCOMM串列通訊 串列號大於10出錯的解決辦法

用Delphi 7.0編寫串列埠讀寫程式,發現有些串列埠無法開啟,打開出錯。“Error opening serial port”。 經過百度查詢有參考文章解決,複製內容過來如下: 如果你已經會使用SPCOMM且出現10個以上的串列埠問題 請改一下串列埠名

Delphi使用spcomm串列通訊 串列號大於10出錯的解決辦法 spcomm error opening serial port

//./com18是windows裡邊的SymbolLinkName,這個在WINAPI裡邊就需要這 樣寫,SPCOMM的這部分的原始碼如下: procedure TComm.StartComm; var    hNewCommFile:   THandle; begin      // Are w

串列通訊-----串列監聽執行緒全域性函式(C++)

``````` #include "windows.h" #define COMMBUFFER_SIZE 18 //每組資料位元組數 //串列埠變數 HANDLE g_hComm = INVALID_HANDLE_VALUE;//串列埠控制代碼 HANDLE g_hCommThread = NU

Android SD卡簡單的檔案操作

Android SD卡簡單的檔案讀寫操作   最近有這樣的需求,把每次統計到的資料,以txt形式儲存到手機SD卡或是手機記憶體中,遇到一些問題,記錄下來。   首先如果要在程式中使用sdcard進行儲存,我們必須要在AndroidManifset.xml檔案進行下

Android獲取外接SD卡路徑

1. 外接SD卡的一些問題 1.1 關於外接SD卡上的讀寫路徑 Android 4.4及以上版本,應用的外接SD卡讀寫路徑被限定在固定路徑上(外接SD卡根路徑/Android/data/包名/files)。 Android4.4以下版本,申請了外接SD卡讀寫許可權的應用在整個

Android 第二章 本地檔案的

讀寫的第一種方式: 使用最初始的IO方式讀寫到應用包目錄下面 package com.example.login; import java.io.BufferedReader; import java.io.File; import java.io.FileInput

android 8.0 mtk平臺新增檔案節點許可權

上層讀寫自己新增節點檔案/sys/bus/i2c/drivers/ac108/1-003b/ac108_debug/ac108_power: FileOutputStream file; try{     file = new FileOutputStream("/sy

程序通訊之檔案

檔案也可以用來作為程序通訊的工具, 1.建立 讀取者程式 // InterProcess_Communucation_FILE_READER.cpp : 定義控制檯應用程式的入口點。 // #inc

Android AsyncHttpClient登入儲存cookie和cookie

AsyncHttpClient有個特性:(11)持久化cookie儲存,可以將cookie儲存到你的應用程式的SharedPreferences中AsyncHttpClient Cookie相關的官方的文件This library also includes a Persis

Android平臺上直接實體記憶體漏洞的那些事

/* 本文章由 莫灰灰 編寫,轉載請註明出處。   */ 通過mmap直接操作實體記憶體的漏洞應該算是比較常見的一類漏洞了,在2012年、2013年的這段時間裡,爆出了好幾個實體記憶體讀寫相關的漏洞。主要是因為某些裝置本身具有mmap實體記憶體的功能,但是其許可權

android nfc中MifareClassic格式的

Android支援的資料格式 資料格式的Intent filter AndroidManifest.xml檔案中,要像向下列示例那樣,在<activity>元素內的<meta-data>元素中指定你建立的資原始檔: <activity&g

Android靜態安全檢測 -> 檔案任意

檔案任意讀寫 -openFileOutput方法 一、API 1. openFileOutput(String name , int mode) 示例:openFileOutput("text.tx

Android串列通訊串列例項

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

【C語言實現串列通訊知識點整理(三)】串列開啟、設定資料成功後進行資料

int OpenDev(char *Dev) { int fd = open(Dev,O_RDWR | O_NOCTTY | O_NONBLOCK); if(-1 == fd) { perror("Can't Open Serial Port"); return -1;

串列通訊模組3:串列通訊程式設計基礎(、關閉)

上一節總結了如何開啟串列埠並討論瞭如何配置串列埠,本節是在上一節的基礎上,進一步討論串列埠程式設計的基礎——如何進行檔案讀寫?如何關閉串列埠? 1. 讀寫串列埠 串列埠的讀寫操作和檔案的讀寫操作是一樣的,也是通過ReadFile()及WriteFile()函式來實現

用API實現串列非同步

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

C API方式串列

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

串列程式設計-超時 COMMTIMEOUTS設定

讀寫超時是在   呼叫  ReadFile 和  WriteFile   函式讀寫串列埠的時候系統提供的超時機制 typedef struct _COMMTIMEOUTS {    &nbs

用C#一步步串列通訊

http://lib.csdn.net/article/csharp/42789    附言: 1. 有網友反應我寫的這篇文章還不錯,索性就將它置頂了,希望對大家串列埠程式設計的學習有所幫助。 2.在此吐槽一下東家CSDN的編輯框,非常難用,剛才排版還好