Bài viết 10 thuộc series STM32F103C8T6 tập trung theo hướng “học để làm được việc”. Với chủ đề UART trên STM32F103C8T6: truyền nhận dữ liệu với máy tính, 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.
UART là kênh debug và điều khiển quan trọng trong mọi project Embedded. Một giao thức UART rõ ràng giúp bạn theo dõi hệ thống nhanh, đặc biệt ở giai đoạn tích hợp phần cứng.
1. Mục tiêu bài học
Bài này giúp bạn xây nền tảng truyền nhận ổn định, từ cấu hình baudrate đến thiết kế parser lệnh tránh lỗi treo hoặc mất dữ liệu.
- Hiểu khung UART: start bit, data bits, parity, stop bit.
- Cấu hình UART và kiểm tra baudrate đúng với terminal.
- Triển khai nhận dữ liệu bằng interrupt an toàn.
- Thiết kế parser lệnh text đơn giản cho project.
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.
- Nguyên nhân lệch baud và ký tự rác.
- So sánh polling, interrupt và DMA cho UART.
- Nguyên tắc buffer vòng khi nhận dữ liệu liên tục.
- Cách đóng gói bản tin có delimiter hoặc checksum.
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
Cấu hình baudrate và độ tin cậy truyền thông
Baudrate phải đồng nhất giữa hai đầu giao tiếp. Trong thực tế, sai clock hoặc sai cài đặt parity/stop bit sẽ gây hiện tượng dữ liệu đứt đoạn hoặc ký tự lỗi rất khó chẩn đoán nếu không có quy trình test chuẩn.
- Bắt đầu với 115200 8N1 để debug phổ thông.
- Xác minh clock thực trước khi kết luận lỗi UART.
- Dùng bản tin test cố định để kiểm tra nhanh.
Nhận UART bằng interrupt và buffer vòng
Nhận từng byte bằng interrupt phù hợp cho command line hoặc khối dữ liệu vừa. Buffer vòng giúp tránh mất dữ liệu khi main loop bận, đồng thời cho phép parser xử lý theo khung linh hoạt.
- ISR chỉ đẩy byte vào buffer và tái arm receive.
- Main loop tách bản tin theo ký tự kết thúc.
- Giới hạn chiều dài khung để tránh tràn buffer.
Thiết kế giao thức lệnh tối giản
Một giao thức đơn giản nhưng rõ ràng giúp debug nhanh và dễ tự động hóa kiểm thử. Ví dụ định dạng CMD:VALUE\n với phản hồi ACK/ERR sẽ phù hợp cho hầu hết project học tập.
- Chuẩn hóa danh sách lệnh và tham số.
- Trả về mã lỗi cụ thể thay vì im lặng.
- Thêm lệnh HELP để người mới dễ dùng.
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
Hãy coi UART như một giao diện người-máy thu nhỏ: có định dạng, có timeout, có phản hồi lỗi.
- Cấu hình UART1 trong CubeMX với baudrate mục tiêu.
- Kết nối USB-UART adapter và kiểm tra mass chung.
- Gửi chuỗi test từ STM32 lên terminal.
- Bật nhận interrupt từng byte và buffer vòng.
- Viết parser command cơ bản với delimiter \n.
- Thêm phản hồi ACK/ERR và log trạng thái.
Ở 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ụ nhận UART bằng interrupt từng byte
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.
uint8_t rx_byte;
char cmd_buf[64];
volatile uint8_t cmd_ready = 0;
static uint8_t idx = 0;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1) {
if (rx_byte == '\n') { cmd_buf[idx] = 0; cmd_ready = 1; idx = 0; }
else if (idx < sizeof(cmd_buf)-1) cmd_buf[idx++] = rx_byte;
HAL_UART_Receive_IT(&huart1, &rx_byte, 1);
}
}
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õ.
- Sai baudrate hoặc sai clock gây ký tự rác.
- Không re-arm UART interrupt sau mỗi byte nhận.
- Tràn buffer do không giới hạn độ dài lệnh.
- Parser không xử lý ký tự CR/LF đúng chuẩn terminal.
- Log quá nhiều làm nghẽn đường truyền.
Đừng chỉ in printf liên tục; hãy chuẩn hóa gói log để dễ đọc và ít tốn băng 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ế.
- Xây dựng bộ lệnh LED ON/OFF/TOGGLE qua UART.
- Thêm lệnh SETPWM:x để điều khiển duty motor.
- Thiết kế khung bản tin có checksum 8-bit.
- Viết script Python gửi lệnh tự động để test parser.
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.