在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

安卓端的串口通信實踐講解1

jf_78858299 ? 來源:Android技術之家 ? 作者:Android技術之家 ? 2023-04-23 17:59 ? 次閱讀

前言

在上一篇文章中我們講解了關于串口的基礎知識,沒有看過的同學推薦先看一下,否則你可能會不太理解這篇文章所述的某些內容。

這篇文章我們將講解安卓端的串口通信實踐,即如何使用串口通信實現安卓設備與其他設備例如PLC主板之間數據交互。

需要注意的是正如上一篇文章所說的,我目前的條件只允許我使用 ESP32 開發版燒錄 Arduino 程序與安卓真機(小米10U)進行串口通信演示。

準備工作

由于我們需要使用 ESP32 燒錄 Arduino 程序演示安卓端的串口通信,所以在開始之前我們應該先把程序燒錄好。

那么燒錄一個怎樣的程序呢?

很簡單,我這里直接燒了一個 ESP32 使用 9600 的波特率進行串口通信,程序內容就是 ESP32 不斷的向串口發送數據 “e” ,并且監聽串口數據,如果接收到數據 “o” 則打開開發版上自帶的 LED 燈,如果接收到數據 “c” 則關閉這個 LED 燈。

代碼如下:

#define LED 12


void setup() {
  Serial.begin(9600);
  pinMode(LED, OUTPUT);
}


void loop() {
  if (Serial.available()) {
    char c = Serial.read();
    if (c == 'o') {
      digitalWrite(LED, HIGH);
    }
    if (c == 'c') {
      digitalWrite(LED, LOW);
    }
  }


  Serial.write('e');


  delay(100);
}

上面的 12 號 Pin 是這塊開發版的 LED。

使用 Arduino自帶串口監視器測試結果:

圖片

可以看到,確實如我們設想的通過串口不斷的發送字符 “e”,并且在接收到字符 “o” 后點亮了 LED。

安卓實現串口通信

原理概述

眾所周知,安卓其實是基于 Linux操作系統,所以在安卓中對于串口的處理與 Linux 一致。

在 Linux 中串口會被視為一個“設備”,并體現為 /dev/ttys 文件。

/dev/ttys 又被稱為字符終端,例如 ttys0 對應的是 DOS/Windows 系統中的 COM1 串口文件。

通常,我們可以簡單理解,如果我們插入了某個串口設備,則這個設備與 Linux 的通信會由 /dev/ttys 文件進行 “中轉”。

即,如果 Linux 想要發送數據給串口設備,則可以通過往 /dev/ttys 文件中直接寫入要發送的數據來實現,如:

echo test > /dev/ttyS1 這個命令會將 “test” 這串字符發送給串口設備。

如果想讀取串口發送的數據也是一樣的,可以通過讀取 /dev/ttys 文件內容實現。

所以,如果我們在安卓中想要實現串口通信,大概率也會想到直接讀取/寫入這個特殊文件。

android-serialport-api

在上文中我們說到,在安卓中也可以通過與 Linux 一樣的方式--直接讀寫 /dev/ttys 實現串口通信。

但是其實并不需要我們自己去處理讀寫和數據的解析,因為谷歌官方給出了一個解決方案:android-serialport-api

為了便于理解,我們會大致說一下這個解決方案的源碼,但是就不上示例了,至于為什么,同學們往下看就知道了。另外,雖然這個方案歷史比較悠久,也很長時間沒有人維護了,但是并不意味著不能使用了,只是使用條件比較苛刻,當然,我司目前使用的還是這套方案(哈哈哈哈)。

不過這里我們不直接看 android-serialport-api 的源碼,而是通過其他大佬二次封裝的庫來看: Android-SerialPort-API

在這個庫中,通過

// 默認直接初始化,使用8N1(8數據位、無校驗位、1停止位),path為串口路徑(如 /dev/ttys1),baudrate 為波特率
SerialPort serialPort = new SerialPort(path, baudrate);


// 使用可選參數配置初始化,可配置數據位、校驗位、停止位 - 7E2(7數據位、偶校驗、2停止位)
SerialPort serialPort = SerialPort 
    .newBuilder(path, baudrate)
// 校驗位;0:無校驗位(NONE,默認);1:奇校驗位(ODD);2:偶校驗位(EVEN)
//    .parity(2) 
// 數據位,默認8;可選值為5~8
//    .dataBits(7) 
// 停止位,默認1;1:1位停止位;2:2位停止位
//    .stopBits(2) 
    .build();

初始化串口,然后通過:

InputStream in = serialPort.getInputStream();
OutputStream out = serialPort.getOutputStream();

獲取到輸入/輸出流,通過讀取/寫入這兩個流來實現與串口設備的數據通信。

我們首先來看看初始化串口是怎么做的。

圖片

首先檢查了當前是否具有串口文件的讀寫權限,如果沒有則通過 shell 命令更改權限為 666 ,更改后再次檢查是否有權限,如果還是沒有就拋出異常。

注意這里的執行 shell 時使用的 runtime 是 Runtime.getRuntime().exec(sSuPath); 也就是說,它是通過 root 權限來執行這段命令的!

換句話說,如果想要通過這種方式實現串口通信,必須要有 ROOT 權限!這就是我說我不會給出示例的原因,因為我手頭的設備無法 ROOT 啊。至于為啥我司還能繼續使用這種方案的原因也很簡單,因為我們工控機的安卓設備都是定制版的啊,擁有 ROOT 權限不是基本操作?

確定權限可用后通過 open 方法拿到一個類型為 FileDescriptor 的變量 mFd ,最后通過這個 mFd 拿到輸入輸出流。

所以核心在于 open 方法,而 open 方法是一個 native 方法,即 C 代碼:

private native FileDescriptor open(String absolutePath, int baudrate, int dataBits, int parity,
    int stopBits, int flags);

C 的源碼這里就不放了,只需要知道它做的工作就是打開了 /dev/ttys 文件(準確的說是“終端”),然后通過傳遞進去的這些參數去按串口規則解析數據,最后返回一個 javaFileDescriptor 對象。

在 java 中我們再通過這個 FileDescriptor 對象可以拿到輸入/輸出流。

原理說起來是十分的簡單。

看完通信部分的原理后,我們再來看看我們如何查找可用的串口呢?

其實和 Linux 上也一樣:

public Vector<File> getDevices() {
    if (mDevices == null) {
        mDevices = new Vector<File>();
        File dev = new File("/dev");

        File[] files = dev.listFiles();


        if (files != null) {
            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;
}

也是通過直接遍歷 /dev 下的文件,只不過這里做了一些額外的過濾。

或者也可以通過讀取 /proc/tty/drivers 配置文件后過濾:

Vector

關于讀取可用串口設備,其實從這里的路徑也可以看出,都是系統路徑,也就是說,如果沒有權限,大概率也是讀取不到東西的。

這就是使用與 Linux 一樣的方式去讀取串口數據的基本原理,那么問題來了,既然我說這個方法使用條件比較苛刻,那么更易用的替代方案是什么呢?

我們下面就會介紹,那就是使用安卓的 USB host (USB主機)的功能。

USB host

Android 3.1(API 級別 12)或更高版本的平臺直接支持 USB 配件和主機模式。USB 配件模式還作為插件庫向后移植到 Android 2.3.4(API 級別 10)中,以支持更廣泛的設備。設備制造商可以選擇是否在設備的系統映像中添加該插件庫。

在安卓 3.1 版本開始,支持將USB作為主機模式(USB host)使用,而我們如果想要通過 USB 讀取串口數據則需要依賴于這個主機模式。

在正式開始介紹USB主機模式前,我們先簡要介紹一下安卓上支持的USB模式。

安卓上的USB支持三種模式:設備模式、主機模式、配件模式。

設備模式即我們常用的直接將安卓設備連接至電腦上,此時電腦上顯示為 USB 外設,即可以當成 “U盤” 使用拷貝數據,不過現在安卓普遍還支持 MTP模式(作為攝像頭)、文件傳輸模式(即當U盤用)、網卡模式等。

主機模式即將我們的安卓設備作為主機,連接其他外設,此時安卓設備就相當于上面設備模式中的電腦。此時安卓設備可以連接鍵盤、鼠標、U盤以及嵌入式應用USB轉串口、轉I2C等設備。但是如果想要將安卓設備作為主機模式可能需要一條支持 OTG 的數據線或轉接頭。(Micro-USB 或 USB type-c 轉 USB-A 口)

而在 USB 配件模式下,外部 USB 硬件充當 USB 主機。配件示例可能包括機器人控制器、擴展塢、診斷和音樂設備、自助服務終端、讀卡器等等。這樣,不具備主機功能的 Android 設備就能夠與 USB 硬件互動。Android USB 配件必須設計為與 Android 設備兼容,并且必須遵守 Android 配件通信協議。

設備模式與配件模式的區別在于在配件模式下,除了 adb 之外,主機還可以看到其他 USB 功能。

圖片

使用USB主機模式與外設交互數據

在介紹完安卓中的三種USB模式后,下面我們開始介紹如何使用USB主機模式。當然,這里只是大概介紹原生APi的使用方法,我們在實際使用中一般都都是直接使用大佬編寫的第三方庫。

準備工作

在開始正式使用USB主機模式時我們需要先做一些準備工作。

首先我們需要在清單文件(AndroidManifest.xml)中添加:

name="android.hardware.usb.host" />






name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /

一個完整的清單文件示例如下:

<manifest ...>
    <uses-feature android:name="android.hardware.usb.host" />
    <uses-sdk android:minSdkVersion="12" />
    ...
    <application>
        <activity ...>
            ...
            <intent-filter>
                <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
            <span class="hljs-name"intent-filter>
        <span class="hljs-name"activity>
    <span class="hljs-name"application>
<span class="hljs-name"manifest>

聲明好清單文件后,我們就可以查找當前可用的設備信息了:

private fun scanDevice(context: Context) {
    val manager = context.getSystemService(Context.USB_SERVICE) as UsbManager
    val deviceList: HashMap

將 ESP32 開發版插上手機,運行程序,輸出如下:

圖片

可以看到,正確的查找到了我們的 ESP32 開發版。

這里提一下,因為我們的手機只有一個 USB 口,此時已經插上了 ESP32 開發版,所以無法再通過數據線直接連接電腦的 ADB 了,此時我們需要使用無線 ADB,具體怎么使用無線 ADB,請自行搜索。

另外,如果我們想要通過查找到設備后請求連接的方式連接到串口設備的話,還需要額外申請權限。(同理,如果我們直接在清單文件中提前聲明需要連接的設備則不需要額外申請權限,具體可以看看參考資料5,這里不再贅述)

首先聲明一個廣播接收器,用于接收授權結果:

private lateinit var permissionIntent: PendingIntent


private const val ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION"


private val usbReceiver = object : BroadcastReceiver() {


    override fun onReceive(context: Context, intent: Intent) {
        if (ACTION_USB_PERMISSION == intent.action) {
            synchronized(this) {
                val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)


                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                    device?.apply {
                      // 已授權,可以在這里開始請求連接
                        connectDevice(context, device)
                    }
                } else {
                    Log.d(TAG, "permission denied for device $device")
                }
            }
        }
    }
}

聲明好之后在 Acticity 的 OnCreate 中注冊這個廣播接收器:

permissionIntent = PendingIntent.getBroadcast(this, 0, Intent(ACTION_USB_PERMISSION), FLAG_MUTABLE)
val filter = IntentFilter(ACTION_USB_PERMISSION)
registerReceiver(usbReceiver, filter)

最后,在查找到設備后,調用 manager.requestPermission(deviceList.values.first(), permissionIntent) 彈出對話框申請權限。

連接到設備并收發數據

完成上述的準備工作后,我們終于可以連接搜索到的設備并進行數據交互了:

private fun connectDevice(context: Context, device: UsbDevice) {
    val usbManager = context.getSystemService(Context.USB_SERVICE) as UsbManager


    CoroutineScope(Dispatchers.IO).launch {
        device.getInterface(0).also { intf ->
            intf.getEndpoint(0).also { endpoint ->
                usbManager.openDevice(device)?.apply {
                    claimInterface(intf, forceClaim)
                    while (true) {
                        val validLength = bulkTransfer(endpoint, bytes, bytes.size, TIMEOUT)
                        if (validLength > 0) {
                            val result = bytes.copyOfRange(0, validLength)
                            Log.i(TAG, "connectDevice: length = $validLength")
                            Log.i(TAG, "connectDevice: byte = ${result.contentToString()}")
                        }
                        else {
                            Log.i(TAG, "connectDevice: Not recv data!")
                        }
                    }
                }
            }
        }
    }
}

在上面的代碼中,我們使用 usbManager.openDevice 打開了指定的設備,即連接到設備。

然后通過 bulkTransfer 接收數據,它會將接收到的數據寫入緩沖數組 bytes 中,并返回成功接收到的數據長度。

運行程序,連接設備,日志打印如下:

圖片

可以看到,輸出的數據并不是我們預料中的數據。

這是因為這是非常原始的數據,如果我們想要讀取數據,還需要針對不同的串口轉USB芯片或協議編寫驅動程序才能獲取到正確的數據。

順道一提,如果想要將數據寫入串口數據的話可以使用 controlTransfer()

所以,我們在實際生產環境中使用的都是基于此封裝好的第三方庫。

這里推薦使用 usb-serial-for-android

usb-serial-for-android

使用這個庫的第一步當然是導入依賴:

// 添加倉庫
allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}
// 添加依賴
dependencies {
    implementation 'com.github.mik3y:usb-serial-for-android:3.4.6'
}

添加完依賴同樣需要在清單文件中添加相應字段以及處理權限,因為和上述使用原生API一致,所以這里不再贅述。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • plc
    plc
    +關注

    關注

    5011

    文章

    13299

    瀏覽量

    463398
  • 串口通信
    +關注

    關注

    34

    文章

    1626

    瀏覽量

    55530
  • 安卓
    +關注

    關注

    5

    文章

    2130

    瀏覽量

    57207
  • ESP32
    +關注

    關注

    18

    文章

    971

    瀏覽量

    17277
收藏 人收藏

    評論

    相關推薦

    STM32的USART串口通信實踐

    STM32的USART串口通信實踐
    發表于 08-16 07:20

    Matlab的串口通信實

    第9章 Matlab的串口通信實現本章節主要為大家講解Matlab的串口方式波形數據傳輸和后期數據分析功能,非常實用。目錄第9章 Matlab的串口
    發表于 08-17 06:48

    串口通信實驗分享

    實驗四、串口通信實驗? 自律學習堅強 ,拒絕迷茫。作者:行走的皮卡丘時間:2021/4/4喜歡就去追,這個紅燈等不到,說不定下一個紅燈等到了,嘻嘻!!!!!!!文章目錄實驗四、串口通信實
    發表于 01-14 09:08

    與ESP8266串口WIFI模塊的通信實現相關資料推薦

    Button分別對應相應的功能鍵。通信實現原理使用基于tcp協議的socket通信串口WiFi模塊配置成tcp Service模式,然后
    發表于 01-18 08:34

    STM32 USART串口通信實踐

    STM32串口通訊USART串口通信實踐USART串口通信實踐1、實驗環境參考資料 野火官方的
    發表于 02-10 07:06

    基于串口的LABVIEW與PLC的通信實

    基于串口的LABVIEW與PLC的通信實現:介紹使用ADAM5000的通信協議實現LABVIEW與ADAM5510通信
    發表于 09-19 07:55 ?62次下載

    串口通信實例教程

    串口通信實例教程,感興趣的可以看看。
    發表于 06-23 17:56 ?0次下載

    STM32 串口通信實

    USB轉RS485線在F103環境下進行UART通信實驗和RS232通信實驗在F407環境下進行RS485實驗(1.我的103板子沒有485口,2.正好看下開發板環境不同的差異)UART
    發表于 12-20 19:26 ?15次下載
    STM32 <b class='flag-5'>串口</b><b class='flag-5'>通信實</b>驗

    STM32下的USART串口通信程序

    STM32的USART串口通信實踐
    發表于 12-24 18:42 ?13次下載
    STM32下的USART<b class='flag-5'>串口</b><b class='flag-5'>通信</b>程序

    實驗四、串口通信實

    堅強 ,拒絕迷茫。作者:行走的皮卡丘時間:2021/4/4喜歡就去追,這個紅燈等不到,說不定下一個紅燈等到了,嘻嘻!!!!!!!文章目錄實驗四、串口通信實驗一、 實驗目的二、 實驗設備三、 實驗原理3.1、狀態寄存器USART_SR及函數3.2、USATR
    發表于 01-14 10:09 ?2次下載
    實驗四、<b class='flag-5'>串口</b><b class='flag-5'>通信實</b>驗

    串口通信實

    串口通信實串口通信實驗代碼圖像串口通信實驗晶振12MHz,波特率1200, 程序啟動后單片機主
    發表于 01-14 10:12 ?6次下載
    <b class='flag-5'>串口</b><b class='flag-5'>通信實</b>驗

    串口通信入門之modbus(上)

    在之前的兩篇文章中,我們講解串口的基礎知識和在中使用串口通信的方法,如果還沒看過之前文章的
    的頭像 發表于 04-23 17:40 ?1929次閱讀
    <b class='flag-5'>安</b><b class='flag-5'>卓</b>與<b class='flag-5'>串口</b><b class='flag-5'>通信</b>入門之modbus(上)

    串口通信入門之modbus(下)

    在之前的兩篇文章中,我們講解串口的基礎知識和在中使用串口通信的方法,如果還沒看過之前文章的
    的頭像 發表于 04-23 17:40 ?5024次閱讀
    <b class='flag-5'>安</b><b class='flag-5'>卓</b>與<b class='flag-5'>串口</b><b class='flag-5'>通信</b>入門之modbus(下)

    串口通信實踐講解2

    這篇文章我們將講解串口通信實踐,即如何使用串口
    的頭像 發表于 04-23 17:59 ?1700次閱讀
    <b class='flag-5'>安</b><b class='flag-5'>卓</b><b class='flag-5'>端</b>的<b class='flag-5'>串口</b><b class='flag-5'>通信實踐</b><b class='flag-5'>講解</b>2

    串口通信實驗資料分享

    串口通信實驗資料分享
    發表于 06-29 15:03 ?5次下載
    主站蜘蛛池模板: 特一级黄色毛片| 色香婷婷| 亚洲天堂免费| 三级黄色网址| 黄色w站| 午夜爱爱小视频| 七月色婷婷| 国产一级特黄a大片免费| 午夜在线视频观看版| 欧美天天干| 69日本人xxxxxxxx色| 怡红院色视频在线| 全日本爽视频在线| bt天堂网在线www资源| 欧美特黄特色aaa大片免费看| 色吧色吧色吧网| 三级在线国产| 午夜在线影院| 视频一区二区在线| 免费a级毛片出奶水| 亚洲第一区精品日韩在线播放| 激情六月丁香婷婷| 婷婷深爱| 国产美女特级嫩嫩嫩bbb| 日日爱网站| 一区在线播放| 四虎国产视频| www.亚洲天堂.com| 5g成人影院| 美女一区二区三区| 国产精品亚洲精品日韩动图| 国产美女一级视频| 免费三级网站| 色拍拍综合网| 亚洲你xx我xx网站| 新天堂网| 伊人网99| 天天好比| 男女刺激性视频大片| 嫩草影院网站入口| 深夜视频在线免费|