国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android開發之串口編程原理的實現方式

2020-02-21 17:33:08
字體:
來源:轉載
供稿:網友

Android使用直接讀寫串行設備,Internet上已存在開源項目,本文是武林技術頻道小編為大家整理的Android開發之串口編程原理的實現方式,跟著武林技術頻道小編帶你來了解一下吧!

提到串口編程,就不得不提到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"
+ "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, 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布局文件中添加兩個編輯框,一個用來發送命令,一個用來接收命令:

復制代碼 代碼如下:



android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >

android:id="@+id/EditTextReception"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:gravity="top"
android:hint="Reception"
android:isScrollContainer="true"
android:scrollbarStyle="insideOverlay" >



android:id="@+id/EditTextEmission"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="Emission"
android:lines="1" >





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; itext[i] = t.charAt(i);
}
try {
mOutputStream.write(new String(text).getBytes());
mOutputStream.write('/n');
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
});
}

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


寫到這里,代碼基本上寫完了。下面就是要實現JNI層的功能了,要實現JNI,必須首先生成頭文件,頭文件的生成方式也很簡單, 我們編譯工程,在終端輸入 javah org.winplus.serial.utils.SerialPort 則會生成頭文件:org_winplus_serial_utils_SerialPort.h,這個頭文件的名字可以隨意命名。我們將它命名為:SerialPort.h拷貝到新建的目錄jni中,新建SerialPort.c 文件,這兩個文件的代碼就不貼出來了。直接到上傳的代碼中看吧。
(四)串口的應用,可實現掃描頭,指紋識別等外圍USB轉串口的特色應用
還蠻繁瑣的,以上只是對開源項目android-serialport-api 進行精簡想了解此項目請點擊此處!就這樣吧,晚了準備見周公去!

通過武林技術頻道小編介紹的Android開發之串口編程原理的實現方式,相信大家都有了一定的了解,如需了解更多的相關資訊,請繼續關注武林技術頻道吧!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 保康县| 长丰县| 宜君县| 阿瓦提县| 舞钢市| 时尚| 保靖县| 民乐县| 石渠县| 东港市| 宜兰市| 桓台县| 玛纳斯县| 筠连县| 武邑县| 屏南县| 石屏县| 乌拉特中旗| 达孜县| 博白县| 姚安县| 桂平市| 梁平县| 布拖县| 宣恩县| 沁阳市| 蚌埠市| 甘德县| 合江县| 扎兰屯市| 东乌| 罗甸县| 五寨县| 华宁县| 前郭尔| 汉中市| 安康市| 永寿县| 台北市| 泸水县| 乐平市|