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;
}
}
總結:
- CPP的檔名和函式名一定要和JAVA的包名和類名對應
- 串列埠讀操作時使用了一個執行緒
- 寫串列埠時最終是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的編輯框,非常難用,剛才排版還好