Hướng dẫn sử dụng printf với STM32 Uart để in Log trên Keil C

Sử dụng hàm printf để in Log khi Debug tren STM32

Trong bài này chúng ta sẽ học cách retarget (chuyển hướng) hàm printf của thư viện stdio sang UART để thuận tiện cho việc truyền dữ liệu. Việc sử dụng printf sẽ làm đơn giản hơn khâu in Log để debug, hoặc các tác vụ khác cần xử lý chuỗi.

Bài 22 trong Serie Học lập trình STM32 từ A tới Z

Tại sao chúng ta nên sử dụng printf

Thông thường chúng ta sẽ sử dụng UART để giao tiếp với máy tính hoặc các ngoại vi khác.
Nếu dùng UART để in ra 1 chuỗi (String), chúng ta cần khởi tạo 1 String hoặc Array, sau đó dùng sprintf để ghép các chuỗi và giá trị khác nhau vào, rồi mới gửi đi thông qua hàm HAL_UART_Transmit.

Điều này làm tăng thao tác lập trình và có thể xảy ra lỗi nếu không xóa các String hoặc Array đó trước khi dùng cho lần sau.

Khi các bạn debug chương trình, có một kĩ năng là in Log (nhật kí chương trình), giúp cho lập trình viên hiểu được tiến trình của hệ thống. Khi in Log chúng ta thường xuyên phải in chuỗi, nếu mỗi lần lại phải sprintf rồi gọi hàm UART thì rất mất thời gian.

Vậy nên thông thường chúng ta sẽ retarget lại hàm printf để xuất chuỗi qua UART cho đơn giản hơn. Giống như nối lại đường ống: printf vẫn “xả” chữ ra như cũ, chỉ là đầu ra được nối thẳng vào chân TX của UART.

Hướng dẫn retarget printf trên Keil C

Đầu tiên tạo một project với 1 cổng UART (ở đây là USART1), phần này các bạn đọc kĩ trong bài Lập trình STM32 UART nhé.

set uart

Sau đó, để sử dụng printf chúng ta thêm thư viện <stdio.h> vào main.c

include stdio

Cuối cùng retarget hàm xuất kí tự của thư viện C (fputc với Keil, _write với GCC, __write với IAR) bằng các lệnh sau:

#if defined(__CC_ARM) || defined(__ARMCC_VERSION)
/* Keil MDK: ARM Compiler 5 (__CC_ARM) và ARM Compiler 6 (__ARMCC_VERSION) */
int fputc(int ch, FILE *f) {
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
  return ch;
}
#elif defined(__ICCARM__)
/* IAR */
#include "LowLevelIOInterface.h"
size_t __write(int handle, const unsigned char *buffer, size_t size) {
  HAL_UART_Transmit(&huart1, (uint8_t *)buffer, size, HAL_MAX_DELAY);
  return size;
}
#elif defined(__GNUC__)
/* GCC: STM32CubeIDE */
int _write(int fd, char *ptr, int len) {
  HAL_UART_Transmit(&huart1, (uint8_t *)ptr, len, HAL_MAX_DELAY);
  return len;
}
#endif

 

Lưu ý: Keil MDK bản mới dùng ARM Compiler 6, trình biên dịch này cũng định nghĩa sẵn macro __GNUC__. Vì vậy phải kiểm tra __ARMCC_VERSION trước __GNUC__ như code trên, nếu không Keil sẽ biên dịch nhầm sang hàm _write và printf không in ra gì. Ngoài ra nhớ tick Use MicroLIB trong Options for Target → Target, nếu không chương trình có thể bị treo ở lệnh BKPT (semihosting) khi chạy mà không cắm debug.

OK, vậy là bây giờ chúng ta có thể sử dụng printf để in dữ liệu ra rồi.

retarget printf stm32

Kết nối UART với mạch USB-UART (PA9 – TX của STM32 nối RX của mạch USB-UART, PA10 – RX nối TX, GND nối GND) và dùng phần mềm Hercules Terminal để test nhé.

Sơ đồ đấu nối STM32 UART1 với cáp USB to UART: PA9 (TX) nối RX, PA10 (RX) nối TX, GND nối GND

Hercules terminal with uart stm32

Lưu ý: Nhớ thêm \r\n vào cuối chuỗi printf. Nhiều thư viện chỉ thực sự gửi dữ liệu khi gặp kí tự xuống dòng, và Terminal cũng cần \r\n mới xuống dòng đúng, nếu không các dòng log sẽ dính liền vào nhau.

Hoặc các bạn có thể sử dụng thư viện printf_uart, tải ở link sau:

https://github.com/nguyenkhue2608/STM32-Hal-CubeMx-and-KeilC

Kết

Printf là một hàm rất tiện lợi khi làm việc với các chuỗi. Hi vọng sau bài này các bạn sẽ biết cách sử dụng nó với STM32, giúp quá trình Debug trở nên đơn giản hơn.

Nếu bạn thấy bài viết này có ích hãy để lại bình luận và đừng quên gia nhập Hội Anh Em Nghiện Lập trình nhé.

5/5 - (2 bình chọn)

6 những suy nghĩ trên “Hướng dẫn sử dụng printf với STM32 Uart để in Log trên Keil C”

  1. Hoà nói:

    #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
    cho em hỏi sao cái dòng này của em lại bị mờ ạ, ad có thể cho em xin cách fix được không ạ

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *