12.2 C
London
Thứ Năm, Tháng 10 9, 2025

Giao thức UART – Ví dụ với STM32

Giao thức UART (Universal Asynchronous Receiver-Transmitter) là một giao thức kinh điển khi làm lập trình nhúng mà các bản gặp phải.

📡Giới thiệu về giao tiếp nối tiếp (Serial Communication)

Serial vs Parallel

  • Giao tiếp song song (Parallel): nhiều bit gửi đồng thời qua nhiều dây (ví dụ 8 bit gửi qua 8 dây)
  • Giao tiếp nối tiếp (Serial): gửi từng bit lần lượt qua một (hoặc ít) dây dữ liệu
  • Serial có ưu điểm: dùng ít dây, chi phí thấp hơn, dễ mở rộng đường truyền, đặc biệt khi khoảng cách truyền dài

📌Do đó trong vi điều khiển / nhúng, hầu hết giao tiếp giữa các chip sử dụng dạng nối tiếp như UART, SPI, I²C, USB, v.v.

Các chuẩn giao tiếp nối tiếp phổ biến

Một số chuẩn nối tiếp thường dùng:

Giao thứcDây / tín hiệu chínhĐồng bộ / Không đồng bộỨng dụng thường gặp
UART / USARTTX, RX (và đôi khi RTS/CTS)Không đồng bộGiao tiếp máy tính – vi điều khiển, module Bluetooth, module GSM
SPIMOSI, MISO, SCK, CSĐồng bộ (có xung giờ)Flash, cảm biến cao tốc, màn hình, ADC ngoại vi
I²CSDA, SCLĐồng bộMạng nhiều thiết bị, cảm biến, EEPROM, RTC
USB / CAN / LIN / RS-485Trong hệ thống lớn, mạng công nghiệp

🛠️Cấu tạo & cách thức hoạt động của UART / Serial không đồng bộ

Các thành phần chính

Ở phía phần cứng / MCU, một khối UART thường gồm:

  • Thanh ghi truyền (Transmit Register)
  • Thanh ghi dịch (Shift Register)
  • Thanh ghi nhận (Receive Register)
  • Bộ sinh tần số (baud rate generator)
  • Logic điều khiển khung dữ liệu (start bit, dữ liệu, parity, stop bit)

📌Khi bạn gửi một byte, dữ liệu từ thanh ghi truyền sẽ được dịch ra từng bit, nối tiếp gửi ra kênh TX; phía nhận thì đọc bit từ RX và lắp lại thành byte.

Khung dữ liệu (Frame) UART

Một khung UART cơ bản có cấu trúc:

  • Start bit: 1 bit thấp (logic 0) để báo bắt đầu
  • Data bits: thường 8 bit (LSB trước)
  • Parity bit (tuỳ chọn): kiểm tra lỗi (even / odd)
  • Stop bit(s): 1 hoặc 2 bit cao (logic 1) để báo kết thúc

Hai đầu bên giao tiếp phải thống nhất các tham số: baud rate, số bit dữ liệu, parity, stop bit để hiểu đúng dữ liệu gửi – nếu không sẽ nhận nhầm.

📌Ví dụ: nếu baud rate là 115200 bps, thì mỗi bit được gửi trong khoảng thời gian ~8.68 micro-giây.

Đồng bộ hoá và sai số

Do giao tiếp UART không gửi xung đồng hồ (clock line) chung, nên khi nhận bit, vi điều khiển dựa vào thời gian nội bộ (bằng cách chia tần số) để đo vị trí bắt bit, đọc bit giữa.

Điều này có nghĩa nếu baud rate giữa hai thiết bị không khớp, hoặc dao động clock nhiều, sẽ dẫn đến lỗi nhận bit. Thường người ta cho phép sai số ±2–3% để vẫn hoạt động ổn định.


⚙️Ứng dụng & ví dụ minh họa: giao tiếp giữa máy tính và MCU, hoặc giữa hai MCU

Mô hình giao tiếp cơ bản

Ví dụ chuẩn: PC ↔ Converter USB-TTL ↔ Vi điều khiển

  • PC gửi dữ liệu qua USB, converter (ví dụ FT232, CH340) chuyển sang tín hiệu TTL 3.3 V hoặc 5 V
  • MCU dùng chân TX / RX để nhận / gửi

📌Nếu hai MCU giao tiếp trực tiếp, ta nối TX của cái này vào RX của cái kia và ngược lại.

Ví dụ STM32 (ví dụ sử dụng HAL / CubeMX)

📌Dưới đây là minh hoạ đơn giản để giao tiếp UART giữa STM32 và máy tính:

Phần cứng

  • STM32 sử dụng mức 3.3 V (không cần chuyển level nếu converter USB-TTL cũng 3.3 V)
  • Nối: STM32 TX → RX của converter, STM32 RX ← TX converter
  • GND chung

Cấu hình phần mềm (HAL + CubeMX)

  1. Trong CubeMX:
    • Bật USART (UART): chọn 1 USART (ví dụ USART1)
    • Chọn baud rate (ví dụ 115200), 8 bit dữ liệu, no parity, 1 stop bit
    • Khai báo pin TX / RX tương ứng (ví dụ PA9 → TX, PA10 → RX)
    • Enable “USART global interrupt” nếu muốn dùng ngắt
  2. Trong mã nguồn:
/* Khởi tạo UART */
UART_HandleTypeDef huart1;

void MX_USART1_UART_Init(void)
{
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
}
  1. Gửi / nhận dữ liệu:
// Gửi chuỗi
char msg[] = "Hello from STM32!\r\n";
HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);

// Nhận 1 byte (blocking)
uint8_t rx;
HAL_UART_Receive(&huart1, &rx, 1, HAL_MAX_DELAY);
// Hoặc dùng ngắt / DMA để không block chính của chương trình

Khi bạn mở terminal (ví dụ PuTTY, Tera Term) ở baud 115200, bạn sẽ thấy chuỗi từ STM32 được gửi lên.

Ví dụ giao tiếp giữa hai STM32

  • Cấu hình đúng UART trên cả hai MCU
  • STM32-A gửi dữ liệu “Ping”, STM32-B nhận và trả lại “Pong”
  • Sử dụng ngắt nhận hoặc polling để kiểm tra liệu dữ liệu có đến không

⚠️Những lưu ý khi làm việc với giao tiếp Serial / UART

  • Khớp baud rate giữa hai đầu là bắt buộc
  • Chọn dung lượng bộ đệm (buffer) phù hợp, vì nếu dữ liệu đến quá nhanh mà không đọc kịp có thể overflow
  • Khi nối dài đường truyền, có thể cần bổ sung transceiver chuẩn RS-485 / RS-232 để cách ly và truyền xa
  • Nếu sử dụng parity / stop bit / flow control (RTS/CTS), đảm bảo cả hai đầu hiểu cùng cách
  • Nếu dùng DMA hoặc ngắt để tối ưu hiệu suất, phải chú ý đồng bộ hoá và quản lý trạng thái

✅Kết luận

UART là chuẩn giao tiếp nối tiếp đơn giản nhưng cực kỳ quan trọng trong các hệ thống nhúng. Với STM32, ta có thể:

  • Bắt đầu từ polling (blocking) để test đơn giản.
  • Tiến lên ngắt và DMA để tối ưu hiệu suất.

💡 Sau khi nắm UART, bạn sẽ dễ dàng tiếp cận các giao tiếp khác như SPI, I²C, CAN.

Đánh giá bài viết!
Admin Peter
Admin Peterhttps://capitlab.com/
Coder & Hardware Designer $ Embedded Linux

Nội dung hấp dẫn

Modbus RTU STM32 (Phần 3)

Ở phần 1 và phần 2 mình đã giới...

Modbus RTU STM32 (Phần 2)

Giao thức Modbus trên STM32

Modbus RTU STM32 (Phần 1)

Trong các hệ thống điều khiển công nghiệp, Modbus...

STM32 HAL driver với cảm biến nhịp tim MAX30100 sử dụng STM32CUBE IDE

Cấu tạo và cách giao tiếp với cảm biến...

Thiết kế mạch cầu H tích hợp PID

Bài viết này mình sẽ chia sẻ cho...

Đặt mạch Trung Quốc, mua linh kiện hiếm

Bài viết nói về đặt mạch Trung Quốc và...

Nội dung liên quan