android串列埠程式
串列埠程式設計需要了解的基本知識點:對於串列埠程式設計,我們只需對串列埠進行一系列的設定,然後開啟串列埠,這些操作我們可以參考串列埠除錯助手的原始碼進行學習。在Java中如果要實現串列埠的讀寫功能只需操作檔案裝置類:FileDescriptor即可,其他的事都由驅動來完成不用多管!當然,你想了解,那就得看驅動程式碼了。這裡並不打算對驅動進行說明,只初略闡述應用層的實現方式。
(一)JNI:
關於JNI的文章網上有很多,不再多做解釋,想詳細瞭解的朋友可以檢視雲中漫步的技術文章,寫得很好,分析也很全面,那麼在這篇拙文中我強調3點:
1、如何將編譯好的SO檔案打包到APK中?(方法很簡單,直接在工程目錄下新建資料夾 libs/armeabi,將SO檔案Copy到此目錄即可)
2、命名要注意的地方?(在編譯好的SO檔案中,將檔案重新命名為:libfilename.so即可。其中filename.so是編譯好後生成的檔案)
3、MakeFile檔案的編寫(不用多說,可以直接參考package/apps目錄下用到JNI的相關專案寫法)
這是關鍵的程式碼:
- <span style="font-size:18px;"> 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;
- }
- }
- </span>
(二)FileDescritor:
檔案描述符類的例項用作與基礎機器有關的某種結構的不透明控制程式碼,該結構表示開放檔案、開放套接字或者位元組的另一個源或接收者。檔案描述符的主要實際用途是建立一個包含該結構的FileInputStream
或FileOutputStream
。這是API的描述,不太好理解,其實可簡單的理解為:FileDescritor就是對一個檔案進行讀寫。
(三)實現串列埠通訊細節
1) 建工程:SerialDemo包名:org.winplus.serial,並在工程目錄下新建jni和libs兩個資料夾和一個org.winplus.serial.utils,如下圖:
2) 新建一個類:SerialPortFinder,新增如下程式碼:
- <span style="font-size:18px;">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<Driver> mDrivers = null;
- public class Driver {
- public Driver(String name, String root) {
- mDriverName = name;
- mDeviceRoot = root;
- }
- private String mDriverName;
- private String mDeviceRoot;
- Vector<File> mDevices = null;
- public Vector<File> getDevices() {
- if (mDevices == null) {
- mDevices = new Vector<File>();
- 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<Driver> getDrivers() throws IOException {
- if (mDrivers == null) {
- mDrivers = new Vector<Driver>();
- 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<String> devices = new Vector<String>();
- // Parse each driver
- Iterator<Driver> itdriv;
- try {
- itdriv = getDrivers().iterator();
- while (itdriv.hasNext()) {
- Driver driver = itdriv.next();
- Iterator<File> 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<String> devices = new Vector<String>();
- // Parse each driver
- Iterator<Driver> itdriv;
- try {
- itdriv = getDrivers().iterator();
- while (itdriv.hasNext()) {
- Driver driver = itdriv.next();
- Iterator<File> 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()]);
- }
- }
- </span>
上面這個類在“android-serialport-api串列埠工具測試隨筆”中有詳細的說明,我就不多說了。
3)新建SerialPort類,這個類主要用來載入SO檔案,通過JNI的方式開啟關閉串列埠
- <span style="font-size:18px;">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");
- }
- }
- </span>
4) 新建一個MyApplication 繼承android.app.Application,用來對串列埠進行初始化和關閉串列埠
- <span style="font-size:18px;">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 Application 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;
- }
- }
- }
- </span>
5) 新建一個繼承抽象的Activity類,主要用於讀取串列埠的資訊
- <span style="font-size:18px;">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();
- }
- }
- </span>
6)編寫string.xml 以及baudrates.xml檔案
在string.xml檔案中新增:- <span style="font-size:18px;"> <string name="error_configuration">Please configure your serial port first.</string>
- <string name="error_security">You do not have read/write permission to the serial port.</string>
- <string name="error_unknown">The serial port can not be opened for an unknown reason.</string>
- </span>
在baudrates.xml檔案中新增
- <span style="font-size:18px;"><?xml version="1.0" encoding="utf-8"?>
- <resources>
- <string-array name="baudrates_name">
- <item>50</item>
- <item>75</item>
- <item>110</item>
- <item>134</item>
- <item>150</item>
- <item>200</item>
- <item>300</item>
- <item>600</item>
- <item>1200</item>
- <item>1800</item>
- <item>2400</item>
- <item>4800</item>
- <item>9600</item>
- <item>19200</item>
- <item>38400</item>
- <item>57600</item>
- <item>115200</item>
- <item>230400</item>
- <item>460800</item>
- <item>500000</item>
- <item>576000</item>
- <item>921600</item>
- <item>1000000</item>
- <item>1152000</item>
- <item>1500000</item>
- <item>2000000</item>
- <item>2500000</item>
- <item>3000000</item>
- <item>3500000</item>
- <item>4000000</item>
- </string-array>
- <string-array name="baudrates_value">
- <item>50</item>
- <item>75</item>
- <item>110</item>
- <item>134</item>
- <item>150</item>
- <item>200</item>
- <item>300</item>
- <item>600</item>
- <item>1200</item>
- <item>1800</item>
- <item>2400</item>
- <item>4800</item>
- <item>9600</item>
- <item>19200</item>
- <item>38400</item>
- <item>57600</item>
- <item>115200</item>
- <item>230400</item>
- <item>460800</item>
- <item>500000</item>
- <item>576000</item>
- <item>921600</item>
- <item>1000000</item>
- <item>1152000</item>
- <item>1500000</item>
- <item>2000000</item>
- <item>2500000</item>
- <item>3000000</item>
- <item>3500000</item>
- <item>4000000</item>
- </string-array>
- </resources>
- </span>
7)開始編寫介面了:在main.xml佈局檔案中新增兩個編輯框,一個用來傳送命令,一個用來接收命令:
- <span style="font-size:18px;"><?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" >
- <EditText
- 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" >
- </EditText>
- <EditText
- android:id="@+id/EditTextEmission"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:hint="Emission"
- android:lines="1" >
- </EditText>
- </LinearLayout>
- </span>
8) SerialDemoActivity類的實現:
- <span style="font-size:18px;">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<t.length(); i++) {
- text[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));
- }
- }
- });
- }
- }
- </span>
寫到這裡,程式碼基本上寫完了。下面就是要實現JNI層的功能了,要實現JNI,必須首先生成標頭檔案,標頭檔案的生成方式也很簡單, 我們編譯工程,在終端輸入 javah org.winplus.serial.utils.SerialPort 則會生成標頭檔案:org_winplus_serial_utils_SerialPort.h,這個標頭檔案的名字可以隨意命名。我們將它命名為:SerialPort.h拷貝到新建的目錄jni中,新建SerialPort.c 檔案,這兩個檔案的程式碼就不貼出來了。直接到上傳的程式碼中看吧。
(四)串列埠的應用,可實現掃描頭,指紋識別等外圍USB轉串列埠的特色應用。
還蠻繁瑣的,以上只是對開源專案android-serialport-api 進行精簡想了解此專案請點選此處!就這樣吧,晚了準備見周公去!
原創文章,轉載請註明出處:http://blog.csdn.net/tangcheng_ok/article/details/7021470
相關文章
- Android之串列埠程式設計Android串列埠程式設計
- Android 串列埠通訊Android串列埠
- Linux串列埠程式設計Linux串列埠程式設計
- 沒有真實串列埠裝置時使用"虛擬串列埠驅動"除錯你的串列埠程式碼串列埠除錯
- MSM8953 Android 9.0 開啟uart串列埠Android串列埠
- 串列埠blog串列埠
- 串列埠UART串列埠
- 帶內串列埠 在串列埠中輸入命令串列埠
- 串列埠資料抓取及串列埠通訊模擬串列埠
- 你真的瞭解串列埠嗎(示波器串列埠波形分析)串列埠
- 串列埠通訊串列埠
- ROS串列埠程式設計學習筆記ROS串列埠程式設計筆記
- gdbserver連線Ubuntu除錯程式(使用串列埠)ServerUbuntu除錯串列埠
- QT實現串列埠助手中串列埠名的實時更新QT串列埠
- linux 串列埠通訊Linux串列埠
- 【STM32】串列埠串列埠
- 串列埠小工具串列埠
- RHEL6.8程式單印表機串列埠檢查串列埠
- Linux下串列埠監視Linux串列埠
- 11. 串列埠通訊串列埠
- (10)uart串列埠通訊串列埠
- 串列埠通訊型別串列埠型別
- 串列埠通訊協議串列埠協議
- 串列埠,COM口,UART,USART串列埠
- ubuntu繫結串列埠號Ubuntu串列埠
- 虛擬串列埠工具和串列埠除錯工具詳解 - 附下載地址串列埠除錯
- Xamarin.Forms-手機串列埠除錯程式開發文件ORM串列埠除錯
- 迪文屏OS彙編程式碼開發-串列埠篇串列埠
- C# SerialPort 串列埠通訊C#串列埠
- python讀取串列埠 資料Python串列埠
- serial for mac 串列埠除錯工具Mac串列埠除錯
- 串列埠無法正常通訊串列埠
- 串列埠屏開發曲線串列埠
- 串列埠收發UART(Verilog HDL)串列埠
- UART串列埠及Linux實現串列埠Linux
- linux串列埠命令列除錯Linux串列埠命令列除錯
- 串列埠通訊利器:SerialPortStream庫詳解,輕鬆實現C#串列埠開發串列埠C#
- 小型plc串列埠通訊簡介串列埠
- unity3d 讀取串列埠Unity3D串列埠