安卓與串口通信-基礎篇
theme: channing-cyan
前言
安卓並不僅僅只是一個手機操作系統,在很多領域都能見到安卓的身影。
無論是車載系統、工控系統、屏控系統還是物聯網設備基本都有安卓的一席之地。
在所謂的寒冬之下,純粹的安卓開發似乎已經不再吃香,於是越來越多的安卓開發者轉向了車載、工控等領域。
而在這些領域,繞不開串口通信相關知識,無論是車載系統與車身各個傳感器或者 ECU 通信還是工控系統與 PLC 交互,都離不開串口。
説來慚愧,雖然我勉強算是一個做安卓工控的吧,但是實際上乾的活卻是調輪子,調參數,對於其中的基本原理一直是一知半解的。
正好最近有空,我決定先從串口通信學起,慢慢夯實我的基礎知識。
在學習過程中也會把自己學到的東西結合自己工作中積累的為數不多的經驗分享出來。
因為對這方面其實我也不甚瞭解,所以難免會有紕漏,如有錯誤還望各位大佬指正。
本文中的部分圖表來自於文末標註的參考資料
串口基礎知識
串行與並行
如果我們想讓兩台設備(單片機、電腦等)相互通信,一般可以有兩種方式:串行與並行。
例如我們想從設備A中發送一條 8 bit 的數據到設備 B。
如果是串行的方式則會從第1位到第8位依次排隊從一根線(注意,這裏的一根線不是物理意義上的一根線,可以理解成一根完整的數據線)上發送過去。
如果使用並行的方式,則可能設備A和設備B之間有8條線相連接,發送時這 8 bit 數據會同時從8根線上發送到設備B。
直接説可能不太好理解,但是看這張圖就能秒懂:
從上面的圖也不難看出,並行傳輸相比於串行傳輸速度會更快,但是相應的線路成本也更高,而且抗干擾能力也沒有串行強,所以並行的通信距離沒有串行的距離長。
同時,由於串行協議的外設簡單,成本低通信距離遠,所以我們在單片機或者説工控設備之間通常使用的都是串行傳輸。
而串行傳輸又可以分為同步串行和異步串行。
同步串行與異步串行
同步串行指的是通信雙方的時鐘頻率保持一致,接收方時刻準備接收數據,並且只需要在接受到開始傳輸的信號後即可連續傳輸數據,數據之間沒有間隙,不需要起始位和結束位,傳輸效率高,可以進行一對多通信。常用的同步串行有 I2C 和 SPI。
而異步串行指的是雙方時鐘頻率不一致,傳輸數據時採用幀格式傳輸,發送方發送每一幀數據時需要附加起始位和結束位,接收方通過讀取起始位和結束位來實現與發送方的信息同步,傳輸效率低,只能1對1通信。通常一幀數據由起始位、數據位、校驗位、停止位組成。常用的異步串行有 UART ,即我們俗稱的串口通信。
串口通信(UART)
為了實現串口通信,我們需要在設備之間連接三條線:TX(發送)、RX(接收)、GND(接地)。
分別將設備A的TX和設備B的RX相連;設備A的RX和設備B的TX相連;設備A、B的GND相連。
由於每個設備之間都同時有發送和接收端,所以串口通信是全雙工通信,即支持同時發送和接收數據。
假如我們想要由設備A向設備B發送一個字符 “E”,則發送效果如圖:
即通過設備A的發送端(TX)向設備B的接收端(RX)發送一段數據。
此時如果測量發送數據時的這條線上的電平,將得到這麼一個波形圖:
此即二進制的 “01000101” 換算成十進制則為 69 ,在 ASCII 中表示字母 “E” :
而這裏電平中讀到的 0 和 1 是怎麼定義呢?一般來説我們會定義高電平為1,低電平為0,具體怎麼定義下面會講,現在先按下不表。
事實上,這裏的波形圖並不完整,還記得我們上面介紹異步串行時説過的嗎?它是採用幀傳輸的方式來傳輸數據的。也就是説,一個完整的數據幀至少還應該包含前面的起始位(1位低電平)和最後的結束位(1-2位高電平):
另外,一幀數據中數據位可選5-8位,一般來説使用的是 8 位;在數據位之後結束位之前還可以有一個校驗位用於校驗數據傳輸是否正確(奇偶校驗、CRC校驗等),當空閒時數據線輸出高電平,稱為空閒位:
那麼問題來了,接收端怎麼知道使用的數據位是多少位、是否使用校驗、結束位有幾位?
答案是不知道,也無法知道。
所以通常在串口通信開始之前,傳輸雙方需要約定好使用的 數據位、校驗位、停止位。
一般使用時都是 8位數據位、無校驗、1位停止位,即俗稱的 “8N1”。
除了上面説的三個參數外,在開始通信前還需要約定一個參數,那就是“波特率”。
波特率即每秒傳輸多少個bit的數據,單位為 bps(bits per second)。
回到我們上面説的傳輸字母 “E” 的圖上,雖然從圖中可以很直觀的看出電平的變化,但是,接收方應該怎麼知道“多長一段”是一個 bit 呢?這就需要用到我們的波特率了。
例如我們上面設置的波特率是 9600 bps,則表示每秒發送 9600 bit,即一個 bit 寬度為 104 微秒。
此時接收方在讀取到起始位後,會開始每間隔104微秒讀取一次數據,直至讀取到結束位。
(事實上,在讀取到起始位後通常還會延遲 52 微秒才會開始讀取,此後每隔 104 微秒讀取一次,這是為了避免開始讀取時數據不穩定)
自此,串口通信的基本原理就全部講完了。
是不是覺得少了點什麼呢?
對的,上面還留了一個問題,就是我們怎麼去界定高電平和低電平呢?
這就引出了我們的下一個主角 RS232 。
RS232
一般來説,串口通信使用的是 TTL 電平。
TTL 即 Transistor-transistor logic ,晶體管邏輯,其電路由雙極晶體管構成,工作電壓為 5V。所以我們一般將 +5V 電壓認為是高電平(1),0V認為是低電平(0)。但是,TTL電路輸出的高電平最小為 2.4V、低電平最大為0.4V,這就導致高低電平之間的差值最小可能只有2V,所以其傳輸數據時極易受到干擾,例如此時受到靜電干擾,可能就會把低電平變為了高電平。所以此種傳輸方式的距離非常短,一般在1米以內,只適用於芯片和芯片之間或設備與電腦之間的數據傳輸。
為了改善這些缺點,美國電子工業聯盟制定了一個標準:RS232 。
RS232改善的方法非常簡單,就是將電平提高。
為了提高傳輸的電平,我們只需要在原本的串口傳輸設備之間增加一個電平轉換芯片即可。
此時,如果發送設備發送一個 5V 的高電平給轉換芯片,會被提高到 -15V 後發送給接收設備;反之,如果這個芯片接收到 -15V 的高電平,又會將其轉換為 5V 發送給設備。
這就完成了將 TTL 電平轉換為 RS232 電平的操作。
在 RS232 中,高電平(1)為 -3V至-15V;低電平(0)為 +3V至+15V。
RS232 只是改變了電平大小,使得高低電平之間相差了將近 20V ,這樣一來就讓 RS232 的抗干擾能力大大增強,傳輸距離也達到了理論 15 米,同時傳輸數據格式也沒有改變。但是這也導致 RS232 的最大波特率只能使用 19200 bps,使得傳輸速率有限,且抗共模干擾能力依舊沒有改善。
所以,又誕生了 RS485 標準。
RS485
與 RS232 相同的是,RS485也是通過在原本的串口通訊基礎上添加了電平轉換芯片來實現。
不同的是, RS485 不再採用固定的電平高低判斷方式,而是使用差分信號來表示數據。
RS485 接線只需要兩根線A與B。當A與B之間的電壓差在2V到6V之間時表示邏輯1;差值在-6V到-2V之間時表示邏輯0 。
同時 RS485 通常採用雙絞線的形式接線,這就使得它的抗共模干擾能力大大增強,因為當受到干擾時 A和B的電壓都會改變,此時他們的電壓差值就基本不會受到影響。
由於 RS485 使用差分信號的特點,使得它的可傳輸信號最低能達到 200mV、數據最高傳輸率能達到 10Mbps、傳輸距離可以達到1200米。
不過由於 RS485 只使用了兩根線,且兩根線都用於傳輸同一個數據的特性,使得它只支持半雙工通信,即同一時間只能發送或接收數據,不能同時發送與接收數據;但是也因為這個特點,使得 RS485 不同於 RS232 只能兩個設備點對點通信,而是可以一對多通信,即一個主機可以同時與多個從機通信。
總結
自此我們已經對串口通信有了一個基本的認識。
從上文中我們可以發現,UART定義了數據傳輸格式標準,RS232和RS485定義物理層面的數據傳輸方式與接線方式:
也就是説,無論是串口通信還是 RS232 還是 RS485,其實它們的數據格式與內容都是一樣的。
對於我們安卓開發人員來説,無論使用什麼標準幾乎都對我們沒有影響,我們需要做的只是解析好通過串口傳輸過來的數據即可。
如何進行測試
講解完了上述的串口通信原理,那麼我們應該怎麼測試呢?
最好的方式當然是有一個實機能夠測試,但是有時候我們手頭暫時沒有設備或者不方便使用真實設備進行測試,此時我們就可以使用虛擬串口進行模擬測試。
概述
在 Windows 上進行模擬測試相對於 MAC 上簡單的多。
大致流程為:使用軟件創建虛擬串口-啟動安卓模擬器並與創建的虛擬串口連接-完成安卓與虛擬串口的通訊。
由於我目前主力機使用的是 MAC ,所以我嘗試按照上述步驟在 MAC 上進行模擬測試。
但是卻卡死在了第一步,即創建虛擬串口。
我嘗試查找了一番,發現在 MAC 上並沒有可用的虛擬串口軟件,好不容易找到一款,卻已經停止維護,因為新版本的 MAC 權限收緊,無法再創建虛擬串口。
好在,我手頭還有兩塊 ESP32 的開發版,所以其實不使用虛擬串口模擬也行,我可以直接使用 ESP32 開發版進行測試。
那麼,現在問題又來到了第二個步驟。
但是經過我的嘗試,MAC 始終無法在啟動安卓模擬器與串口設備連接。
不過雖然無法使用,但是過程中踩了一些坑,也找到了解決方案,所以這裏還是大致講一下。
MAC 啟動模擬器與串口連接
首先,我們需要創建好安卓模擬器,這一步直接在 Android Studio 中按照常規方式創建就可以了
創建好模擬器後,我們不能直接在 Android Studio 中啟動,而是必須在終端中啟動,因為我們需要指定啟動參數。
啟動需要執行 emulator
命令,這個命令的可執行為文件在 AndroidSdk/tools
中,例如我的路徑是 /Users/equationl/Library/Android/sdk/tools
。如果你不知道你的 AndroidSdk 目錄在哪兒,你可以直接在 Android Studio 的設置中搜索 “sdk” 後,在 “Android SDK Location” 選項中看到這個路徑:
找到你的路徑後執行以下命令即可啟動模擬器:
```shell cd /Users/equationl/Library/Android/sdk/tools
./emulator -avd Pixel_6_Pro_API_31 ```
其中的 “Pixel_6_Pro_API_31” 就是你之前創建的模擬器的名稱。
當然,這裏只是單純的啟動了模擬器,並沒有連接到串口,所以此時你啟動的模擬器是找不到你插入設備的串口的。
如果我們需要連接串口,則需要執行:
./emulator -avd Pixel_6_Pro_API_31 -qemu -serial /dev/tty.wchusbserial544C0047221
其中的 /dev/tty.wchusbserial544C0047221
即為你的串口路徑。
在 MAC 上查看可用串口的命令為:
ls /dev/tty.*
然而,如果你的 MAC 是 ARM CPU的話,大概率啟動模擬器時會報錯:
這應該是谷歌的一個小 BUG,從錯誤信息也能看出,錯誤的原因是不存在 darwin-x86_64/qemu-system-aarch64
這個文件。
我們來到報錯的路徑,可以看到,壓根沒有叫 darwin-x86_64
的文件夾,但是有一個叫 darwin-aarch64
的文件夾,裏面就有 qemu-system-aarch64
文件。
哈哈哈,其實到這裏估計已經有人反應過來了,我們明明是 ARM 的處理器,怎麼會有 x86 文件夾嘛。
所以我們直接把 darwin-aarch64
軟連接到 darwin-x86_64
就行了。
執行命令:
```shell cd /Users/equationl/Library/Android/sdk/emulator/qemu
ln -s darwin-aarch64 darwin-x86_64 ```
然後再返回 tools 目錄,執行啟動模擬器的命令,可以看到,啟動成功了。
但是,連接串口失敗,並報錯:
可以看到,有兩個錯誤,一個是無法識別 serial
命令;另一個是無法連接到我們指定的串口。
關於這個錯誤,我也是搜索了好久,也嘗試了各種方法,但是都無法解決。
不過我發現另外一種實現方法,那就是使用單獨的虛擬機開啟一個安卓的模擬器,然後由這個虛擬機創建一個虛擬串口給安卓模擬器使用,並將這個虛擬串口連接至物理設備。
啊這?擱這套娃呢?太麻煩了!果斷放棄!
感興趣的可以去嘗試一下,教程地址:【Android】mac下Android的串口及NDK調試探索
總結
因為上面的原因,最終我選擇的測試方案是 使用安卓真機+ESP32開發版進行測試。
對於同樣是使用 MAC 的用户,我的建議是直接使用真機+ESP32開發版,或者其他更便宜的開發版進行測試學習,畢竟一塊開發版也就十幾塊錢,真沒必要為了省這十幾塊錢而去折騰這個神坑的無底洞。
而對於 Windows 用户就方便多了,不僅可以使用和 MAC 一樣的真機測試方案,還可以很方便的使用虛擬串口以及安卓模擬器進行測試。
總結
我們今天主要講解了串口的一些基本知識,並且大概介紹了應該怎麼去測試串口通信。
所有知識點都是循序漸進的,設備間的通信有串行通信和並行通信,一般我們在實際中使用的是串行比較多,而串行分為同步串行與異步串行,常用的是異步串行,異步串行常用協議是 UART 即常説的串口,為了解決串口通信的問題,又衍生出了 RS232、RS485等協議。
參考資料
- How can I launch Android Emulator without android studio on Mac M1
- 5分鐘看懂!串口RS232 RS485最本質的區別!
- 串行通信接口之RS-232、RS-485與RS-422
- 3分鐘理解通信協議之串口UART到底是個啥?
- 超簡單的一種通信,2分鐘搞懂,串口通訊的工作原理!
- UART協議詳解
- UART接口的基礎知識詳解
- 串口通信(UART)介紹
- 什麼是串口通訊?
- 串口通訊詳解
- 串口通訊詳解
本文正在參加「金石計劃 . 瓜分6萬現金大獎」
- 安卓與串口通信-校驗篇
- 安卓與串口通信-實踐篇
- 為 Kotlin 的函數添加作用域限制(以 Compose 為例)
- 安卓與串口通信-基礎篇
- Compose For Desktop 實踐:使用 Compose-jb 做一個時間水印助手
- 初探 Compose for Wear OS:實現一個簡易選擇APP
- Compose太香了,不想再寫傳統 xml View?教你如何在已有View項目中混合使用Compose
- 在安卓中壓縮GIF的幾種方法(附實例代碼)
- 魔改車鑰匙實現遠程控車:(1)整體思路及控制方案實現
- 跟我一起使用 compose 做一個跨平台的黑白棋遊戲(3)狀態與遊戲控制邏輯
- 跟我一起使用 compose 做一個跨平台的黑白棋遊戲(2)界面佈局
- 以不同的形式在安卓中創建GIF動圖
- 跟我一起使用 compose 做一個跨平台的黑白棋遊戲(4)移植到compose-jb實現跨平台
- 羨慕大勞星空頂?不如跟我一起使用 Jetpack compose 繪製一個星空背景(帶流星動畫)
- 魔改車鑰匙實現遠程控車:(4)基於compose和經典藍牙編寫一個控制APP