`

Android串口通信:串口读写实例

 
阅读更多
在Android串口通信:基本知识梳理(http://gqdy365.iteye.com/admin/blogs/2188846)的基础上,我结合我项目中使用串口的实例,进行总结;

Android使用jni直接进行串口设备的读写网上已经有开源项目了,本文是基于网上的开源项目在实际项目中的使用做的调整和优化;
Google串口开源项目见:https://code.google.com/p/android-serialport-api/

下面是我项目中的相关代码及介绍:

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 },
};

/*
 * 为某一个类注册本地方法
 */
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")时调用
 * 如果成功返回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;
}



在编译时注意修改const char* kClassName = "com/jerome/serialport/SerialPort";为你Java层与jni对应得包名;

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();
	}

	/**
	 * 发送指令到串口
	 * 
	 * @param cmd
	 * @return
	 */
	public boolean sendCmds(String cmd) {
		boolean result = true;
		byte[] mBuffer = (cmd+"\r\n").getBytes();
//注意:我得项目中需要在每次发送后面加\r\n,大家根据项目项目做修改,也可以去掉,直接发送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);
//注意:我得项目中需要在每次发送后面加\r\n,大家根据项目项目做修改,也可以去掉,直接发送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;
				}
			}
		}
	}

	/**
	 * 关闭串口
	 */
	public void closeSerialPort() {
		sendShellCommond1();
		isStop = true;
		if (mReadThread != null) {
			mReadThread.interrupt();
		}
		if (mSerialPort != null) {
			mSerialPort.close();
		}
	}
	
}


5、使用方法:
a、配置ndk开发环境,具体百度一下;
b、工程根目录下新建jni文件夹,将Android.mk和SerialPort.cpp放进去;
c、ndk中进入jni目录,编译生成so文件,默认so生成在libs/armeabi下;
d、新建com.jerom.serialport目录,将SerialPort和SerialPortUtil放进去;
f、在你要使用的地方初始化SerialPortUtil,实现回调接口OnDataReceiveListener即可接受数据;


总结:
1、串口发送实质就是向串口设备(类似于文件操作)写入字节流,串口读取也是一样;
2、主要jni与Java native得对应;
4
0
分享到:
评论
8 楼 严健严健 2016-11-02  
非常感谢!!!!!!!!!
7 楼 manlan123 2016-10-12  
请问 sendBuffer  sendCmds 里面传入的数据是什么样的啊? 能贴一下吗
6 楼 huangzongwu 2016-05-17  
稍微改改就可以用在自己的项目,实在是太感谢了,csdn上看到的另一篇用HAL的根本没法编译
5 楼 gqdy365 2016-02-15  
zyzyzy123 写道
gqdy365 写道
zyzyzy123 写道
学长你好,能否把这个工程提供给我一份,刚接触串口,急需,万分感谢啊

完成的jni部分已经都贴出来了,你要自己编译一下,生成SO文件才能用,其他代码都是我项目相关的代码,没办法分享;

SerialPortUtil.java,这个文件代码贴进去有错误,我主要是通过串口向gprs发送数据,有没有相关的代码啊


跟GPRS通信的没有现成的代码,不过jni串口通信这一块可以共用
4 楼 zyzyzy123 2015-12-18  
gqdy365 写道
zyzyzy123 写道
学长你好,能否把这个工程提供给我一份,刚接触串口,急需,万分感谢啊

完成的jni部分已经都贴出来了,你要自己编译一下,生成SO文件才能用,其他代码都是我项目相关的代码,没办法分享;

SerialPortUtil.java,这个文件代码贴进去有错误,我主要是通过串口向gprs发送数据,有没有相关的代码啊
3 楼 gqdy365 2015-12-18  
zyzyzy123 写道
学长你好,能否把这个工程提供给我一份,刚接触串口,急需,万分感谢啊

完成的jni部分已经都贴出来了,你要自己编译一下,生成SO文件才能用,其他代码都是我项目相关的代码,没办法分享;
2 楼 zyzyzy123 2015-12-17  
学长你好,能否把这个工程提供给我一份,刚接触串口,急需,万分感谢啊
1 楼 YY793924556 2015-11-23  
能否提供一下源码啊 谢谢

相关推荐

    Android串口通信之串口读写实例

    在Android串口通信:基本知识梳理的基础上,我结合我项目中使用串口的实例,进行总结; Android使用jni直接进行串口设备的读写网上已经有开源项目了,本文是基于网上的开源项目在实际项目中的使用做的调整和优化; ...

    Android 串口通信实例 基于eclipse开发

    基于开源的android_serialport_api,实现串口数据的读写 设置设备打开的串口号和波特率 如:/dev/ttyUSB0 115200 public interface Reader { public boolean open() throws SecurityException, IOException; //...

    VB串口通信UART程序实例源码合集学习资料(200个).zip

    VB串口通信UART程序实例源码200个合集: GE PLC串口通讯,VB编制,读取内存单元.rar PC机与51单片机之间的串口通讯,VB编的,分PC和单片机两部分.rar VB Modbus RTU源码,其中协议部分已生成DLL,可直接调用.rar VB.net...

    VB串口通信源码210个

    002、Visual Basic串口通信工程开发实例导航随书源码7个 003、Visual Basic串口通信与测控应用技术实战详解 源代码(15个全) 004、GE PLC串口通讯,VB编制,读取内存单元 005、PC机与51单片机之间的串口通讯,VB编的,...

    visual C++_Turbo C串口通信编程实践

    11.3.2 RS-422与RS-485串行接口标准 11.3.3 RS-422与RS-485的网络安装注意要点 11.3.4 RS-232、RS422、RS485电气参数对比 11.4 串口调试注意事项 11.5 常用数据校验法 11.5.1奇偶校验 11.5.2 循环冗余码...

    android 使用服务读取串口数据实例

    android 使用服务读取串口数据实例,读取的数据实时写到txt文本。串口读写的库的地址:https://code.google.com/p/android-serialport-api/。我的博客:http://blog.csdn.net/qq_16064871。

    c++串口读写实例源代码

    VC实现串口通讯,数据接收、数据发送,数据解析,以回调函数的方式给其它调用 的地方或者界面直接使用。

    Visual Basic 数据采集与串口通信测控应用实战(part1)

    5.1 串行接口简介 131 5.1.1 串口通信的基本概念 131 5.1.2 rs-232c串口通信标准 136 5.1.3 串口通信线路连接 139 5.1.4 计算机中的串行端口 141 5.2 串行通信控件mscomm 143 5.2.1 mscomm控件...

    Visual C++/Turbo C串口通信编程实践及源代码-3

    11.3.2 rs-422与rs-485串行接口标准 315 11.3.3 rs-422与rs-485的网络安装注意要点 317 11.3.4 rs-232、rs422、rs485电气参数对比 318 11.4 串口调试注意事项 318 11.5 常用数据校验法 318 11.5.1 奇偶校验 318...

    Visual C++_Turbo C 串口通信编程实践.(电子工业.龚建伟.熊光明) 第二版 电子版

    11.3.2 RS-422与RS-485串行接口标准 315 11.3.3 RS-422与RS-485的网络安装注意要点 317 11.3.4 RS-232、RS422、RS485电气参数对比 318 11.4 串口调试注意事项 318 11.5 常用数据校验法 318 11.5.1 奇偶校验 318 ...

    Visual C++串口通信技术详解.(机械工业.李景峰.杨丽娜.潘恒)

    主要涵盖串口通信的理论基础、Visual c++集成开发环境简介、MSComm控件串口编程、Windows API串口编程、TAPI通信编程、串口实现双机互连、串口调试精灵、串口控制Modem设备、串口控制单片机、串口控制PLC、串口控制...

    MFC实战篇-VS实现基于MFC的串口通信程序实例-串口通信.pdf

    mfc串口通信的接收与发送 一、串口通信的概念 二、串口通信的三种协议 2.1 RS-232 2.2 RS-422 2.3 RS-485 三、串口通信方法和用处 3.1 用处 3.2 串口通信方法 四、重要参数 4.1 波特率 4.2 数据位 4.3 停止位 4.4 ...

    Visual Basic 数据采集与串口通信测控应用实战(part2)

    5.1 串行接口简介 131 5.1.1 串口通信的基本概念 131 5.1.2 rs-232c串口通信标准 136 5.1.3 串口通信线路连接 139 5.1.4 计算机中的串行端口 141 5.2 串行通信控件mscomm 143 5.2.1 mscomm控件...

    Visual C++/Turbo C串口通信编程实践 及源代码-1

    11.3.2 rs-422与rs-485串行接口标准 315 11.3.3 rs-422与rs-485的网络安装注意要点 317 11.3.4 rs-232、rs422、rs485电气参数对比 318 11.4 串口调试注意事项 318 11.5 常用数据校验法 318 11.5.1 奇偶校验 318...

    linux串口通讯实例

    // filename:stty.h ...//(6) 操作完成后,需要调用函数cleanTTY()来释放申请的串口信息接口; //其中,lockTTY()和unlockTTY()是为了能够在多线程中使用。在读写操作的前后,需要锁定和释放串口资源。

    PC与三菱PLC串口通信 C#实例源程序

    通过C#编写的串口 与三菱PLC 通信 该例程实现了读写三菱PLC的电压、开关量数据

    Visual C++/Turbo C串口通信编程实践及源代码-2

    11.3.2 rs-422与rs-485串行接口标准 315 11.3.3 rs-422与rs-485的网络安装注意要点 317 11.3.4 rs-232、rs422、rs485电气参数对比 318 11.4 串口调试注意事项 318 11.5 常用数据校验法 318 11.5.1 奇偶校验 318...

    RFID 读写器串口

    基于C#的RFID串口界面 方便RFID读写器的测试 界面简单 操作方便

Global site tag (gtag.js) - Google Analytics