Android Virtual Device与串口调试助手间进行收发数据通信

实现目标:Android Virtual Device与串口调试助手间进行收发数据通信

1、代码分析
AVD串口通信程序主要参考google的开源串口类android-serialport-api,关于串口操作主要有:(1)打开串口;(2)读串口;(3)写串口;(4)关闭串口。在本demo中,只有一个Activity,其中包含了打开串口,读写串口的操作,打开串口等操作用到了JNI,使得Java可以调用C语言写成的库。

1.1 MainActivity.java,主要包含打开串口按钮,返回文件输入输出流,发送和接收按钮,并将接收和发送的内容在EditText显示


package com.charles.uart;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {

private static final String TAG=MainActivity.class.getSimpleName(); //log信息

private Button btOpen;
private Button btSend;
private Button btRec;
private EditText mSend;
private EditText mReception;

SerialPort sp;

FileInputStream mInputStream;
FileOutputStream mOutputStream;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//初始化钮以及文本显示
btOpen= (Button) findViewById(R.id.button);
btSend= (Button) findViewById(R.id.button2);
btRec= (Button) findViewById(R.id.button3);
mSend= (EditText) findViewById(R.id.editText);
mReception= (EditText) findViewById(R.id.editText2);
Log.i(TAG,"钮以及文本显示初始化完成");

//点击打开串口
btOpen.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.i(TAG,"点击了打开按钮");

/*
* 打开串口,返回文件输入输出流
* */
sp=new SerialPort(new File("/dev/ttyS1"),9600);

mInputStream= (FileInputStream) sp.getInputStream();
mOutputStream= (FileOutputStream) sp.getOutputStream();

Toast.makeText(getApplicationContext(),"open",Toast.LENGTH_SHORT).show();

}
});

//点击发送
btSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.i(TAG,"点击了发送钮");

try {
//获取发送数据
String send=mSend.getText().toString();

//将获取的数据发出
mOutputStream.write(send.getBytes());
mOutputStream.write('\\n');
} catch (IOException e) {
e.printStackTrace();
return;
}

Toast.makeText(getApplicationContext(),"send",Toast.LENGTH_SHORT).show();
}
});

//点击接收
btRec.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.i(TAG,"点击了接收钮");

int size;

//存储接收数据
byte[] buffer=new byte[64];

if (mInputStream==null){
Log.i(TAG,"没接收到数据,返回");
return;
}

//如果接收到数据
try {
size=mInputStream.read(buffer); //读取接收数据

if (size>0){
onDataReceived(buffer,size); //如果读取的数据不为空,将读取的数据显示在EditText
}
} catch (IOException e) {
e.printStackTrace();
return;
}
}
});
}

void onDataReceived(final byte[] buffer,final int size){
runOnUiThread(new Runnable() {
@Override
public void run() {
if (mReception!=null){
mReception.append(new String(buffer,0,size));
}
}
});
}
}



1.2 SerialPort.java类,主要调用JNI打开串口,返回文件输入输出流


package com.charles.uart;

import android.util.Log;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

/**
* Created by charles.wang_cp on 2016/11/29.
*/

public class SerialPort {

static {
System.loadLibrary("serialport");
}

private FileInputStream mFileInputStream;
private FileOutputStream mFileOutputStream;

private FileDescriptor mFd;


public SerialPort(File device,int baudrate){

if (!device.canWrite()||!device.canWrite()){
return; //如果节点无读写权限
}

//打开串口,并获取文件输入输出流
mFd=open(device.getAbsolutePath(),baudrate);

mFileInputStream=new FileInputStream(mFd);
mFileOutputStream=new FileOutputStream(mFd);

}


//获取文件输入输出流
public InputStream getInputStream(){return mFileInputStream;}
public OutputStream getOutputStream(){return mFileOutputStream;}

//调用JNI打开串口
private native static FileDescriptor open(String path,int baudrate);
}


1.3 调用JNI打开串口
1.3.1 调用JNI时,请确保NDK已经配置好,在local.properties中有SDK和NDK的配置路径
1.3.2 注意:
SerialPort中添加的native open方法:此处会有Cannot resolve corresponding JNI function java_com_charles_uart_SerialPort_open,可以先不用理会
1.3.3 编译生成字节码文件
Build->Make Project,执行这一步为了验证工程中无其他错误,并生成.class字节码文件,生成的字节码文件位于工程目录下的
build\\intermediates\\classes\\debug文件夹下

1.3.4 使用Javah生成头文件
打开cmd进入工程main目录下:
D:\\AndroidProjet\\HelloJni01\\app\\src\\main>javah -d jni -classpath D:\\Users\\charle
s.wang_cp\\AppData\\Local\\Android\\sdk\\platforms\\android-25\\android.jar;..\\..\\build
\\intermediates\\classes\\debug com.charles.uart.SerialPort

注意:此处的android-25根据工程的compile SDK API来选择,执行此命令后会生成jni文件夹以及对应有包名的头文件.h

1.3.5在jni文件夹下创建jni.c文件,并编辑c源代码,注意要区分创建.c还是.cpp文件,如果创建成.cpp,而编辑的是.c源代码,那么可能编译时会有语法错误,C源代码中的方法来自com_charles_uart_SerialPort.h中的定义

jni.c源代码

//
// Created by charles.wang_cp on 2016/11/29.
//

#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:com_charles_uart_SerialPort
*Method:open
*Signature: (Ljava/lang/String;I)Ljava/io/FileDescriptor
*/

JNIEXPORT jobject JNICALL Java_com_charles_uart_SerialPort_open
(JNIEnv *env, jclass 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;


}



1.3.6 配置gradle.properties文件以及build.gradle文件
首先在gradle.properties中末尾添加:android.useDeprecatedNdk=true
然后在build.gradle中添加如下配置


1.3.7 Build->Rebuild Project
这一步编译代码将生成.so文件,生成路径在\build\intermediates\ndk\lib文件夹下

2、创建AVD
2.1 Tools->Android->AVD Manager->Create Virtual Device 创建Android Virtual Device
注意:Nexus 在Marshmallow模式的system image下,每次Activity启动和退出时,log都会打印出未知错误
如图为创建成功的avd1和avd2

2.2 启动AVD,并且为AVD设置串口COM,本demo中为AVD设置COM2

2.3 获取节点权限,否则将不能对串口进行读写操作
//获取root权限
>adb root
//查看串口节点
>ls -l ttyS*
//修改系统属性
>chown root:root ttyS*
//节点获得读写可执行权限
>chmod 777 ttyS*
//关闭Selinux
>setenforce 0


3、AVD与串口调试助手进行串口通信
3.1 将串口通信程序apk安装到avd2

3.2 打开串口调试助手,并将串口设置为COM1,波特率为9600(串口程序设置为9600)

3.3 AVD点击“打开串口”
3.4 输入发送数据,例如:Zesusis,点击“发送”,在串口调试助手接收端可以看到接收的数据

3.5 点击“接收”,再在串口调试助手端写好要发送的数据,例如:2016,在AVD端可以看到接收的数据

至此,说明Android与串口调试助手间的串口通信调试成功。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值