Bài viết 11 thuộc series STM32F103C8T6 tập trung theo hướng “học để làm được việc”. Với chủ đề Giao tiếp I2C trên STM32F103C8T6 với LCD/OLED/cảm biến, bạn sẽ đi từ lý thuyết cốt lõi đến quy trình triển khai thực tế trong phòng lab. Nội dung được thiết kế để sinh viên, kỹ thuật viên bảo trì và người mới chuyển từ PLC sang Embedded đều có thể áp dụng ngay vào project mẫu.
I2C cho phép kết nối nhiều thiết bị chỉ với hai dây, nhưng cũng là giao thức dễ gặp lỗi nếu wiring hoặc cấu hình timing chưa chuẩn. Kỹ năng debug I2C là điểm khác biệt lớn giữa học lý thuyết và làm dự án thực.
1. Mục tiêu bài học
Mục tiêu của bài là giúp bạn làm chủ cả ba lớp: nguyên lý bus, driver HAL và kỹ thuật phân tích lỗi khi giao tiếp thực tế.
- Hiểu đặc tính open-drain và vai trò điện trở pull-up trên I2C.
- Cấu hình master I2C ổn định trên STM32F103C8T6.
- Giao tiếp thành công với LCD/OLED/cảm biến phổ biến.
- Xử lý các lỗi bus bận, NACK, timeout theo quy trình rõ ràng.
Sau khi hoàn thành, bạn nên tự trả lời được ba câu hỏi: hệ thống cần đo/điều khiển đại lượng gì, vòng đời xử lý dữ liệu chạy theo chu kỳ nào, và cơ chế bảo vệ lỗi nào cần có để hệ thống ổn định khi chạy liên tục ngoài hiện trường.
2. Kiến thức nền tảng
STM32F103C8T6 dùng lõi ARM Cortex-M3, phù hợp để học đồng thời kiến trúc MCU và phương pháp lập trình thời gian thực. Khi học theo lộ trình chuẩn, bạn nên nắm đồng thời phần cứng (clock, GPIO, bus, nguồn), firmware (HAL/LL, ngắt, timer), và phương pháp test (scope, logic analyzer, UART log). Việc kết hợp đủ ba lớp kiến thức giúp quá trình debug nhanh hơn nhiều so với chỉ học code thuần.
- Address 7-bit và cách dịch trái trong HAL API.
- Start, repeated start, stop condition.
- Khác biệt giữa write register và read register.
- Ảnh hưởng chiều dài dây và tốc độ bus tới độ ổn định.
Trong quá trình thực hành, bạn nên duy trì checklist kiểm thử ở từng vòng: xác nhận wiring, xác nhận xung clock, xác nhận trạng thái ngoại vi, sau đó mới kiểm tra thuật toán. Cách làm này giảm đáng kể lỗi dây chuyền và giúp nhóm dự án phối hợp hiệu quả giữa phần cứng và phần mềm.
3. Phân tích kỹ thuật
Timing và pull-up trong I2C
Do I2C dùng open-drain, tín hiệu lên mức cao phụ thuộc điện trở kéo lên. Nếu pull-up không phù hợp hoặc dây quá dài, sườn lên chậm sẽ gây lỗi giao tiếp. Chọn tốc độ 100kHz cho giai đoạn đầu giúp ổn định hơn.
- Bắt đầu với pull-up 4.7kΩ cho mức 3.3V.
- Giảm tốc độ nếu bus có nhiều thiết bị hoặc dây dài.
- Kiểm tra mức logic khi dùng module có level shifting.
Làm việc với thiết bị thanh ghi
Nhiều cảm biến I2C yêu cầu ghi địa chỉ thanh ghi rồi mới đọc dữ liệu. Cần tách rõ quy trình write/read để tránh nhầm API. Việc chuẩn hóa driver theo hàm read_reg/write_reg giúp tái sử dụng nhanh.
- Định nghĩa hằng địa chỉ thanh ghi trong header.
- Dùng timeout hợp lý cho mọi giao dịch.
- Kiểm tra mã lỗi HAL để khoanh vùng nhanh.
Debug I2C theo lớp
Khi lỗi I2C xảy ra, cần debug theo 3 lớp: điện (pull-up, nguồn, dây), giao thức (address, ACK/NACK, frame), phần mềm (timeout, state machine). Cách tiếp cận theo lớp giúp giảm thời gian tìm lỗi đáng kể.
- Dùng scanner I2C để xác nhận địa chỉ thiết bị.
- Quan sát xung start/stop bằng analyzer.
- Tạo hàm reset bus khi phát hiện treo đường SDA.
Từ góc nhìn hệ thống, chủ đề này không chỉ là “chạy được chức năng”, mà còn là bài toán tối ưu độ tin cậy, độ trễ xử lý, khả năng mở rộng khi thêm cảm biến hoặc thêm kênh truyền thông. Nếu ngay từ đầu bạn chuẩn hóa cấu trúc code theo module driver/app/service thì giai đoạn mở rộng về sau sẽ nhẹ hơn rất nhiều.
4. Các bước thực hiện
Trước khi nghi ngờ code, hãy kiểm tra mức kéo lên SDA/SCL và dạng sóng thực bằng logic analyzer.
- Cấu hình I2C1 ở 100kHz và xác nhận chân SDA/SCL đúng.
- Viết hàm scanner để phát hiện thiết bị trên bus.
- Giao tiếp LCD I2C hoặc OLED SSD1306 hiển thị text cơ bản.
- Đọc một cảm biến I2C và in kết quả qua UART.
- Thêm timeout + retry cho giao dịch quan trọng.
- Kiểm thử khi thay đổi chiều dài dây và số thiết bị bus.
Ở mỗi bước, bạn nên lưu log kỹ thuật (ảnh wiring, ảnh cấu hình CubeMX, thông số timer/UART/ADC, snapshot kết quả đo). Bộ log này giúp giảng viên hoặc trưởng nhóm đánh giá tiến độ nhanh và giảm thời gian trao đổi khi cần hỗ trợ từ xa.
5. Ví dụ hàm đọc thanh ghi I2C
Code dưới đây là mẫu rút gọn theo HAL Library, tập trung phần cốt lõi để bạn nhìn rõ luồng xử lý. Khi đưa vào dự án thực, hãy tách code thành các module rõ ràng, thêm kiểm tra lỗi trả về và timeout cho mọi giao tiếp ngoại vi.
HAL_StatusTypeDef I2C_ReadReg(uint16_t dev7, uint8_t reg, uint8_t *data, uint16_t len)
{
uint16_t addr = dev7 << 1;
if (HAL_I2C_Master_Transmit(&hi2c1, addr, ®, 1, 50) != HAL_OK)
return HAL_ERROR;
return HAL_I2C_Master_Receive(&hi2c1, addr, data, len, 50);
}
Nguyên tắc quan trọng: giữ hàm ngắt ngắn gọn, xử lý nặng chuyển về vòng lặp chính hoặc task nền; dùng biến trạng thái rõ ràng để tránh race condition; thêm timeout cho mọi thao tác có thể block. Đây là nền tảng để firmware ổn định khi chạy lâu dài.
6. Lỗi thường gặp
Phần lớn lỗi trong STM32F103C8T6 đến từ cấu hình chưa đồng bộ giữa clock, ngoại vi và wiring thực tế. Nếu bạn xử lý theo thứ tự “đọc triệu chứng -> khoanh vùng lớp lỗi -> test độc lập từng khối”, thời gian debug sẽ giảm rất rõ.
- Quên dịch trái địa chỉ 7-bit khi dùng HAL.
- Không có pull-up hoặc pull-up quá lớn gây sườn lên chậm.
- Gộp nhầm quy trình ghi/đọc register.
- Timeout quá ngắn khi bus đang bận.
- Không xử lý tình huống thiết bị NACK ngẫu nhiên.
Duy trì bảng địa chỉ thiết bị I2C trong dự án để tránh xung đột khi mở rộng hệ thống.
7. Bài tập thực hành
Để chuyển từ mức “hiểu lý thuyết” sang “làm chủ kỹ năng”, bạn nên làm các bài tập sau theo đúng thứ tự từ dễ đến khó, sau mỗi bài đều ghi lại tiêu chí đạt/chưa đạt và thông số đo thực tế.
- Viết scanner I2C in ra toàn bộ địa chỉ thiết bị đang kết nối.
- Hiển thị nhiệt độ từ cảm biến I2C lên OLED mỗi 500 ms.
- Tạo cơ chế retry 3 lần khi đọc cảm biến thất bại.
- Đo dạng sóng SDA/SCL và phân tích chất lượng tín hiệu.
Khuyến nghị chấm bài theo rubric: đúng chức năng (40%), độ ổn định 30 phút chạy liên tục (30%), chất lượng cấu trúc code (20%), tài liệu kỹ thuật và báo cáo đo kiểm (10%).
Tài liệu tham khảo
- STMicroelectronics, STM32F103x8/B Datasheet.
- STMicroelectronics, RM0008 Reference Manual: STM32F10xxx advanced ARM-based 32-bit MCUs.
- STMicroelectronics, UM1850 STM32CubeMX user manual và tài liệu STM32Cube liên quan.
- ARM, Cortex-M3 Technical Reference Manual.
- STMicroelectronics Application Notes về GPIO, ADC, Timer, UART, I2C, SPI, DMA và Watchdog.