Android串口通信之串口讀寫實(shí)例
在Android串口通信:基本知識梳理的基礎(chǔ)上,我結(jié)合我項(xiàng)目中使用串口的實(shí)例,進(jìn)行總結(jié);
Android使用jni直接進(jìn)行串口設(shè)備的讀寫網(wǎng)上已經(jīng)有開源項(xiàng)目了,本文是基于網(wǎng)上的開源項(xiàng)目在實(shí)際項(xiàng)目中的使用做的調(diào)整和優(yōu)化;
Google串口開源項(xiàng)目
下面是我項(xiàng)目中的相關(guān)代碼及介紹:
1、SerialPort.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 <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 },
};
/*
* 為某一個(gè)類注冊本地方法
*/
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")時(shí)調(diào)用
* 如果成功返回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;
}
在編譯時(shí)注意修改const char* kClassName = "com/jerome/serialport/SerialPort";為你Java層與jni對應(yīng)得包名;
2、Android.mk
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
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
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();
}
/**
* 發(fā)送指令到串口
*
* @param cmd
* @return
*/
public boolean sendCmds(String cmd) {
boolean result = true;
byte[] mBuffer = (cmd+"\r\n").getBytes();
//注意:我得項(xiàng)目中需要在每次發(fā)送后面加\r\n,大家根據(jù)項(xiàng)目項(xiàng)目做修改,也可以去掉,直接發(fā)送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);
//注意:我得項(xiàng)目中需要在每次發(fā)送后面加\r\n,大家根據(jù)項(xiàng)目項(xiàng)目做修改,也可以去掉,直接發(fā)送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;
}
}
}
}
/**
* 關(guān)閉串口
*/
public void closeSerialPort() {
sendShellCommond1();
isStop = true;
if (mReadThread != null) {
mReadThread.interrupt();
}
if (mSerialPort != null) {
mSerialPort.close();
}
}
}
5、使用方法:
a、配置ndk開發(fā)環(huán)境,具體百度一下;
b、工程根目錄下新建jni文件夾,將Android.mk和SerialPort.cpp放進(jìn)去;
c、ndk中進(jìn)入jni目錄,編譯生成so文件,默認(rèn)so生成在libs/armeabi下;
d、新建com.jerom.serialport目錄,將SerialPort和SerialPortUtil放進(jìn)去;
f、在你要使用的地方初始化SerialPortUtil,實(shí)現(xiàn)回調(diào)接口OnDataReceiveListener即可接受數(shù)據(jù);
總結(jié):
1、串口發(fā)送實(shí)質(zhì)就是向串口設(shè)備(類似于文件操作)寫入字節(jié)流,串口讀取也是一樣;
2、主要jni與Java native得對應(yīng);
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android開發(fā)筆記SQLite優(yōu)化記住密碼功能
這篇文章主要為大家詳細(xì)介紹了Android開發(fā)筆記SQLite優(yōu)化記住密碼功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07
Android實(shí)現(xiàn)音樂播放進(jìn)度條傳遞信息的兩種方式(在service和activity中)
這篇文章主要介紹了Android:在service和activity之中,實(shí)現(xiàn)音樂播放進(jìn)度條傳遞信息的兩種方式,MediaPlayer做音樂播放器采坑以及解決辦法,需要的朋友可以參考下2020-05-05
Android InputMethodManager輸入法簡介
這篇文章主要介紹了Android InputMethodManager輸入法框架的使用,具有參考價(jià)值,需要的朋友可以參考下。2016-06-06
Android Studio出現(xiàn)Failed to pull selection: open failed: Permi
本篇文章給大家分享了Android Studio中導(dǎo)出數(shù)據(jù)庫文件的方法以及出現(xiàn)Failed to pull selection: open failed: Permission denied的解決思路,有興趣的學(xué)習(xí)下。2018-05-05
Android移動(dòng)應(yīng)用開發(fā)指南之六種布局詳解
Android應(yīng)用界面要美觀好看,就需要運(yùn)用到一定的布局技術(shù),Android布局是不可忽視的,是android應(yīng)用界面開發(fā)的重要一環(huán),這篇文章主要給大家介紹了關(guān)于Android移動(dòng)應(yīng)用開發(fā)指南之六種布局的相關(guān)資料,需要的朋友可以參考下2022-09-09
Android開發(fā)之串口編程原理和實(shí)現(xiàn)方式
提到串口編程,就不得不提到JNI,不得不提到JavaAPI中的文件描述符類:FileDescriptor;下面我分別對JNI、FileDescriptor以及串口的一些知識點(diǎn)和實(shí)現(xiàn)的源碼進(jìn)行分析說明,感興趣的朋友可以了解下2013-01-01
Android xmlns 的作用及其自定義實(shí)例詳解
這篇文章主要介紹了 Android xmlns 的作用及其自定義實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-06-06

