Hướng dẫn retarget/redirect hàm printf để sử dụng cho UART trên KeilC

Nhiều bạn lập trình nhúng khi phát triển hoặc debug chương trình thường có suy nghĩ sử dụng các hàm trong thư viện chuẩn của C như printf() để gửi dữ liệu thông qua giao tiếp UART đến máy tính và hiển thị trên các phần mềm như Terminal, Hercules hoặc giao tiếp với các module khác…Nếu có thể sử dụng được hàm printf() thì giao tiếp UART sẽ trở nên đơn giản và quen thuộc hơn. Vậy làm thế nào để có thể sử dụng hàm printf() trong Keil C, cụ thể hơn là cho vi điều khiển lõi ARM -STM32F103C8T6. Trong bài viết này, mình sẽ gửi một số hướng dẫn đến để có thể sử dụng hàm printf() khi giao tiếp UART.

[HỌC ONLINE: LẬP TRÌNH VI ĐIỀU KHIỂN STM32, VI XỬ LÝ ARM CORTEX – M] 

  1/ Sử dụng hàm printf cho giao tiếp UART trong Keil C:

Bước 1:

Mở CubeMX chọn chip STM32F103C8 để bắt đầu một Project mới

Bước 2:

Cấu hình nạp code ở SYS sang Serial Wire

Bước 3:

  • Chọn USART1 là chế độ giao tiếp không đồng bộ
  • Chân PA10 sẽ được đặt là RX và PA9 là TX.
  • Tại tab Parameter Settings chúng ta có thể thay đổi được những thông số như Baud Rate, Word Length, Parity, Stop Bits của bộ USART1.

Bước 4:

Sau khi hoàn thành các bước trên ta tiến hành, chuyển sang tab Project Manager để cài đặt việc tạo code từ CubeMX

  • Đặt tên và chọn IDE MDK-ARM V5 để sinh code ở tab Project

  • Ở tab Code Generator chọn chỉ copy những thư viện cần thiết. Hoàn tất cài đặt và sinh code.

Bước 5:

  • Đối với các chức năng output tiêu chuẩn của thư viện chuẩn trong ngôn ngữ C, thiết bị output mặc định là trình hiển thị. Để xuất hàm printf vào cổng Serial, bạn cần chuyển output của hàm fputc sang cổng Serial (redirect).
  • Thêm các dòng trong hình dưới vào file main.c

Bước 6:

  • Thêm hàm các dòng dưới vào file main.c
  • Truyền những chuỗi kí tự bằng hàng printf.

Bước 7:

Thực hiện test hàm prinf trên Serial

  • Build và nạp chương trình vào board.
  • Kết nối 2 chân TX,RX của USART1 với USB TTL.
  • Mở phần mềm Hercules trên máy tính để hiển thị. Chọn cổng COM tương ứng và tốc độ Baud 9600. Nhấn nút Open để kết nối.

  2/ Sử dụng chế độ ngắt cho giao tiếp UART trong Keil C:

Bước 1-4: Tương tự như đã thực hiện khi sử dụng với hàm printf.

Tuy nhiên ở bước 3 chúng ta cần chuyển sang tab NVIC Settings và tick Enable Interrupt USART1

Bước 4: Hoàn tất cài đặt và sinh code

Bước 5:

Tạo 2 mảng trong file main.c:

  • aTxStartMessage : lưu dữ liệu ban đầu muốn gửi lên Serial khi khởi động board.
  • aRxBuffer : lưu dữ liệu nhận.

Bước 6:

Sử dụng module USART1 ở chế độ Interrupt cần 2 hàm TX và RX:

  • Mở tab Function, mở file stm32f1xx_hal_uart.c

  • Tìm 2 hàm HAL_UART_Transmit_IT và HAL_UART_Receive_IT.

  • Copy sang hàm main và thay đổi tham số truyền vào. Trong đó:
    • &huart1 : chỉ định khởi động module USART1
    • aTxStartMessage : bộ đệm lưu dữ liệu muốn gửi.
    • aRxBuffer : bộ đệm lưu dữ liệu nhận.
    • sizeof(aTxStartMessage) : độ dài dữ liệu muốn gửi.
    • 10 : độ dài dữ liệu muốn nhận.

Bước 7:

Thêm hàm Callback vào file main.c để gửi lên Serial sau khi nhận đủ 10 kí tự trong UART1 Interrupt.

Bước 11:

Thực hiện test chương trình

  • Build và nạp chương trình vào board.
  • Kết nối 2 chân TX,RX của USART1 với USB TTL.
  • Mở phần mềm Hercules trên máy tính để hiển thị. Chọn cổng COM tương ứng và tốc độ Baud 9600. Nhấn nút Open để kết nối.

Mở file header stm32f1xx_hal_uart.h. Ở cuối file, bạn có thể thấy hàm serial với một số IO functions dưới đây.

TX/RX function của USART gồm:

  • HAL_UART_Transmit(); truyền dữ liệu theo mode polling, sử dụng quản lý timeout.
  • HAL_UART_Receive(); nhận dữ liệu theo mode polling, sử dụng quản lý timeout.
  • HAL_UART_Transmit_IT(); truyền dữ liệu theo mode interrupt.
  • HAL_UART_Receive_IT(); nhận dữ liệu theo mode interrupt.
  • HAL_UART_Transmit_DMA(); truyền dữ liệu theo mode DMA.
  • HAL_UART_Receive_DMA(); nhận dữ liệu theo mode DMA.
  • HAL_UART_TxHalfCpltCallback(): Khi truyền một nửa dữ liệu thành công, nó sẽ được gọi bằng chức năng xử lý ngắt.
  • HAL_UART_TxCpltCallback(): Khi truyền dữ liệu kết thúc, nó sẽ được gọi bằng chức năng xử lý ngắt.
  • HAL_UART_RxHalfCpltCallback(): Khi nhận một nửa dữ liệu thành công, nó sẽ được gọi bằng chức năng xử lý ngắt.
  • HAL_UART_RxCpltCallback (): Khi nhận dữ liệu kết thúc, nó sẽ được gọi bằng chức năng xử lý ngắt.
  • HAL_UART_ErrorCallback (): Khi có lỗi trong quá trình truyền dữ liệu, nó sẽ được gọi bằng chức năng xử lý ngắt.

Có 3 chế độ giao tiếp để truyền Serial: Polling, Interrupt, DMA.

  • Chế độ Polling: CPU liên tục truy vấn thiết bị IO và xử lý yêu cầu từ thiết bị.
  • Chế độ Interrupt: Khi hoạt động IO kết thúc, bộ điều khiển thiết bị IO truyền tín hiệu ngắt tới bộ xử lý qua đường IRQ. Quá trình nhận tín hiệu và thoát chương trình xử lý ngắt để giải quyết việc truyền dữ liệu.
  • DMA: Truy cập trực tiếp bộ nhớ cho phép khối dữ liệu được truyền giữa bộ nhớ và thiết bị IO mà không cần can thiệp trung gian của CPU. Nó chỉ yêu cầu CPU gửi lệnh khối truyền dữ liệu đến thiết bị ngay từ đầu và sử dụng ngắt để đánh giá việc truyền tải có hoàn tất hay không và hoạt động tiếp theo đã sẵn sàng hay chưa.

Chúc các bạn thành công!

Xem thêm: Tổng hợp các bài hướng dẫn Lập trình vi điều khiển STM32 tại đây.

Nhóm Tapit ARM R&D