Khi bắt đầu làm việc với các dòng vi điều khiển như Arduino Uno, STM32 hay ESP32, thử thách đầu tiên sau bài toán "chớp tắt LED" chính là làm thế nào để vi điều khiển giao tiếp được với thế giới bên ngoài: đọc dữ liệu từ cảm biến nhiệt độ, hiển thị thông số lên màn hình OLED, gửi lệnh sang module định vị GPS hay truyền log về máy tính để debug.
Lúc này, bạn sẽ bắt gặp ba chuẩn giao tiếp nối tiếp kinh điển: UART, I2C và SPI. Dù cùng mục đích truyền nhận dữ liệu, mỗi chuẩn lại mang triết lý thiết kế, tốc độ và cách đấu nối phần cứng hoàn toàn khác nhau.
UART: Đơn giản, trực tiếp và không cần xung nhịp đồng bộ
UART (Universal Asynchronous Receiver-Transmitter) là chuẩn truyền thông nối tiếp bất đồng bộ (Asynchronous) theo cơ chế điểm - điểm (Point-to-Point).

Nguyên lý hoạt động của UART
UART hoạt động theo phương thức truyền dữ liệu bất đồng bộ (Asynchronous). Điều này có nghĩa là thiết bị gửi và thiết bị nhận không cần dùng chung một đường Clock. Thay vào đó, hai bên phải thống nhất trước tốc độ truyền, thường được gọi là Baud Rate. Một số Baud Rate thường gặp gồm:
- 9600 baud.
- 19200 baud.
- 38400 baud.
- 57600 baud.
- 115200 baud.
Khi không truyền dữ liệu, đường UART thường ở trạng thái logic HIGH. Khi bắt đầu truyền một khung dữ liệu, thiết bị phát sẽ gửi lần lượt:
Start Bit → Data Bits → Parity Bit (nếu có) → Stop Bit
Trong đó:
- Start Bit: Báo hiệu bắt đầu một khung dữ liệu.
- Data Bits: Chứa dữ liệu cần truyền, thường từ 5 đến 9 bit, phổ biến nhất là 8 bit.
- Parity Bit: Bit kiểm tra chẵn lẻ, có thể được sử dụng để phát hiện một số lỗi truyền dữ liệu.
- Stop Bit: Đánh dấu kết thúc khung dữ liệu.
Một cấu hình UART rất phổ biến là 8N1, nghĩa là:
- 8 bit dữ liệu.
- N – No Parity: không sử dụng Parity Bit.
- 1 Stop Bit.
Do không có đường Clock đồng bộ, Baud Rate của thiết bị truyền và thiết bị nhận cần được thiết lập tương thích. Nếu hai bên sử dụng tốc độ truyền khác nhau, dữ liệu nhận được có thể bị sai hoặc không thể giải mã.
Sơ đồ nguyên lý kết nối phần cứng
Điểm đặc trưng nhất của UART là kết nối bắt chéo:
- Chân truyền TX của thiết bị này phải nối vào chân nhận RX của thiết bị kia.
- Bắt buộc phải có đường GND chung để làm mốc điện áp tham chiếu.

I2C: Bậc thầy tiết kiệm chân vi điều khiển
I2C (Inter-Integrated Circuit) là chuẩn giao tiếp đồng bộ hoạt động theo mô hình Master - Slave, cho phép nhiều thiết bị dùng chung một đường bus chỉ với 2 dây tín hiệu.

Nguyên lý hoạt động của I2C
I2C là giao tiếp đồng bộ (Synchronous). Dữ liệu được truyền dựa trên tín hiệu Clock do thiết bị điều khiển bus tạo ra. Bus I2C gồm hai đường:
- SDA (Serial Data): Đường truyền dữ liệu.
- SCL (Serial Clock): Đường xung nhịp.
Trong cấu hình phổ biến, thiết bị điều khiển sẽ bắt đầu quá trình giao tiếp và gửi địa chỉ của thiết bị cần làm việc. Mỗi thiết bị trên bus I2C có một địa chỉ riêng, thường là địa chỉ 7 bit. Nhờ đó, nhiều thiết bị có thể dùng chung SDA và SCL mà vi điều khiển vẫn xác định được thiết bị cần giao tiếp. Quá trình truyền dữ liệu I2C cơ bản có thể diễn ra như sau:
Start → Address → Read/Write → ACK/NACK → Data → Stop
Trong đó:
- Start Condition: Báo hiệu bắt đầu quá trình giao tiếp.
- Address: Địa chỉ thiết bị cần giao tiếp.
- Read/Write Bit: Xác định thao tác đọc hoặc ghi.
- ACK: Thiết bị nhận xác nhận đã nhận dữ liệu.
- NACK: Không xác nhận hoặc thông báo kết thúc quá trình nhận.
- Data: Dữ liệu được truyền.
- Stop Condition: Kết thúc phiên giao tiếp.
Các tốc độ I2C thường gặp gồm:
- Standard Mode: 100 kHz.
- Fast Mode: 400 kHz.
- Fast Mode Plus: 1 MHz.
- Một số chế độ I2C còn hỗ trợ tốc độ cao hơn.
Trong các dự án Arduino, ESP32 và STM32 thông thường, tốc độ 100 kHz hoặc 400 kHz được sử dụng phổ biến nhất.
Sơ đồ nguyên lý kết nối phần cứng
-
SDA (Serial Data): Đường truyền dữ liệu hai chiều.
- SCL (Serial Clock): Đường xung nhịp do Master phát ra.
Lưu ý phần cứng bắt buộc: Do ngõ ra của I2C là cực má hở (Open-Drain), bus bắt buộc phải có 2 điện trở kéo lên nguồn (Pull-up resistors), thường chọn giá trị từ 2.2kΩ đến 4.7kΩ nối lên đường nguồn 3.3V hoặc 5V.

SPI: Tốc độ tối đa cho ứng dụng nặng
SPI (Serial Peripheral Interface) là chuẩn giao tiếp đồng bộ 4 dây tốc độ rất cao (10 MHz đến 80+ MHz), hỗ trợ truyền nhận dữ liệu toàn song công (Full-duplex).

Nguyên lý hoạt động của SPI
SPI hoạt động theo phương thức giao tiếp đồng bộ, trong đó thiết bị điều khiển tạo ra tín hiệu Clock trên đường SCK. Khi muốn giao tiếp với một thiết bị ngoại vi, vi điều khiển sẽ kích hoạt chân CS tương ứng, thường bằng cách kéo chân này xuống mức LOW. Sau đó dữ liệu được truyền đồng thời trên hai đường:
- MOSI: Dữ liệu từ Master đến Slave.
- MISO: Dữ liệu từ Slave về Master.
Do có hai đường dữ liệu riêng biệt, SPI có thể hỗ trợ Full-duplex, nghĩa là truyền và nhận dữ liệu cùng thời điểm. Quá trình giao tiếp cơ bản:
Chọn CS → Tạo Clock → Truyền/nhận dữ liệu → Bỏ chọn CS
SPI không quy định một tốc độ duy nhất cho tất cả thiết bị. Tốc độ truyền phụ thuộc vào:
- Khả năng của vi điều khiển.
- Tốc độ tối đa của thiết bị ngoại vi.
- Thiết kế PCB.
- Chiều dài đường truyền.
- Chất lượng tín hiệu.
Trong thực tế, SPI có thể hoạt động từ vài trăm kHz đến hàng chục MHz hoặc cao hơn tùy phần cứng. SPI cũng có nhiều chế độ hoạt động khác nhau dựa trên hai thông số:
- CPOL – Clock Polarity.
- CPHA – Clock Phase.
Sự kết hợp của CPOL và CPHA tạo thành SPI Mode 0, Mode 1, Mode 2 và Mode 3. Khi kết nối thiết bị SPI, cần cấu hình đúng Mode theo Datasheet của thiết bị ngoại vi.
Sơ đồ nguyên lý kết nối phần cứng
Các thiết bị Slave dùng chung đường Clock và Data, nhưng mỗi Slave cần một chân chọn chip (CS / Chip Select) riêng biệt điều khiển từ Master:
-
SCK: Xung nhịp chung.
-
MOSI: Dữ liệu Master gửi xuống Slave.
-
MISO: Dữ liệu Slave gửi ngược về Master.
-
CS1, CS2: Tích cực mức thấp (LOW) để chọn Slave cần làm việc.

So sánh UART, I2C và SPI
|
Tiêu chí |
UART |
I2C |
SPI |
|
Xung đồng bộ |
Không (Bất đồng bộ) |
Có (SCL) |
Có (SCK) |
|
Số dây giao tiếp chuẩn |
2 dây (TX, RX) |
2 dây (SDA, SCL) |
4 dây (SCK, MOSI, MISO, CS) |
|
Kiểu truyền |
Toàn song công (Full-duplex) |
Bán song công (Half-duplex) |
Toàn song công (Full-duplex) |
|
Tốc độ thông dụng |
9.6 kbps - 115.2 kbps |
100 kbps - 400 kbps |
10 Mbps - 80+ Mbps |
|
Mở rộng thiết bị |
1 - 1 (Point-to-Point) |
Nhiều thiết bị (theo địa chỉ Hex) |
Nhiều thiết bị (mỗi Slave thêm 1 chân CS) |
|
Linh kiện kèm theo |
Không |
Cần trở kéo Pull-up (4.7kΩ) |
Không |
Nên chọn UART, I2C hay SPI?
Không có chuẩn giao tiếp nào tốt nhất trong mọi trường hợp. Việc lựa chọn UART, I2C hay SPI phụ thuộc vào yêu cầu về tốc độ truyền, số lượng thiết bị ngoại vi, số chân GPIO còn trống và mức độ phức tạp của hệ thống.
- Có thể lựa chọn nhanh theo nhu cầu như sau:
- Chọn UART khi cần giao tiếp đơn giản giữa hai thiết bị, truyền log debug hoặc kết nối GPS, GSM, Bluetooth.
- Chọn I2C khi cần kết nối nhiều cảm biến và muốn tiết kiệm chân GPIO.
- Chọn SPI khi tốc độ truyền dữ liệu là yếu tố quan trọng, chẳng hạn màn hình đồ họa, thẻ nhớ hoặc bộ nhớ ngoài.
- Chọn I2C thay vì SPI nếu tốc độ không quá quan trọng nhưng cần kết nối nhiều thiết bị.
- Chọn SPI thay vì I2C nếu cần tốc độ cao và vi điều khiển vẫn còn đủ chân GPIO.

Trong thực tế, một hệ thống nhúng không nhất thiết chỉ sử dụng duy nhất một chuẩn giao tiếp. Arduino, ESP32 hay STM32 có thể sử dụng UART, I2C và SPI đồng thời cho các thiết bị ngoại vi khác nhau. Ví dụ, một hệ thống ESP32 có thể sử dụng:
- UART để nhận dữ liệu từ module GPS
- I2C để đọc cảm biến nhiệt độ, độ ẩm và RTC.
- SPI để điều khiển màn hình TFT hoặc lưu dữ liệu vào thẻ nhớ SD.
Do đó, khi lựa chọn giữa UART, I2C và SPI, nên cân nhắc đồng thời tốc độ, số lượng thiết bị, số chân GPIO và yêu cầu thực tế của dự án thay vì chỉ dựa vào một thông số duy nhất.
Tổng kết
Không có một chuẩn giao tiếp nào là "hoàn hảo nhất" cho mọi bài toán, mà chỉ có chuẩn phù hợp nhất với yêu cầu phần cứng và tài nguyên dự án của bạn: cần đơn giản gỡ lỗi hãy chọn UART, cần tiết kiệm chân cho cụm cảm biến hãy ưu tiên I2C, và khi cần tốc độ cao cho màn hình đồ họa hay bộ nhớ ngoài thì SPI luôn là lựa chọn hàng đầu.
Hy vọng bài so sánh này giúp bạn tự tin hơn khi lên sơ đồ nguyên lý và thiết kế mạch cho các dự án sắp tới. Trong dự án hiện tại, bạn đang sử dụng chuẩn giao tiếp nào nhiều nhất và có từng gặp phải "ca khó" nào khi kết nối chúng không? Hãy chia sẻ cùng mình ở phần bình luận bên dưới nhé!