当前位置:  编程技术>移动开发

Android开发之串口编程原理和实现方式

    来源: 互联网  发布时间:2014-10-13

    本文导语:  提到串口编程,就不得不提到JNI,不得不提到JavaAPI中的文件描述符类:FileDescriptor。下面我分别对JNI、FileDescriptor以及串口的一些知识点和实现的源码进行分析说明。这里主要是参考了开源项目android-serialport-api。串口编程需要...

提到串口编程,就不得不提到JNI,不得不提到JavaAPI中的文件描述符类:FileDescriptor。下面我分别对JNI、FileDescriptor以及串口的一些知识点和实现的源码进行分析说明。这里主要是参考了开源项目android-serialport-api。

串口编程需要了解的基本知识点:对于串口编程,我们只需对串口进行一系列的设置,然后打开串口,这些操作我们可以参考串口调试助手的源码进行学习。在Java中如果要实现串口的读写功能只需操作文件设备类:FileDescriptor即可,其他的事都由驱动来完成不用多管!当然,你想了解,那就得看驱动代码了。这里并不打算对驱动进行说明,只初略阐述应用层的实现方式。

(一)JNI:
关于JNI的文章网上有很多,不再多做解释,想详细了解的朋友可以查看云中漫步的技术文章,写得很好,分析也很全面,那么在这篇拙文中我强调3点:
1、如何将编译好的SO文件打包到APK中?(方法很简单,直接在工程目录下新建文件夹 libs/armeabi,将SO文件Copy到此目录即可)
2、命名要注意的地方?(在编译好的SO文件中,将文件重命名为:libfilename.so即可。其中filename.so是编译好后生成的文件)
3、MakeFile文件的编写(不用多说,可以直接参考package/apps目录下用到JNI的相关项目写法)
这是关键的代码:
代码如下:

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 with flags 0x%x", path_utf, O_RDWR | flags);
fd = open(path_utf, O_RDWR | flags);
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;
}
}


(二)FileDescritor:
文件描述符类的实例用作与基础机器有关的某种结构的不透明句柄,该结构表示开放文件、开放套接字或者字节的另一个源或接收者。文件描述符的主要实际用途是创建一个包含该结构的FileInputStream 或FileOutputStream。这是API的描述,不太好理解,其实可简单的理解为:FileDescritor就是对一个文件进行读写。
(三)实现串口通信细节
1) 建工程:SerialDemo包名:org.winplus.serial,并在工程目录下新建jni和libs两个文件夹和一个org.winplus.serial.utils,如下图:
2) 新建一个类:SerialPortFinder,添加如下代码:
代码如下:

package org.winplus.serial.utils;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.util.Iterator;
import java.util.Vector;

import android.util.Log;

public class SerialPortFinder {

private static final String TAG = "SerialPort";

private Vector mDrivers = null;

public class Driver {
public Driver(String name, String root) {
mDriverName = name;
mDeviceRoot = root;
}

private String mDriverName;
private String mDeviceRoot;
Vector mDevices = null;

public Vector getDevices() {
if (mDevices == null) {
mDevices = new Vector();
File dev = new File("/dev");
File[] files = dev.listFiles();
int i;
for (i = 0; i < files.length; i++) {
if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) {
Log.d(TAG, "Found new device: " + files[i]);
mDevices.add(files[i]);
}
}
}
return mDevices;
}

public String getName() {
return mDriverName;
}
}

Vector getDrivers() throws IOException {
if (mDrivers == null) {
mDrivers = new Vector();
LineNumberReader r = new LineNumberReader(new FileReader(
"/proc/tty/drivers"));
String l;
while ((l = r.readLine()) != null) {
// Issue 3:
// Since driver name may contain spaces, we do not extract
// driver name with split()
String drivername = l.substring(0, 0x15).trim();
String[] w = l.split(" +");
if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) {
Log.d(TAG, "Found new driver " + drivername + " on "
+ w[w.length - 4]);
mDrivers.add(new Driver(drivername, w[w.length - 4]));
}
}
r.close();
}
return mDrivers;
}

public String[] getAllDevices() {
Vector devices = new Vector();
// Parse each driver
Iterator itdriv;
try {
itdriv = getDrivers().iterator();
while (itdriv.hasNext()) {
Driver driver = itdriv.next();
Iterator itdev = driver.getDevices().iterator();
while (itdev.hasNext()) {
String device = itdev.next().getName();
String value = String.format("%s (%s)", device,
driver.getName());
devices.add(value);
}
}
} catch (IOException e) {
e.printStackTrace();
}
return devices.toArray(new String[devices.size()]);
}

public String[] getAllDevicesPath() {
Vector devices = new Vector();
// Parse each driver
Iterator itdriv;
try {
itdriv = getDrivers().iterator();
while (itdriv.hasNext()) {
Driver driver = itdriv.next();
Iterator itdev = driver.getDevices().iterator();
while (itdev.hasNext()) {
String device = itdev.next().getAbsolutePath();
devices.add(device);
}
}
} catch (IOException e) {
e.printStackTrace();
}
return devices.toArray(new String[devices.size()]);
}
}


上面这个类在“android-serialport-api串口工具测试随笔”中有详细的说明,我就不多说了。
3)新建SerialPort类,这个类主要用来加载SO文件,通过JNI的方式打开关闭串口
代码如下:

package org.winplus.serial.utils;

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, int flags)
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 666 " + device.getAbsolutePath() + "n"
+ "exitn";
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, flags);
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,
int flags);

public native void close();

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


4) 新建一个MyApplication 继承android.app.Application,用来对串口进行初始化和关闭串口
代码如下:

package org.winplus.serial;

import java.io.File;
import java.io.IOException;
import java.security.InvalidParameterException;

import org.winplus.serial.utils.SerialPort;
import org.winplus.serial.utils.SerialPortFinder;

import android.content.SharedPreferences;

public class MyApplication extends android.app.Application {
public SerialPortFinder mSerialPortFinder = new SerialPortFinder();
private SerialPort mSerialPort = null;

public SerialPort getSerialPort() throws SecurityException, IOException, InvalidParameterException {
if (mSerialPort == null) {
/* Read serial port parameters */
SharedPreferences sp = getSharedPreferences("android_serialport_api.sample_preferences", MODE_PRIVATE);
String path = sp.getString("DEVICE", "");
int baudrate = Integer.decode(sp.getString("BAUDRATE", "-1"));

/* Check parameters */
if ( (path.length() == 0) || (baudrate == -1)) {
throw new InvalidParameterException();
}

/* Open the serial port */
mSerialPort = new SerialPort(new File(path), baudrate, 0);
}
return mSerialPort;
}

public void closeSerialPort() {
if (mSerialPort != null) {
mSerialPort.close();
mSerialPort = null;
}
}
}


5) 新建一个继承抽象的Activity类,主要用于读取串口的信息
代码如下:

package org.winplus.serial;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidParameterException;

import org.winplus.serial.utils.SerialPort;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;

public abstract class SerialPortActivity extends Activity {
protected MyApplication mApplication;
protected SerialPort mSerialPort;
protected OutputStream mOutputStream;
private InputStream mInputStream;
private ReadThread mReadThread;

private class ReadThread extends Thread {

@Override
public void run() {
super.run();
while (!isInterrupted()) {
int size;
try {
byte[] buffer = new byte[64];
if (mInputStream == null)
return;

/**
* 这里的read要尤其注意,它会一直等待数据,等到天荒地老,海枯石烂。如果要判断是否接受完成,只有设置结束标识,或作其他特殊的处理。
*/
size = mInputStream.read(buffer);
if (size > 0) {
onDataReceived(buffer, size);
}
} catch (IOException e) {
e.printStackTrace();
return;
}
}
}
}

private void DisplayError(int resourceId) {
AlertDialog.Builder b = new AlertDialog.Builder(this);
b.setTitle("Error");
b.setMessage(resourceId);
b.setPositiveButton("OK", new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
SerialPortActivity.this.finish();
}
});
b.show();
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mApplication = (MyApplication) getApplication();
try {
mSerialPort = mApplication.getSerialPort();
mOutputStream = mSerialPort.getOutputStream();
mInputStream = mSerialPort.getInputStream();

/* Create a receiving thread */
mReadThread = new ReadThread();
mReadThread.start();
} catch (SecurityException e) {
DisplayError(R.string.error_security);
} catch (IOException e) {
DisplayError(R.string.error_unknown);
} catch (InvalidParameterException e) {
DisplayError(R.string.error_configuration);
}
}

protected abstract void onDataReceived(final byte[] buffer, final int size);

@Override
protected void onDestroy() {
if (mReadThread != null)
mReadThread.interrupt();
mApplication.closeSerialPort();
mSerialPort = null;
super.onDestroy();
}
}


6)编写string.xml 以及baudrates.xml文件
在string.xml文件中添加:
代码如下:

Please configure your serial port first.
You do not have read/write permission to the serial port.
The serial port can not be opened for an unknown reason.


在baudrates.xml文件中添加
代码如下:





50
75
110
134
150
200
300
600
1200
1800
2400
4800
9600
19200
38400
57600
115200
230400
460800
500000
576000
921600
1000000
1152000
1500000
2000000
2500000
3000000
3500000
4000000


50
75
110
134
150
200
300
600
1200
1800
2400
4800
9600
19200
38400
57600
115200
230400
460800
500000
576000
921600
1000000
1152000
1500000
2000000
2500000
3000000
3500000
4000000





7)开始编写界面了:在main.xml布局文件中添加两个编辑框,一个用来发送命令,一个用来接收命令:
代码如下:













8) SerialDemoActivity类的实现:
代码如下:

package org.winplus.serial;

import java.io.IOException;

import android.os.Bundle;
import android.view.KeyEvent;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;

public class SerialDemoActivity extends SerialPortActivity{
EditText mReception;

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

// setTitle("Loopback test");
mReception = (EditText) findViewById(R.id.EditTextReception);

EditText Emission = (EditText) findViewById(R.id.EditTextEmission);
Emission.setOnEditorActionListener(new OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
int i;
CharSequence t = v.getText();
char[] text = new char[t.length()];
for (i=0; i

    
 
 

您可能感兴趣的文章:

  • 请问, android+linux内核 平台上, 有一个串口接口的外设置芯片, 想对它进行初化, 一般根据该平台下什么驱动改写?
  • 100分请教高手一个 android串口控制台和i2c超时的问题(本人结贴率达到90%)
  • Android增量升级的方法和原理详细介绍
  • android Setting中隐藏项实现原理与代码
  • android开机自启动原理与实现案例(附源码)
  • android IntentService实现原理及内部代码分享
  • Android裁剪图片为圆形图片的实现原理与代码
  • 解析android 流量监测的实现原理
  • Android系统开发中log的使用方法及简单的原理
  • Android应用图标在状态栏上显示实现原理
  • Android基站定位原理及实现代码
  • Android在listview添加checkbox实现原理与代码
  • android开发之蜂鸣提示音和震动提示的实现原理与参考代码
  • android 键盘事件和屏幕事件的运行原理及交互实现
  • Android仿UC底部菜单栏实现原理与代码
  • android:照片涂画功能实现过程及原理详解
  • Android检测Cursor泄漏的原理以及使用方法
  • android底部菜单栏实现原理与代码
  • android书架效果实现原理与代码
  • Android 进程间通信实现原理分析
  • Android 操作系统获取Root权限 原理详细解析
  • android 大图片拖拽并缩放实现原理
  •  
    本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
    本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。












  • 相关文章推荐
  • Android的OpenGL编程实例 Android-GL
  • android网络编程之android连接网络的简单示例代码
  • Android编程心得分享——JSON学习过程
  • Android 编程下的计时器代码
  • Android 编程下字库的使用及注意事项
  • android的UDP编程实例
  • Android编程中避免内存泄露的方法总结
  • android 网络编程之网络通信几种方式实例分享
  • 申请Android Map 的API Key(v2)的最新申请方式(SHA1密钥)
  • Android瀑布流实例 android_waterfall
  • Android开发需要的几点注意事项总结
  • Android系统自带样式 (android:theme) iis7站长之家
  • android 4.0 托管进程介绍及优先级和回收机制
  • Android网络共享软件 Android Wifi Tether
  • Android访问与手机通讯相关类的介绍
  • Android 图标库 Android GraphView
  • Android及andriod无线网络Wifi开发的几点注意事项
  • 轻量级Android开发工具 Android Tools
  • Android 2.3 下StrictMode介绍
  • Android 开发环境 Android Studio
  • IDEA的Android开发插件 idea-android
  • Android手机事件提醒 Android Notifier
  • XBMC的Android客户端 android-xbmcremote
  • Android小游戏 Android Shapes
  • Android电池监控 Android Battery Dog
  • android开发:“android:WindowTitle”没有对应项no resource
  • Android 上类似IOS 的开关控件。 Android ToggleButton
  • Android 将 android view 的位置设为右下角的解决方法
  • Android 2D游戏引擎 Android Angle




  • 特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3