「在 SCT 工作時,第一次用 Python 控制硬體設備,才發現程式碼和硬體之間需要一種「共同語言」——通訊協定。不需要懂電路,只要知道資料是如何被打包和傳送的,就能寫出穩定的通訊程式。」
UART 和 I2C 是嵌入式系統最基礎的兩種通訊協定。這篇從軟體工程師的視角切入, 不講電氣規格,而是聚焦在「如何用 Python 與硬體溝通」、「兩種協定的使用場景差異」、 以及「實際工作中最常遇到的排查問題」。
1. 為什麼軟體工程師要懂通訊協定
隨著 Raspberry Pi 和 Arduino 普及,軟體工程師越來越常需要寫程式控制硬體。 了解通訊協定的基礎概念,讓你能夠快速排查問題、選對工具。
Raspberry Pi 控制感測器
溫濕度、距離、加速度感測器大多透過 UART 或 I2C 介面連接, Python 幾行程式就能讀取數據。
測試儀器 UART Console
嵌入式設備的 debug console 幾乎都走 UART, 透過 pyserial 可以自動化擷取設備 log 與發送指令。
I2C 多感測器整合
一條 I2C bus 可同時連接多個感測器,用地址區分, 特別適合 IoT 設備中需要整合多種感測器的場景。
2. UART — 點對點非同步通訊
一句話理解 UART
「兩條線,Tx 發、Rx 收,點對點非同步通訊。雙方不需要共用時鐘,但必須事先約定好相同的 Baud rate。」
| 參數 | 常用值 | 說明 |
|---|---|---|
| Baud rate | 9600 / 115200 bps | 每秒傳輸的符號數,雙方必須一致,否則資料亂碼 |
| Data bits | 8 bits(幾乎固定) | 每次傳輸的資料位元數 |
| Stop bits | 1 bit | 標記每個資料框的結束,常見值為 1 或 2 |
| Parity | None(最常見) | 奇偶校驗,用於基礎錯誤檢測,多數場景設 None |
| Flow control | None / RTS-CTS | 流量控制,簡單場景不需要 |
連接方式:交叉接線(Cross-connection)
⚠ Tx 接 Rx,不能 Tx 接 Tx(常見接錯)
Python 的 pyserial 套件封裝了 UART 操作, 安裝後幾行程式碼就能與設備通訊:
UART 最常見的問題
• Baud rate 不一致:資料亂碼(出現奇怪字元)的第一個排查點,確認雙方設定完全相同。
• Tx 接 Tx(接錯線):完全收不到資料,記住要交叉接——A 的 Tx 接 B 的 Rx。
• 權限問題(Linux):Permission denied: '/dev/ttyUSB0',執行 sudo usermod -aG dialout $USER 後重新登入。
• 找不到設備:執行 ls /dev/tty*,USB 轉 UART 通常是 /dev/ttyUSB0,板載是 /dev/ttyAMA0。
3. I2C — 一主多從匯流排
一句話理解 I2C
「兩條線(SDA 資料 + SCL 時鐘),一個主設備(Master)可以連接最多 127 個從設備(Slave),用 7-bit 地址區分每個設備。」
I2C 匯流排拓撲
| I2C 地址 | 設備 | 用途 |
|---|---|---|
| 0x3C / 0x3D | SSD1306 OLED 顯示器 | 小型顯示模組,0x3C 最常見 |
| 0x48 | ADS1115 ADC | 類比轉數位,讀取類比感測器 |
| 0x68 / 0x69 | MPU-6050 / MPU-6500 | 六軸陀螺儀 + 加速度計 |
| 0x76 / 0x77 | BME280 | 溫度 + 濕度 + 氣壓三合一感測器 |
| 0x27 / 0x3F | PCF8574 LCD 背包 | I2C 轉 LCD 1602 介面 |
Python 的 smbus2 套件封裝了 I2C 操作, 透過地址與暫存器編號讀寫設備:
掃描所有 I2C 設備:i2cdetect
在 Raspberry Pi 上可以用指令掃描 bus 上所有設備的地址:
$ i2cdetect -y 1
輸出會顯示一個地址表,有設備的格子會顯示 hex 地址(如 3c),空的格子顯示 --。
如果沒有安裝:sudo apt install i2c-tools
4. UART vs I2C 比較
兩種協定都是兩條線,但設計目的完全不同:UART 是簡單的點對點串列通訊,I2C 是可以連接多個設備的匯流排通訊。
| 面向 | UART | I2C |
|---|---|---|
| 線數 | 2 條(Tx / Rx) | 2 條(SDA / SCL) |
| 通訊模式 | 非同步(Asynchronous) | 同步(Synchronous,有時鐘線) |
| 設備數量 | 點對點(1 對 1) | 1 Master 最多 127 個 Slave |
| 速度 | 最高 ~1 Mbps(常用 115200 bps) | 100 Kbps(Standard)/ 400 Kbps(Fast)/ 1 Mbps(Fast+) |
| 定址 | 不需要(直接通訊) | 7-bit 地址區分設備 |
| 適用場景 | GPS 模組、藍牙模組、系統 debug console | 溫濕度感測器、EEPROM、顯示器、IMU |
| Python 套件 | pyserial | smbus2 |
5. 軟體工程師最常遇到的問題
以下是實際工作中最常遇到的坑,每一個都可能讓你卡一個小時,但解法其實很簡單。
找不到設備 / 無法開啟串口
• UART:執行 ls /dev/tty* 確認設備檔案是否存在。USB 轉 UART 晶片通常是 /dev/ttyUSB0,Raspberry Pi 板載 UART 是 /dev/ttyAMA0(或 /dev/serial0)。
• I2C:執行 i2cdetect -y 1 掃描 bus,確認設備地址顯示在輸出表格中。如果什麼都沒有,先檢查接線是否正確(特別是 SDA / SCL 有沒有接反)。
• 也可以用 dmesg | grep tty 或 dmesg | grep i2c 查看 kernel 識別設備的日誌。
Linux 權限問題
• UART 權限:Permission denied: '/dev/ttyUSB0',需要把用戶加入 dialout 群組:
$ sudo usermod -aG dialout $USER
執行後需要登出再重新登入才生效。如果急著測試,可以用 sudo 暫時執行。
• I2C 權限:同樣的方式,加入 i2c 群組:sudo usermod -aG i2c $USER
資料亂碼(UART 特有)
• 症狀:readline() 回傳奇怪的字元或亂碼。
• 第一步:確認雙方的 Baud rate 設定完全相同。115200 和 9600 都是常見值,兩邊任何一邊設錯都會亂碼。
• 第二步:確認 Data bits(8)、Stop bits(1)、Parity(None)設定一致。
• 第三步:確認接線是交叉接(A 的 Tx 接 B 的 Rx),不是平行接。
6. 面試常考題 Q&A
嵌入式或系統相關職位常問的通訊協定問題,附上軟體工程師視角的完整答案。
UART 和 I2C 的主要差異?
UART 是點對點的非同步通訊,不需要時鐘線,雙方約定相同的 Baud rate 後各自用內部時鐘計時,適合一對一的簡單通訊(GPS、藍牙模組)。I2C 是同步的多從設備匯流排,有一條共用時鐘線(SCL)讓所有設備同步,用 7-bit 地址區分設備,一條 bus 最多可連 127 個從設備,適合連接多種感測器(溫濕度、IMU、OLED)的場景。
I2C 如何區分連接在同一條 bus 上的多個設備?
每個 I2C 設備出廠時都有一個固定的 7-bit 地址(0x00–0x7F),Master 發起通訊時會先傳送目標 Slave 的地址,只有地址匹配的 Slave 才會回應,其他設備忽略。部分設備(如 BME280 可以是 0x76 或 0x77)提供地址選擇引腳,讓同型號的兩個設備可以共存在同一條 bus 上。
如何用 Python 讀取 UART 設備的資料?
用 pyserial 套件:先 import serial,然後用 serial.Serial() 建立連線,傳入 port(如 "/dev/ttyUSB0")、baudrate(如 115200)、timeout 等參數;用 ser.write(b"command\n") 發送指令;用 ser.readline() 讀取一行回應並 decode("utf-8") 轉成字串;完成後呼叫 ser.close()。要注意發送的資料必須是 bytes(用 b"" 前綴),讀回來的也是 bytes,需要 decode 才能當字串處理。
上一篇
EP.02 — Shell Script 自動化
Bash 語法、變數、迴圈、實用腳本範例
下一篇
EP.04 — Coming Soon
下一篇文章準備中...