JNI調用串口

SerialTool.c

#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 Java_com_example_serialtool_SerialPort_open
(JNIEnv *env, jobject thiz, jstring path, jint baudrate)
{
int fd;
speed_t speed;
jobject mFileDescriptor;

/* Check arguments */
{
speed = getBaudrate(baudrate);
if (speed == -1) {
/* TODO: throw an exception */
LOGE("Invalid baudrate");
return NULL;
}
}

/* Opening device */
{
jboolean iscopy;
const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy);
LOGD("Opening serial port %s", path_utf);
fd = open(path_utf, O_RDWR);
LOGD("open() fd = %d", fd);
(*env)->ReleaseStringUTFChars(env, path, path_utf);
if (fd == -1)
{
/* Throw an exception */
LOGE("Cannot open port");
/* TODO: throw an exception */
return NULL;
}
}

/* Configure device */
{
struct termios cfg;
LOGD("Configuring serial port");
if (tcgetattr(fd, &cfg))
{
LOGE("tcgetattr() failed");
close(fd);
/* TODO: throw an exception */
return NULL;
}

cfmakeraw(&cfg);
cfsetispeed(&cfg, speed);
cfsetospeed(&cfg, speed);

if (tcsetattr(fd, TCSANOW, &cfg))
{
LOGE("tcsetattr() failed");
close(fd);
/* TODO: throw an exception */
return NULL;
}
}

/* Create a corresponding file descriptor */
{
jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");
jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "<init>", "()V");
jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");
mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);
(*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd);
}

return mFileDescriptor;
}

/*
* Class: cedric_serial_SerialPort
* Method: close
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_example_serialtool_SerialPort_close
(JNIEnv *env, jobject thiz)
{
jclass SerialPortClass = (*env)->GetObjectClass(env, thiz);
jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor");

jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");
jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I");

jobject mFd = (*env)->GetObjectField(env, thiz, mFdID);
jint descriptor = (*env)->GetIntField(env, mFd, descriptorID);

LOGD("close(fd = %d)", descriptor);
close(descriptor);
}

Android.mk
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := SerialTool
LOCAL_SRC_FILES := SerialTool.c
LOCAL_LDLIBS := -llog

include $(BUILD_SHARED_LIBRARY)

SerialPort.java
package com.example.serialtool;

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;
import android.util.Log;

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 {

/* Check access permission */
if (!device.canRead() || !device.canWrite()) {
try {
/* Missing read/write permission, trying to chmod the file */
Process su;
su = Runtime.getRuntime().exec("/system/bin/su");
String cmd = "chmod 777 " + device.getAbsolutePath() + "\n"
+ "exit\n";
su.getOutputStream().write(cmd.getBytes());
if ((su.waitFor() != 0) || !device.canRead()
|| !device.canWrite()) {
throw new SecurityException();
}
} catch (Exception e) {
e.printStackTrace();
throw new SecurityException();
}
}

mFd = open(device.getAbsolutePath(), baudrate);
if (mFd == null) {
Log.e(TAG, "native open returns null");
throw new IOException();
}
mFileInputStream = new FileInputStream(mFd);
mFileOutputStream = new FileOutputStream(mFd);
}

// Getters and setters
public InputStream getInputStream() {
return mFileInputStream;
}

public OutputStream getOutputStream() {
return mFileOutputStream;
}

// JNI
private native static FileDescriptor open(String path, int baudrate);
public native void close();
static {
System.loadLibrary("SerialTool");
}
}

MainActivity.java
package com.example.serialtool;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.text.format.Formatter;
//import com.huayu.io.SerialPort;
import android.util.Log;

public class MainActivity extends Activity {
private SerialPort mySerialPort = null;
private EditText sendText;
private EditText baudText;
private EditText portText;
private TextView textStatus;
private TextView textRecv;
private boolean openFlag = false;
private Thread mt;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sendText = (EditText)findViewById(R.id.editText1);
baudText = (EditText)findViewById(R.id.editText2);
portText = (EditText)findViewById(R.id.editText3);
sendText.setText("this send text");
baudText.setText("19200");
portText.setText("ttyMT0");
textStatus = (TextView)findViewById(R.id.textView1);
textRecv = (TextView)findViewById(R.id.textView2);
textStatus.setText("this is status");
textRecv.setText("this is received text");
}
protected final Handler mh = new Handler(){
public void handleMessage(Message msg){
/*
if(textRecv.getText().toString().length() > 256)
textRecv.setText("");
textRecv.append(msg.obj.toString());
*/
textRecv.setText(msg.obj.toString());
}
};
private Runnable mThread = new Runnable(){

@Override
public void run() {
openFlag = true;
InputStream instream = mySerialPort.getInputStream();
long fileLen = 0;
long startTime = System.currentTimeMillis();
List<String> allData = new ArrayList<String>();
final long ROWBYTECOUNT = 64;
while(openFlag)
{
int ia = 0;
try {
ia = instream.available();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
ia = 0;
}
if(ia>0)
{
Log.i("thread", "have byte " + ia);
byte[] buf = new byte[1024];
try {
int len = instream.read(buf, 0, ia);
if(len > 0)
{
fileLen += len;
int current=0;
StringBuffer stringBuffer = new StringBuffer();
for(int i=0; i<len; i++)
{
current = current<<1+buf[i];
if((i%32!=0)||(i==len-1)){
stringBuffer.append(Integer.toHexString(current)+" ");
current=0;
}
if(i!=0&&i%ROWBYTECOUNT==0){
allData.add(0,stringBuffer.toString()+"\n");
stringBuffer = new StringBuffer();
}
}
long time = (System.currentTimeMillis()-startTime)/1000;
if(time>0){
if(stringBuffer.length()>60){
allData.remove(allData.size()-1);
}
long dataLen = fileLen/time;
Message msg = mh.obtainMessage();
String firstLine = time+"s "+Formatter.formatFileSize(getApplicationContext(), dataLen)+"\n";
StringBuffer msgStr = new StringBuffer(firstLine);
for(String lin:allData){
msgStr.append(lin);
}
msg.obj =msgStr.toString();
mh.sendMessage(msg);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
};
public void onOpen(View v)
{
textStatus.setText("opening");
if(mySerialPort == null)
{
String devname = "/dev/" + portText.getText().toString();
int baud = Integer.parseInt(baudText.getText().toString()); //115200;//baudText.getText();
try {
mySerialPort = new SerialPort(new File(devname), baud);
mt = new Thread(mThread);
mt.start();
textStatus.setText("open ok");
} catch (SecurityException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
textStatus.setText("open ok");
}
else
{
textStatus.setText("serial port already opened!");
}
}
public void onClose(View v)
{
openFlag = false;
textStatus.setText("closing");
if(mySerialPort != null)
{
try {
mySerialPort.getInputStream().close();
mySerialPort.getOutputStream().close();
} catch (IOException e) {
e.printStackTrace();
}
mySerialPort.close();
mySerialPort = null;
textStatus.setText("close ok");
}
else
{
textStatus.setText("serial port already closed");
}
}
public void onSend(View v)
{
textStatus.setText("onSend");
if(mySerialPort != null)
{
OutputStream outstream = mySerialPort.getOutputStream();
String s = sendText.getText().toString();
try {
outstream.write(s.getBytes());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else
{
textStatus.setText("please do open firstly");
}
}
}



/***************************************************實例2***************************************************/
1. 本地類TtyNativeControl
package com.notioni.uart.manager;
import Java.lang.ref.WeakReference;
import Android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
/**
本地方法類
*/
public class TtyNativeControl {
private final static String TAG = "TtyNativeControl";
static{
System.loadLibrary("uart_ctl");
}
private static final int TTY_MSG_RECEIVE = 1;
private static final int TTY_CLOSE_DEVICE = TTY_MSG_RECEIVE+1;
private EventHandler mEventHandler;
private ReceiveCallback mReceiveCallBack;
TtyNativeControl(){
mReceiveCallBack = null;
Looper looper;
if((looper = Looper.myLooper()) != null){
mEventHandler = new EventHandler(this, looper);
}else if((looper = Looper.getMainLooper()) != null){
mEventHandler = new EventHandler(this, looper);
}else{
mEventHandler = null;
}
native_setup(new WeakReference<TtyNativeControl>(this));
}
/**
打開驅動
@return 是否打開成功
*/
public int openTty(){
return _openTty();
}
/**
關閉驅動,需要一定時間,所以採用Handler機制
*/
public int closeTty(){
// mEventHandler.obtainMessage(TTY_CLOSE_DEVICE).sendToTarget();
// return 1;
return _closeTty();
}
/**
發送數據
@param data
@return
*/
public int sendMsgToTty(byte[] data){
return _sendMsgToTty(data);
}
/**
接收數據
@param callback
*/
public final void receiveMsgFromTty(ReceiveCallback callback){
mReceiveCallBack = callback;
_receiveMsgFromTty();
}
/**
設置串口數據位,校驗位,速率,停止位
@param databits 數據位 取值 78
@param event 校驗類型 取值N ,E, O,
@param speed 速率 取值 2400,4800,9600,115200
@param stopBit 停止位 取值或者 2
*/
public int configTty(int databits,char event,int speed,int stopBit){
return _configTty(databits, event, speed, stopBit);
}
/**
@param mode 是否使用原始模式(Raw Mode)方式來通訊 取值0,1,2 說明:0=nothing,1=Raw mode,2=no raw mode
@param showLog 打印出串口信息Log 取值1,0
*/
public int setMode(int mode ,int showLog){
return _setMode(mode, showLog);
}
/**
接收數據回調接口
*/
public interface ReceiveCallback{
void onReceiveData(byte[] data,TtyNativeControl tty);
}
/****************************************************************
本地方法
*/
private native final void native_setup(Object tty_this);
private native int _openTty();
private native int _closeTty();
private native int _sendMsgToTty(byte[] data);
private native void _receiveMsgFromTty();
private native int _configTty(int databits,char event,int speed,intstopBit);
private native int _setMode(int mode,int showLog);
/*
實現底層回調
*/
private static void postEventFromNative(Object tty_ref,int what ,intarg1,int arg2,Object obj){
Log.i(TAG"[postEventFromNative] what:"+what);
TtyNativeControl t = (TtyNativeControl)((WeakReference)tty_ref).get();
if(t == null)return;
if(t.mEventHandler != null){
Message m = t.mEventHandler.obtainMessage(what, arg1, arg2,obj);
t.mEventHandler.sendMessage(m);
}
}
private class EventHandler extends Handler{
private TtyNativeControl mTty;
public EventHandler(TtyNativeControl t,Looper looper){
super(looper);
mTty = t;
}
@Override
public void handleMessage(Message msg) {
switch(msg.what){
case TTY_MSG_RECEIVE://底層接收數據回調上來的事件
if(mReceiveCallBack != null){
mReceiveCallBack.onReceiveData((byte[])msg.obj,mTty);
}
return;
case TTY_CLOSE_DEVICE://關閉驅動
_closeTty();
break;
}
}
}
}
2. JNI類頭文件
#include <jni.h>
#ifndef _Included_com_notioni_uart_manager_TtyNativeControl
#define _Included_com_notioni_uart_manager_TtyNativeControl
#ifdef __cplusplus
extern "C"{
#endif
/**
* Class com_notioni_uart_TtyNativeControl
* Method
*/
JNIEXPORT static void JNICALL com_notioni_uart_manager_TtyNativeControl_native_setup(JNIEnv *env,jobject clazz,jobject weak_this);
JNIEXPORT static int JNICALL com_notioni_uart_manager_TtyNativeControl__openTty(JNIEnv *env,jobject clazz);
JNIEXPORT static int JNICALL com_notioni_uart_manager_TtyNativeControl__closeTty(JNIEnv *env,jobject clazz);
JNIEXPORT static int JNICALL com_notioni_uart_manager_TtyNativeControl__sendMsgToTty(JNIEnv *env,jobject clazz,jbyteArray data);
JNIEXPORT static void JNICALL com_notioni_uart_manager_TtyNativeControl__receiveMsgFromTty(JNIEnv *env,jobject clazz);
JNIEXPORT static int JNICALL com_notioni_uart_manager_TtyNativeControl__configTty(JNIEnv *env,jobject clazz,int nBits,jchar nEvent,int nSpeed,int nStop);
JNIEXPORT static int JNICALL com_notioni_uart_manager_TtyNativeControl__setMode(JNIEnv *env,jobject clazz,int nMode,int showLog);
//JNIEXPORT int JNICALL com_notioni_uart_manager_TtyNativeControl__setSpeed(JNIEnv *env,jobjectclazz,int speed);
//JNIEXPORT int JNICALL com_notioni_uart_manager_TtyNativeControl__setParity(JNIEnv *env,jobjectclazz,int databits,int stopbits,int parity);
#ifdef __cplusplus
}
#endif
#endif

3. JNI實現類
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <utils/misc.h>
#include <utils/Log.h>
#include <utils/threads.h>
#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <utils/Log.h>
#include <sys/ioctl.h>
#include <termios.h>
#include "com_notioni_uart_manager_TtyNativeControl.h"
using namespace android;
struct fields_t{
jfieldID tty;
jmethodID post_event;
};
static fields_t fields;
JavaVM* g_JavaVM;
#define TTY_DEVICE "/dev/ttyS2"
#define LOG_TAG "TtyNativeControl"
#define RECEIVE_DATA_INDEX (1)
#define POST_EVENT()
static int mTtyfd = -1;
static int mOpen = 0;
/**
* class Listener
*/
class JNIMyObserver{
public:
JNIMyObserver(JNIEnv* env,jobject thiz,jobject weak_thiz);
~JNIMyObserver();
void OnEvent(const char* buffer,int length,int what);
private:
JNIMyObserver();
jclass mClass;
jobject mObject;
Mutex mLock;
};
JNIMyObserver::JNIMyObserver(JNIEnv* env,jobject thiz,jobject weak_thiz){
jclass clazz = env->GetObjectClass(thiz);
if(clazz == NULL){
// jniThrowException(env,"java/lang/Exception",NULL);
LOGE("clazz is null");
return;
}
mClass = (jclass)env->NewGlobalRef(clazz);
mObject = env->NewGlobalRef(weak_thiz);
LOGW("mClass=%d",mClass);
LOGW("mObject=%d",mObject);
}
JNIMyObserver::~JNIMyObserver(){
JNIEnv *env = AndroidRuntime::getJNIEnv();
env->DeleteGlobalRef(mObject);
env->DeleteGlobalRef(mClass);
}
void JNIMyObserver::OnEvent(const char* buffer,int length,int what){
LOGW("OnEvent");
Mutex::Autolock _l(mLock);
if(NULL == g_JavaVM){
LOGE("JNIObserver::Event g_JavaVM is NULL");
return;
}
bool isAttacked = false;
/*
創建env指針
*/
JNIEnv* env;
bool status = (g_JavaVM->GetEnv((void**) &env,JNI_VERSION_1_4) != JNI_OK);
if(status){
g_JavaVM->AttachCurrentThread(&env,NULL);
}
/*
創建Java byte[]數組
*/
jbyteArray obj = NULL;
if(buffer !=NULL && buffer != 0){
const jbyte* data = reinterpret_cast<const jbyte*>(buffer);
obj = env->NewByteArray(length);
env->SetByteArrayRegion(obj,0,length,data);
}
env->CallStaticVoidMethod(mClass,fields.post_event,mObject,what,0,0,obj);
if(obj){
env->DeleteLocalRef(obj);
}
if(isAttacked){
g_JavaVM->DetachCurrentThread();//分離線程
}
}
/**
* class Listener end -----------------
*/
JNIMyObserver* listener;
/*
* setup
*/
static void JNICALL com_notioni_uart_manager_TtyNativeControl_native_setup(JNIEnv *env,jobject clazz,jobject weak_this){
LOGW("com_notioni_uart_manager_TtyNativeControl_native_setup");
env->GetJavaVM(&g_JavaVM);
if(listener != NULL){
delete listener;
}
listener = new JNIMyObserver(env,clazz,weak_this);
}
/*
* _openTty
*/
static int JNICALL com_notioni_uart_manager_TtyNativeControl__openTty(JNIEnv *env,jobject clazz){
LOGW("com_notioni_uart_manager_TtyNativeControl__openTty");
mTtyfd = open(TTY_DEVICE,O_RDWR|O_NONBLOCK);//讀寫方式
if(mTtyfd < 0){
LOGE("mTtyfd open failure");
return -1;
}
if(fcntl(mTtyfd,F_SETFL,0)<0){//恢復串口爲堵塞狀態
LOGE("mTtyfd fcntl failure");
}
// if(isatty(STDIN_FILENO)==0){//測試是否爲中斷設備,非0即使終端設備
// LOGE("standard inputs is not a terminal device");
// }else{
// LOGE("isatty success");
// }
mOpen = 1;
LOGW("open device success");
return 1;
}
/*
* _closeTty
*/
static int JNICALL com_notioni_uart_manager_TtyNativeControl__closeTty(JNIEnv *env,jobject clazz){
LOGW("com_notioni_uart_manager_TtyNativeControl__closeTty");
if(mTtyfd < 0){
LOGE("mTtyfd open failure ,non't close");
return -1;
}
mOpen = 0;
sleep(2);//等待線程退出
int c = close(mTtyfd);
if(c < 0){
LOGE("mTtyfd close failure");
return -1;
}
LOGW("close device success");
return 1;
}
/*
* _sendMsgToTty
*/
static int JNICALL com_notioni_uart_manager_TtyNativeControl__sendMsgToTty(JNIEnv *env,jobject clazz,jbyteArray data){//byte[]
LOGW("com_notioni_uart_manager_TtyNativeControl__sendMsgToTty");
//jbyte * arrayBody = env->GetByteArrayElements(data,0); jsize theArrayLengthJ = env->GetArrayLength(data); BYTE * starter = (BYTE *)arrayBody;
if(mTtyfd < 0){
LOGE("mTtyfd open failure ,non't write");
return -1;
}
jbyte* arrayData = (jbyte*)env->GetByteArrayElements(data,0);
jsize arrayLength = env->GetArrayLength(data);
char* byteData = (char*)arrayData;
int len = (int)arrayLength;
LOGW("write data len:%d",len);
int re = write(mTtyfd,byteData,len);
if(re == -1){
LOGE("write device error");
}
return re;
}
/*
線程Run
*/
voidthreadreadTtyData(void* arg){
LOGW("run read data");
if(!(arg)){
return NULL;
}
char* buf = new char[200];
int result = 0,ret;
fd_set readfd;
struct timeval timeout;
while(mOpen){
timeout.tv_sec = 2;//設定超時秒數
timeout.tv_usec = 0;//設定超時毫秒數
FD_ZERO(&readfd);//清空集合
FD_SET(mTtyfd,&readfd);///* 把要檢測的句柄mTtyfd加入到集合裏 */
ret = select(mTtyfd+1,&readfd,NULL,NULL,&timeout);/* 檢測我們上面設置到集合readfd裏的句柄是否有可讀信息 */
switch(ret){
case -1:/* 這說明select函數出錯 */
result = -1;
LOGE("mTtyfd read failure");
break;
case 0:/* 說明在我們設定的時間值5秒加0毫秒的時間內,mTty的狀態沒有發生變化 */
break;
default:/* 說明等待時間還未到5秒加0毫秒,mTty的狀態發生了變化 */
if(FD_ISSET(mTtyfd,&readfd)){/* 先判斷一下mTty這外被監視的句柄是否真的變成可讀的了 */
int len = read(mTtyfd,buf,sizeof(buf));
/**發送數據**/
if(!(arg))break;
JNIMyObserver *l = static_cast<JNIMyObserver *>(arg);
l->OnEvent(buf,len,RECEIVE_DATA_INDEX);
memset(buf,0,sizeof(buf));
}
break;
}
if(result == -1){
break;
}
}
if(buf != NULL){
delete buf;
buf = NULL;
}
LOGE("stop run!");
return NULL;
}
/*
* _receiveMsgFromTty
*/
static void JNICALL com_notioni_uart_manager_TtyNativeControl__receiveMsgFromTty(JNIEnv *env,jobject clazz){
LOGW("com_notioni_uart_manager_TtyNativeControl__receiveMsgFromTty");
if(mTtyfd < 0){
LOGE("mTtyfd open failure ,non't read");
return ;
}
pthread_t id;
int ret;
ret = pthread_create(&id,NULL,threadreadTtyData,listener);
if(ret != 0){
LOGE("create receiver thread failure ");
}else{
LOGW("create read data thred success");
}
}
/**
設置串口數據,校驗位,速率,停止位
* @param nBits 類型 int數據位 取值 78
* @param nEvent 類型 char 校驗類型 取值N ,E, O,,S
* @param mSpeed 類型 int 速率 取值 2400,4800,9600,115200
* @param mStop 類型 int 停止位 取值或者 2
*/
int set_opt(int nBits,char nEvent,int nSpeed,int nStop){
LOGW("set_opt:nBits=%d,nEvent=%c,nSpeed=%d,nStop=%d",nBits,nEvent,nSpeed,nStop);
struct termios newtio,oldtio;
if(tcgetattr(mTtyfd,&oldtio) != 0){
LOGE("setup serial failure");
return -1;
}
bzero(&newtio,sizeof(newtio));
//c_cflag標誌可以定義CLOCALCREAD,這將確保該程序不被其他端口控制和信號干擾,同時串口驅動將讀取進入的數據。CLOCALCREAD通常總是被是能的
newtio.c_cflag |=CLOCAL|CREAD;
//newtio.c_cflag &=~CSIZE;
switch(nBits){//設置數據位數
case 7:
newtio.c_cflag &=~CSIZE;
newtio.c_cflag |=CS7;
break;
case 8:
newtio.c_cflag &=~CSIZE;
newtio.c_cflag |=CS8;
break;
default:
LOGW("nBits:%d,invalid param",nBits);
break;
}
switch(nEvent){//設置校驗位
case 'O':
newtio.c_cflag |=PARENB;//enable parity checking
newtio.c_cflag |=PARODD;//奇校驗位
newtio.c_iflag |=(INPCK|ISTRIP);
//options.c_iflag |= INPCK;//Disable parity checking
break;
case 'E':
newtio.c_cflag|=PARENB;//
newtio.c_cflag&=~PARODD;//偶校驗位
newtio.c_iflag|=(INPCK|ISTRIP);
//options.c_iflag |= INPCK;//Disable parity checking
break;
case 'N':
newtio.c_cflag &=~PARENB;//清除校驗位
//options.c_iflag &=~INPCK;//Enable parity checking
break;
//case 'S':
// options.c_cflag &= ~PARENB;//清除校驗位
// options.c_cflag &=~CSTOPB;
// options.c_iflag |=INPCK;//Disable parity checking
// break;
default:
LOGW("nEvent:%c,invalid param",nEvent);
break;
}
switch(nSpeed){//設置速率
case 2400:
LOGW("B2400:%d",B2400);
cfsetispeed(&newtio,B2400);
cfsetospeed(&newtio,B2400);
break;
case 4800:
LOGW("B4800:%d",B4800);
cfsetispeed(&newtio,B4800);
cfsetospeed(&newtio,B4800);
break;
case 9600:
LOGW("B9600:%d",B9600);
cfsetispeed(&newtio,B9600);
cfsetospeed(&newtio,B9600);
break;
case 115200:
LOGW("B115200:%d",B115200);
cfsetispeed(&newtio,B115200);
cfsetospeed(&newtio,B115200);
break;
default:
cfsetispeed(&newtio,B9600);
cfsetospeed(&newtio,B9600);
LOGW("nSpeed:%d,invalid param",nSpeed);
break;
}
switch(nStop){//設置停止位
case 1:
newtio.c_cflag &= ~CSTOPB;
break;
case 2:
newtio.c_cflag |= CSTOPB;
break;
default:
LOGW("nStop:%d,invalid param",nStop);
break;
}
newtio.c_cc[VTIME] = 0;//設置等待時間
newtio.c_cc[VMIN] = 0;//設置最小接收字符
tcflush(mTtyfd,TCIFLUSH);
if(tcsetattr(mTtyfd,TCSANOW,&newtio) != 0){
LOGE("options set error");
return -1;
}
return 1;
}
/*
* _configTty
*/
static int JNICALL com_notioni_uart_manager_TtyNativeControl__configTty(JNIEnv *env,jobject clazz,int nBits,jchar nEvent,int nSpeed,int nStop){
LOGW("com_notioni_uart_manager_TtyNativeControl__configTty");
return set_opt(nBits,nEvent,nSpeed,nStop);
}
int set_mode(int nMode,int showLog){
LOGW("set_mode:nMode%d,nshowLog=%d",nMode,showLog);
struct termios options;
if(tcgetattr(mTtyfd,&options) != 0){
LOGE("setup serial failure");
return -1;
}
int result = 1;
if(nMode != 0){
if(nMode ==1){
options.c_lflag &=~(ICANON | ECHO | ECHOE | ISIG);//input
options.c_oflag &=~OPOST;//out put
}else if(nMode == 2){
options.c_lflag |=(ICANON | ECHO | ECHOE | ISIG);//input
options.c_oflag |=OPOST;//out put
}
if(tcsetattr(mTtyfd,TCSANOW,&options) != 0){
LOGE("tcsetattr device fail");
result = -1;
}
}
if(showLog == 1){
LOGI("options c_cflag.CS7:%d,CS8:%d",options.c_cflag & CS7,options.c_cflag & CS8);
LOGI("options c_cflag.PARENB:%d,PARODD:%d",options.c_cflag & PARENB,options.c_cflag & PARODD);
LOGI("options c_iflag.INPCK%d,ISTRIP:%d",options.c_iflag & INPCK,options.c_iflag & ISTRIP);
LOGI("option c_ispeed:%d,c_ospeed:%d",cfgetispeed(&options) ,cfgetospeed(&options));
LOGI("options c_cflag.CSTOPB:%d,",options.c_cflag & CSTOPB);
LOGI("options c_cc.VTIME:%d,VMIN:%d",options.c_cc[VTIME],options.c_cc[VMIN]);
LOGI("options c_cflag.CLOCAL:%d,CREAD:%d",options.c_cflag & CLOCAL,options.c_cflag&CREAD);
LOGI("options c_lflag.ICANON:%d,ECHO:%d,ECHOE:%d,ISIG:%d",options.c_lflag & ICANON,options.c_lflag&ECHO,options.c_lflag&ECHOE,options.c_lflag&ISIG);
LOGI("options c_oflag.OPOST:%d,",options.c_oflag &OPOST);
}
return result;
}
static int JNICALL com_notioni_uart_manager_TtyNativeControl__setMode(JNIEnv *env,jobject clazz,int nMode,int showLog){
LOGW("com_notioni_uart_manager_TtyNativeControl__setMode");
return set_mode(nMode,showLog);
}
static JNINativeMethod method_table[] = {
{"native_setup","(Ljava/lang/Object;)V",(void*)com_notioni_uart_manager_TtyNativeControl_native_setup},
{"_openTty","()I",(void*)com_notioni_uart_manager_TtyNativeControl__openTty},
{"_closeTty","()I",(void*)com_notioni_uart_manager_TtyNativeControl__closeTty},
{"_sendMsgToTty","([B)I",(void*)com_notioni_uart_manager_TtyNativeControl__sendMsgToTty},
{"_receiveMsgFromTty","()V",(void*)com_notioni_uart_manager_TtyNativeControl__receiveMsgFromTty},
{"_configTty","(ICII)I",(void*)com_notioni_uart_manager_TtyNativeControl__configTty},
{"_setMode","(II)I",(void*)com_notioni_uart_manager_TtyNativeControl__setMode},
};
static const char* classPathName="com/notioni/uart/manager/TtyNativeControl";
static int register_com_notioni_uart_manager_TtyNativeControl(JNIEnv *env){
LOGW("register_com_notioni_uart_manager_TtyNativeControl");
jclass clazz;
clazz = env->FindClass(classPathName);
if(clazz == NULL){
return -1;
}
fields.post_event = env->GetStaticMethodID(clazz,"postEventFromNative",
"(Ljava/lang/Object;IIILjava/lang/Object;)V");
if(fields.post_event == NULL){
LOGE("Can't find com/notioni/uart/manager.postEventFromNative");
return -1;
}
returnAndroidRuntime::registerNativeMethods(env,classPathName,method_table,NELEM(method_table));
}
jint JNI_OnLoad(JavaVM* vm,void* reserved){
LOGW("JNI_OnLoad");
JNIEnv* env = NULL;
jint result = -1;
if(vm->GetEnv((void**)&env,JNI_VERSION_1_4) != JNI_OK){
goto bail;
}
LOGW("register mothod");
assert(env != NULL);
if(register_com_notioni_uart_manager_TtyNativeControl(env) < 0){
goto bail;
}
return JNI_VERSION_1_4;
bail:
return result;
}

4. JAVA層封裝本地方法
package com.notioni.uart.manager;
import com.notioni.uart.manager.TtyNativeControl.ReceiveCallback;
/*
單例模式
*/
public class UARTCTLManager {
private TtyNativeControl mTtyControl;
private boolean mIsOpen = false;
private static UARTCTLManager mManager;
private UARTCTLManager(){
mTtyControl = new TtyNativeControl();
}
public static UARTCTLManager getInstance(){
if(mManager == null){
mManager = new UARTCTLManager();
}
return mManager;
}
/*
打開驅動
*/
public int openDevice(){
int o = mTtyControl.openTty();
o=1;
if(o == 1){
mIsOpen = true;
}
return o;
}
/*
關閉驅動
*/
public int closeDevice(){
int c = mTtyControl.closeTty();
if(c == 1){
mIsOpen = false;
}
return c;
}
/*
判斷驅動是否打開
*/
public boolean isOpenDevice(){
return mIsOpen;
}
/*
發送數據
*/
public int sendDataToDevice(byte[] data){
return mTtyControl.sendMsgToTty(data);
}
/*
注入接收數據回調方法
*/
public void receiveDataFromDevice(ReceiveCallback callback){
mTtyControl.receiveMsgFromTty(callback);
}
/**
設置串口數據位,校驗位,速率,停止位
@param databits 數據位 取值 78
@param event 校驗類型 取值N ,E, O,,S
@param speed 速率 取值 2400,4800,9600,115200
@param stopBit 停止位 取值或者 2
*/
public int configDevice(int databits,char event,int speed,int stopBit){
if(!mIsOpen){
return -1;
}
int re = mTtyControl.configTty(databits, event, speed, stopBit);
return re;
}
/**
設置串口通信模式,打印串口信息
@param mode 是否使用原始模式(Raw Mode)方式來通訊 取值0,1,2 說明:0=nothing,1=Raw mode,2=no raw mode
@param showLog 打印出串口信息Log 取值1,0
@return
*/
public int setMode(int mode ,int showLog){
return mTtyControl.setMode(mode, showLog);
}
/**
*
接收數據回調接口,接收驅動送到的數據要實現這個回調接口
*/
public interface ReceiveDataCallBack extends ReceiveCallback{
}
}
5. JAVA調用 Activity
package com.notioni.uart;
import com.notioni.uart.manager.TtyNativeControl;
import com.notioni.uart.manager.UARTCTLManager;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class UARTCTLActivity extends Activity implements View.OnClickListener{
private static final String TAG = "UARTCTLActivity";
private UARTCTLManager mUartctlManager;
private Button mOpenOrCloseBtn;
private Button mSendBtn;
private EditText mSendText;
private TextView mReceiveText;
private ReceiveData mReceiveData;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mUartctlManager = UARTCTLManager.getInstance();
mReceiveData = new ReceiveData();
mOpenOrCloseBtn = (Button)findViewById(R.id.openOrcloseBtn);
mSendBtn = (Button)findViewById(R.id.sendBtn);
mSendText = (EditText)findViewById(R.id.sendMsg);
mReceiveText = (TextView)findViewById(R.id.receiveMsg);
mOpenOrCloseBtn.setOnClickListener(this);
mSendBtn.setOnClickListener(this);
}
@Override
protected void onResume() {
super.onResume();
}
@Override
public void onClick(View v) {
switch(v.getId()){
case R.id.openOrcloseBtn://打開或關閉驅動
Log.i(TAG"isOpen:"+mUartctlManager.isOpenDevice());
if(mUartctlManager.isOpenDevice()){
mUartctlManager.closeDevice();
mOpenOrCloseBtn.setText(R.string.open);
}else{
int i =mUartctlManager.openDevice();
Log.i(TAG"openDevice result:"+i);
mOpenOrCloseBtn.setText(R.string.close);
mUartctlManager.setMode(1, 1);//查看串口配置信息
}
break;
case R.id.sendBtn:
openDevice();
int re = mUartctlManager.sendDataToDevice(getSendText());
Log.i(TAG"send result:"+re);
break;
}
}
/*
打開驅動
*/
private void openDevice(){
if(!mUartctlManager.isOpenDevice()){
mUartctlManager.openDevice();
mUartctlManager.receiveDataFromDevice(mReceiveData);
}
}
/*
關閉驅動
*/
public void closeDevice(){
if(mUartctlManager.isOpenDevice()){
mUartctlManager.closeDevice();
}
}
/*
取出待發送的數據
*/
private byte[] getSendText(){
String st = mSendText.getText().toString()+"\r";
if(st == null || "".equals(st)){
return null;
}
return st.getBytes();
}
/*
接收數據
*/
class ReceiveData implements UARTCTLManager.ReceiveDataCallBack{
@Override
public void onReceiveData(byte[] data, TtyNativeControl tty) {
if(mReceiveText != null && data != null){
Log.w(TAG"[onReceiveData] data:"+data.toString());
mReceiveText.setText(data.toString());
}
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(1,1,0,R.string.config);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
if(item.getItemId() == 1){
Intent intent = new Intent();
intent.setClass(this, ConfigActivity.class);
startActivity(intent);
}
return true;
}
@Override
protected void onPause() {
super.onPause();
// closeDevice();
}
@Override
protected void onDestroy() {
closeDevice();
super.onDestroy();
}
}
6. JAVA配置串口屬性
package com.notioni.uart;
import com.notioni.uart.manager.UARTCTLManager;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
import android.util.Log;
import android.widget.Toast;
public class ConfigActivity extends PreferenceActivityimplementsPreference.OnPreferenceChangeListener{
private static final String TAG = "ConfigActivity";
private static final String KEY_DATABITS = "tty_databits";
private static final String KEY_EVENT = "tty_event";
private static final String KEY_SPEED = "tty_speed";
private static final String KEY_STOPBITS = "tty_stopbits";
ListPreference dataBitsPrefer ;
ListPreference eventPrefer ;
ListPreference speedPrefer ;
ListPreference stopBitsPrefer ;
Object dataBitsValues = null;
Object eventValues = null;
Object speedValues = null;
Object stopbitsValues = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.config_options);
PreferenceScreen prefSet = getPreferenceScreen();
dataBitsPrefer = (ListPreference)prefSet.findPreference(KEY_DATABITS);
eventPrefer = (ListPreference)prefSet.findPreference(KEY_EVENT);
speedPrefer = (ListPreference)prefSet.findPreference(KEY_SPEED);
stopBitsPrefer = (ListPreference)prefSet.findPreference(KEY_STOPBITS);
dataBitsPrefer.setValueIndex(0);
eventPrefer.setValueIndex(0);
speedPrefer.setValueIndex(0);
stopBitsPrefer.setValueIndex(0);
dataBitsPrefer.setOnPreferenceChangeListener(this);
eventPrefer.setOnPreferenceChangeListener(this);
speedPrefer.setOnPreferenceChangeListener(this);
stopBitsPrefer.setOnPreferenceChangeListener(this);
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if(preference == dataBitsPrefer){
dataBitsValues = newValue;
}else if(preference == eventPrefer){
eventValues = newValue;
}else if(preference == speedPrefer){
speedValues = newValue;
}else if(preference == stopBitsPrefer){
stopbitsValues = newValue;
}
Log.i(TAG"newValue:"+newValue);
return false;
}
@Override
protected void onPause() {
super.onPause();
UARTCTLManager mUartctlManager = UARTCTLManager.getInstance();
int databits=7,speed=9600,stopbits=1;//設置默認值
char event='N';//設置默認值
if(dataBitsValues != null){
databits = Integer.parseInt(dataBitsValues.toString());
}
if(eventValues != null){
event = eventValues.toString().charAt(0);
}
if(speedValues != null){
speed = Integer.parseInt(speedValues.toString());
}
if(stopbitsValues != null){
stopbits = Integer.parseInt(stopbitsValues.toString());
}
if(!mUartctlManager.isOpenDevice()){
mUartctlManager.openDevice();
}
Log.e(TAG,"databit="+databits+",event="+event+",speed="+speed+",stopbits="+stopbits);
if(databits == -1 || speed == -1 || stopbits == -1 || event=='Q'){
Toast.makeText(this"有參數沒有設置,不去配置串口!", Toast.LENGTH_LONG).show();
return;
}
mUartctlManager.configDevice(databits, event, speed, stopbits);
mUartctlManager.setMode(0, 1);//查看串口配置信息
// Log.e(TAG, "databit="+databits+",event="+event+",speed="+speed+",stopbits="+stopbits);
}
}

7. config_options.xml
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
android:key="video_call_fallback_setting"
android:title="@string/tty_settings"
android:persistent="false">
<ListPreference
android:key="tty_databits"
android:title="@string/databits"
android:persistent="true"
android:entries="@array/databits_entries"
android:entryValues="@array/databits_values"
android:summary="data bits"/>
<ListPreference
android:key="tty_event"
android:title="@string/event"
android:persistent="true"
android:entries="@array/event_entries"
android:entryValues="@array/event_values"
android:summary="data bits"/>
<ListPreference
android:key="tty_speed"
android:title="@string/speed"
android:persistent="true"
android:entries="@array/speed_entries"
android:entryValues="@array/speed_values"
android:summary="data bits"/>
<ListPreference
android:key="tty_stopbits"
android:title="@string/stopbits"
android:persistent="true"
android:entries="@array/stopbits_entries"
android:entryValues="@array/stopbits_values"
android:summary="data bits"/>
</PreferenceScreen>
8. main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="60dip"
android:orientation="horizontal"
>
<Button
android:id="@+id/openOrcloseBtn"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/open"
/>
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="60dip"
android:orientation="horizontal"
>
<EditText
android:id="@+id/sendMsg"
android:layout_gravity="left"
android:layout_width="200dip"
android:layout_height="50dip"
/>
<Button
android:id="@+id/sendBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/send"
/>
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="100dip"
android:orientation="horizontal"
>
<TextView
android:id="@+id/receiveMsg"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/receiveData" />
</LinearLayout>
</LinearLayout>
9. string.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello World, UARTCTLActivity!</string>
<string name="receiveData">Receive data show!</string>
<string name="app_name">UARTCTL</string>
<string name="open">Open</string>
<string name="close">Close</string>
<string name="send">Send</string>
<string name="config">Config</string>
<string name="tty_settings">Settings</string>
<string name="databits">Data bits</string>
<string name="event">Event</string>
<string name="speed">Speed</string>
<string name="stopbits">Stop bits</string>
<string-array name="databits_entries">
<item>-1</item>
<item>7</item>
<item>8</item>
</string-array>
<string-array name="databits_values">
<item>-1</item>
<item>7</item>
<item>8</item>
</string-array>
<string-array name="event_entries">
<item>Settings</item>
<item>None</item>
<item>Event</item>
<item>Odd</item>
</string-array>
<string-array name="event_values">
<item>Q</item>
<item>N</item>
<item>E</item>
<item>O</item>
</string-array>
<string-array name="speed_entries">
<item>-1</item>
<item>B2400</item>
<item>B4800</item>
<item>B9600</item>
<item>B115200</item>
</string-array>
<string-array name="speed_values">
<item>-1</item>
<item>2400</item>
<item>4800</item>
<item>9600</item>
<item>115200</item>
</string-array>
<string-array name="stopbits_entries">
<item>-1</item>
<item>1</item>
<item>2</item>
</string-array>
<string-array name="stopbits_values">
<item>-1</item>
<item>1</item>
<item>2</item>
</string-array>
</resources>
10. Android.mk
LOCAL_PATH:=$(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES :$(call all-java-files-under,src)
#LOCAL_JAVA_LIBRARIES := uart-ctl
LOCAL_JNI_SHARED_LIBRARIES := uart_ctl #so文件打包到apk
LOCAL_PACKAGE_NAME := UARTCTL
#LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
include $(LOCAL_PATH)/jni/Android.mk
include $(call all-makefiles-under,$(LOCAL_PATH))
11. jni/Android.mk
LOCAL_PATH:=$(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES:= \
com_notioni_uart_manager_TtyNativeControl.cpp
LOCAL_C_INCLUDES := \
$(JNI_H_INCLUDE)
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
libui \
libandroid_runtime
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE := uart_ctl
include $(BUILD_SHARED_LIBRARY)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章