📌 Giới thiệu
I2C (Inter-Integrated Circuit) là một giao thức truyền thông nối tiếp, cho phép nhiều thiết bị ngoại vi (slave) giao tiếp với một hoặc nhiều thiết bị điều khiển (master) chỉ thông qua hai dây tín hiệu:
- SDA (Serial Data Line): đường dữ liệu.
- SCL (Serial Clock Line): đường xung nhịp.
I2C thường được sử dụng trong các hệ thống nhúng, cảm biến, IC giao tiếp nội bộ bo mạch.
⚖️ So sánh với UART và SPI
Tiêu chí | UART | SPI | I2C |
---|---|---|---|
Số dây | 2 (TX, RX) | 4 + mỗi thiết bị thêm 1 CS | 2 (SDA, SCL) |
Thiết bị | 2 (point-to-point) | Nhiều (nhưng cần nhiều CS) | Nhiều (qua địa chỉ) |
Đồng bộ | Không | Có | Có |
Tốc độ | Trung bình (115200bps) | Cao (MHz) | 100kHz – 400kHz (tới vài MHz) |
Đa master | Không | Không (thường chỉ 1) | Có (hỗ trợ arbitration) |
👉 I2C nằm giữa UART và SPI: ít dây hơn SPI, hỗ trợ nhiều thiết bị hơn UART.
⏳ Lịch sử ngắn gọn
- 1982: Philips giới thiệu chuẩn I2C.
- 1992: Bổ sung chế độ nhanh (400kHz) và địa chỉ 10 bit.
- Sau đó: mở rộng fast-mode plus (1MHz), high-speed (3,4MHz), ultra-fast (5MHz).
- 1995: Intel đưa ra SMBus (biến thể của I2C) dùng cho quản lý hệ thống.
🔌 Hoạt động phần cứng
Đặc điểm:
- Open-drain / Open-collector: Thiết bị chỉ có thể kéo xuống mức thấp, mức cao được giữ nhờ điện trở kéo lên (pull-up resistor).
- Pull-up resistor: Thường dùng 4.7kΩ (có thể điều chỉnh tùy bus dài/ngắn).
- Tương thích điện áp: Dễ nối các thiết bị 3.3V và 5V, nhưng đôi khi cần mạch dịch mức.
📡 Nguyên tắc giao tiếp I2C
1. Start & Stop Condition
- Start: SDA đi từ cao → thấp khi SCL đang cao.
- Stop: SDA đi từ thấp → cao khi SCL đang cao.
2. Địa chỉ và ACK/NACK
- Mỗi thiết bị slave có một địa chỉ (address). Trong chế độ phổ biến, dùng 7 bit làm địa chỉ.
- Khi master gửi địa chỉ + bit đọc/ghi, slave tương ứng nếu nhận diện địa chỉ sẽ gửi ACK (acknowledge) bằng cách kéo SDA thấp tại chu kỳ ACK. Nếu không có ACK, tức là không có thiết bị phản hồi.
- Sau mỗi byte dữ liệu, cũng có bit ACK/NACK để xác nhận dữ liệu đã nhận đúng hay chưa.
3. Clock Stretching
- Trong quá trình truyền, nếu slave chưa sẵn sàng (ví dụ cần thời gian xử lý hoặc chuẩn bị dữ liệu), nó có thể giữ SCL ở mức thấp, làm master đợi trước khi tiếp tục gửi xung clock mới.
- Nhờ đó, slave có thể “nói” rằng nó cần thời gian — master phải tuân thủ và không gửi tiếp nếu SCL vẫn bị kéo thấp
4. Arbitration (đa master)
- Nếu có nhiều master bắt đầu truyền cùng lúc, sẽ có cơ chế tranh chấp: mỗi master khi gửi bit “1” (tức không kéo thấp) sẽ kiểm tra xem SDA thực tế có bằng 1 không. Nếu nó khác (tức có thiết bị khác kéo thấp), master đó biết đã thua arbitration và dừng truyền tiếp.
- Cơ chế này đảm bảo rằng dòng SDA phản ánh “logic thấp” nếu bất kỳ master nào kéo thấp — giúp giải quyết tình huống nhiều master bước vào bus cùng lúc.
5. Chế độ nâng cao – hỗ trợ 10-bit địa chỉ
- Ngoài chế độ 7-bit, I2C cũng định nghĩa chế độ 10-bit địa chỉ, nơi địa chỉ rộng hơn để hỗ trợ nhiều thiết bị hơn.
- Trong trường hợp 10-bit, địa chỉ được gửi qua hai khung dữ liệu (frames) — khung đầu có mã tiền tố “11110” + bit MSB của địa chỉ + RW, khung sau chứa 8 bit địa chỉ thấp.
⚙️ Ví dụ với STM32 (HAL Library)
Với STM32, bạn có thể dùng CubeMX để cấu hình I2C dễ dàng:
- Chọn Peripheral I2C trong CubeMX, ví dụ I2C1.
- Chọn chân SDA, SCL tương ứng (thường hiển thị màu xanh khi enable).
- Cấu hình tốc độ (Standard Mode 100kHz hoặc Fast Mode 400kHz).
- Generate code và mở trong STM32CubeIDE.
Code ví dụ: STM32 Master đọc 1 byte từ Slave
#include "main.h"
#include "i2c.h"
#include "usart.h"
uint8_t data;
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
MX_USART2_UART_Init();
while (1)
{
// Gửi địa chỉ thanh ghi cần đọc
uint8_t reg = 0x75;
HAL_I2C_Master_Transmit(&hi2c1, 0x68 << 1, ®, 1, HAL_MAX_DELAY);
// Đọc dữ liệu từ slave
HAL_I2C_Master_Receive(&hi2c1, 0x68 << 1, &data, 1, HAL_MAX_DELAY);
// In ra UART
char msg[30];
sprintf(msg, "Gia tri: 0x%02X\r\n", data);
HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
HAL_Delay(1000);
}
}
👉 Ở ví dụ này, STM32 cấu hình làm master, đọc thanh ghi WHO_AM_I
từ MPU6050 qua I2C.
⚠️ Lưu ý khi sử dụng I2C
- Địa chỉ trùng lặp: Một số IC có địa chỉ cố định → khó dùng nhiều IC cùng loại.
- Chiều dài bus hạn chế: Bus dài hoặc nhiều thiết bị gây nhiễu, cần tính toán lại pull-up resistor hoặc dùng bus extender.
- Tốc độ: Không phù hợp cho truyền dữ liệu tốc độ cao hoặc khoảng cách xa.
✅ Kết luận
I2C là một trong những giao thức truyền thông quan trọng nhất trong thế giới nhúng. Với chỉ hai dây, nó cho phép kết nối nhiều cảm biến, IC, module ngoại vi trong một hệ thống.
Trên thực tế, bạn có thể dễ dàng triển khai I2C trên Arduino, Raspberry Pi, hoặc STM32. Mỗi nền tảng đều có thư viện hỗ trợ, giúp việc lập trình nhanh chóng và hiệu quả.