National Central University
Uedu Main Site
Explore Uedu
Student Console
Register as Member/Login
Research Informed Consent Center
Survey Center
Educational Big Data Challenge
Claim or create a course
Try an AI Scenario Task
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

3

Local AI

--

Uedu Code

--

中央大學 AQI 80 24°C PM2.5 21
AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

Unit 10

通訊方式:UART、I2C、SPI

三種常見通訊協定的接線與差別
Interaction 25 minute(s)

Learning Objectives

  • 說明 UART、I2C、SPI 各需要幾條線、怎麼接,以及適合哪些模組
  • 讀懂 UART 的訊框:起始位元、8 個資料位元(最低位元先)、停止位元
  • 說明 I2C 的位址、讀寫位元與 ACK,用 i2c.scan() 找裝置、讀暫存器
  • 說明 SPI 的片選與全雙工,依需求選擇協定

開發板和模組怎麼說話

LED、按鈕只要一支腳位就能表達「開或關」。溫溼度感測器、OLED 螢幕、GPS 模組要傳的是一串數字, 如果每個位元都用一支腳位,8 個位元就要 8 條線,腳位很快就不夠用。

所以模組大多用序列通訊(serial communication):在同一條線上,一個位元接著一個位元地傳。 雙方只要事先約好規則(通訊協定),就知道每個高低電位代表什麼。

最常見的三種協定:

UART I2C SPI
訊號線 TX、RX(加 GND) SDA 資料、SCL 時脈(加 GND) SCLK、MOSI、MISO、每個裝置一條 CS
時脈 沒有,雙方事先約好速度 控制器提供 控制器提供
一次連幾個裝置 一對一 一條匯流排掛很多個,用位址區分 很多個,用 CS 選
速度(常見) 9600~115200 bps 100 kHz、400 kHz 數 MHz 以上
常見的模組 GPS、藍牙序列模組、USB 轉序列 OLED、溫溼度與加速度感測器 SD 卡、彩色顯示器

同步與非同步的差別在有沒有時脈線:I2C、SPI 由控制器送時脈,告訴對方「現在讀這一位」; UART 沒有時脈,雙方靠事先約好的速度各自計時。

接線前確認電壓:ESP32、Pico 是 3.3V,有些模組是 5V。5V 的訊號直接接到 3.3V 的腳位可能損壞開發板,需要電位轉換(level shifter)。

UART:約好速度,各自計時

UART 的接線是交叉的:我的 TX(傳送)接對方的 RX(接收),我的 RX 接對方的 TX,再把兩邊的 GND 接在一起。

雙方要先約好鮑率(baud rate,每秒幾個位元),常見 9600 或 115200。最常用的格式叫 8N1:

閒置(1) │起始(0)│D0│D1│D2│D3│D4│D5│D6│D7│停止(1)│ 閒置(1)
         └ 拉低代表開始   └ 資料從最低位元開始送 ┘
  • 閒置時線路是高電位;起始位元把線拉低,接收端從這一刻開始計時
  • 接著 8 個資料位元,最低位元(D0)先送
  • 最後一個停止位元回到高電位

一個位元組共 10 個位元。9600 bps 時每個位元約 104 微秒,一個位元組約 1.04 ms。 雙方鮑率不一樣,接收端會在錯的時間點讀,收到一堆亂碼。

你在電腦上看到 print() 的輸出,很多開發板也是經過一顆 USB 轉 UART 的晶片送過來的。

逐步看:用 UART 向感測模組要資料

ESP32 的 UART2 接到一個序列輸出的感測模組(示意):送出 READ 之後,模組回覆兩行文字。 注意 uart.readline() 讀到的是 bytes,要 decode() 才是字串。

I2C:一條匯流排,用位址點名

I2C 只要兩條線:SDA(資料)與 SCL(時脈),所有裝置並聯在同一組線上,形成一條匯流排(bus)。 每個裝置有一個 7 位元的位址,例如 OLED 常見 0x3C、TMP102 溫度感測器是 0x48。

兩條線都要接上拉電阻(很多模組已內建),閒置時是高電位;裝置只會把線拉低或放開,不會主動推高, 所以多個裝置接在一起也不會互相短路。

一次讀取的過程:

  1. 起始條件:SCL 是高電位時,控制器把 SDA 拉低
  2. 送 7 位元位址(最高位元先)+ 1 個讀寫位元(0 寫、1 讀)
  3. 第 9 個時脈:有這個位址的裝置把 SDA 拉低,回應 ACK;沒有人回應就是 NACK
  4. 傳資料,每 8 個位元之後都有一個 ACK/NACK
  5. 停止條件:SCL 是高電位時,SDA 由低變高

感測器裡的資料放在一格一格的暫存器(register)裡,i2c.readfrom_mem(位址, 暫存器, 位元組數) 就是「向某個裝置的某個暫存器讀幾個位元組」。 同一條匯流排上的位址不能重複;兩個相同位址的模組,要用模組上的跳線改位址,或接到另一組 I2C。

逐步看:掃描 I2C、讀溫度暫存器

匯流排上接了一個 OLED(0x3C)和一個 TMP102 類的溫度感測器(0x48)。 溫度暫存器有 2 個位元組:前 8 位元加上第二個位元組的前 4 位元,組成 12 位元的數值,每一單位是 0.0625°C。

SPI:多幾條線,換來速度

SPI 用四種訊號:

  • SCLK:控制器送出的時脈
  • MOSI:控制器送給裝置的資料(Master Out Slave In)
  • MISO:裝置送回控制器的資料(Master In Slave Out)
  • CS(片選):每個裝置一條,拉低代表選中,沒被選中的裝置不理會時脈,也不驅動 MISO

每一個時脈,MOSI 與 MISO 同時各傳一個位元,送 8 個時脈就同時「送出一個位元組、收到一個位元組」,這叫全雙工。 SPI 沒有位址與 ACK,時脈可以到數 MHz 以上,適合 SD 卡、彩色顯示器這類要傳大量資料的裝置;代價是線比較多,每多一個裝置就多一條 CS。

時脈閒置是高或低(CPOL)、在哪個邊緣取樣(CPHA)組合成模式 0~3,要和裝置的規格一致;最常見的是模式 0。

from machine import Pin, SPI

spi = SPI(1, baudrate=1000000, polarity=0, phase=0,
          sck=Pin(18), mosi=Pin(23), miso=Pin(19))
cs = Pin(5, Pin.OUT, value=1)     # 平常是高電位:沒選中

cs.value(0)                       # 選中裝置
rx = bytearray(1)
spi.write_readinto(b'\x9f', rx)   # 送出一個位元組,同時收一個位元組
cs.value(1)                       # 結束

(Uedu 的模擬器目前沒有模擬 SPI,這段程式只看不執行;下面的邏輯分析儀可以看到 SPI 的波形。)

邏輯分析儀:逐位元看 UART、I2C、SPI

邏輯分析儀把線路上的高低電位畫成波形。上面選協定與要傳的位元組,按「下一個位元」一格一格看,下方會說明這一格的意思。依序完成任務。

互動練習:I2C 溫度顯示在 OLED

同一組 I2C(SCL 接 GPIO22、SDA 接 GPIO21)上掛了溫度感測器(0x48)與 OLED(0x3C)。依序完成任務:

  1. 直接執行起始程式,看 scan() 找到哪些位址。
  2. 在 # TODO 的地方寫一個迴圈:每 0.5 秒讀一次溫度,在 OLED 第一行顯示 Temp: 25.5 C 這樣的文字。
  3. 把輸入設定裡溫度感測器的「結束」拉到 30°C 以上,再執行一次,看 OLED 跟著更新。
  4. 回答一個問題。

提示:顯示新內容前先 oled.fill(0) 清畫面,最後 oled.show() 才會真的顯示。

小測驗:UART

1.

ESP32 用 UART 接一個 GPS 模組,下列哪一種接法正確?

2.

下面的程式列出字母 A 從 D0 到 D7 的位元(UART 傳送的順序),會印出什麼?

v = ord("A")
bits = [(v >> k) & 1 for k in range(8)]
print(v, bits)
3.

9600 bps、8N1 的 UART 傳一個位元組需要多久?

baud = 9600
print(round(10 / baud * 1000, 2), "ms")
第 1 次作答

小測驗:I2C 與 SPI

1.

兩個 OLED 模組的位址都是 0x3C,接在同一組 I2C 上會怎樣?

2.

溫度感測器回傳兩個位元組 0x19、0x40,下面的程式會印出什麼?

data = bytes([0x19, 0x40])
raw = (data[0] << 4) | (data[1] >> 4)
print(raw, raw * 0.0625)
3.

要接一張 SD 卡存大量感測資料,通常用哪一種介面?為什麼?

第 1 次作答

重點整理

  • 序列通訊在同一條線上一個位元接一個位元傳;有時脈線的是同步(I2C、SPI),沒有的是非同步(UART)。
  • UART:TX、RX 交叉接,雙方鮑率相同;8N1 訊框是起始位元、8 個資料位元(最低位元先)、停止位元。
  • I2C:SDA、SCL 兩條線加上拉電阻,用 7 位元位址點名,第 9 個時脈回應 ACK;scan() 找裝置、readfrom_mem() 讀暫存器。
  • SPI:SCLK、MOSI、MISO 加每個裝置一條 CS(低電位選中),全雙工、速度快、線比較多。
  • 接線前確認電壓準位,5V 模組接 3.3V 開發板要電位轉換。

下一課看感測器怎麼把溫度、光線變成電壓與數字,以及讀值為什麼會跳動、怎麼校正。

這一課完成了!
Next lesson:感測器原理與校正
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。