Redirect printf bằng _write() trên STM32 là gì?
Giải thích cách printf trong newlib/newlib-nano đi qua _write() và được redirect ra UART trên STM32.
Ghi chú nhanh
Trong STM32CubeIDE dùng GCC/newlib, printf() thường được redirect ra UART bằng cách override hàm _write().
Ví dụ quen thuộc:
int _write(int file, char *ptr, int len)
{
HAL_UART_Transmit(&huart1, (uint8_t *)ptr, len, HAL_MAX_DELAY);
return len;
}
Ý tưởng là: thư viện C xử lý format string trước, sau đó gọi _write() để gửi chuỗi byte ra output.
Vì sao lại là _write()?
printf() không tự biết UART của bạn nằm ở đâu. Với embedded GCC/newlib, các hàm chuẩn như printf() cần một “cổng ra” thấp hơn để ghi dữ liệu.
_write() chính là một syscall stub mà bạn có thể tự định nghĩa.
Nói đơn giản:
printf("value=%d", x)
-> format thành chuỗi byte
-> gọi _write()
-> firmware quyết định gửi byte đi đâu
Bạn có thể gửi ra UART, SWO, USB CDC, hoặc thậm chí ghi vào RAM buffer.
Khi nào gặp khái niệm này?
Bạn sẽ gặp _write() khi:
- làm redirect
printf()ra UART; - tắt semihosting;
- dùng newlib/newlib-nano;
- đọc tutorial STM32 debug bằng UART;
- muốn thay
HAL_UART_Transmit()blocking bằng logger non-blocking.
Điểm cần cẩn thận
Cách redirect phổ biến nhất thường dùng HAL_UART_Transmit() blocking. Nó dễ hiểu, dễ chạy, nhưng không hề miễn phí.
HAL_UART_Transmit(&huart1, (uint8_t *)ptr, len, HAL_MAX_DELAY);
Nếu log dài hoặc baudrate thấp, CPU sẽ chờ UART truyền xong. Trong một project nhỏ thì không sao. Nhưng trong firmware có timing nhạy, một dòng printf() vô tình có thể làm thay đổi hành vi hệ thống.
Một bẫy nhỏ về kích thước dữ liệu
Không nên giả định _write() luôn nhận nguyên một dòng log hoàn chỉnh.
Tùy cấu hình thư viện C, buffering và cách gọi, dữ liệu có thể đi xuống theo các chunk khác nhau.
Vì vậy _write() nên xử lý theo ptr và len, không nên dựa vào ký tự \0 như string C thông thường.
Ví dụ đúng hướng:
int _write(int file, char *ptr, int len)
{
Debug_UART_Write((const uint8_t *)ptr, (uint16_t)len);
return len;
}
Cách nhớ
printf() phụ trách format.
_write() phụ trách đưa byte ra ngoài.
UART chỉ là một trong nhiều output có thể chọn.
Bài liên quan nên đọc tiếp
- HAL_UART_Transmit() blocking như thế nào?
- HAL_BUSY xảy ra khi nào trong UART DMA?
- Thiết kế hệ thống UART logging non-blocking trên STM32
Thấy nội dung này hữu ích?
Lưu lại hoặc chia sẻ cho người cũng đang học firmware, BIOS/UEFI và embedded systems.
Nội dung liên quan
Một số bài viết, ghi chú hoặc project có liên quan đến nội dung bạn vừa đọc.
UART non-blocking logger trên STM32 với DMA + Ring Buffer
Thiết kế debug UART non-blocking cho STM32: tránh printf, xử lý mất log với DMA, dùng ring buffer và DMA callback.
Flash Operations STM32: Erase, Write, Polling và tại sao program đứng
Cách thao tác Flash STM32 qua HAL: unlock, erase, write, polling vs interrupt, và nguyên nhân program freeze khi xóa Flash.
DMA Normal mode và Circular mode khác nhau thế nào?
Phân biệt DMA Normal mode và Circular mode khi dùng UART, ADC, audio stream hoặc logger trên STM32.
Biến note thành bài viết hoàn chỉnh
Notes là nơi ghi nhanh khái niệm.