STM32Cube HAL 庫提供的 HAL_UART_Transmit/Receive 系列函數需預知數據長度,無法直接支援 printf 這類流式輸出,也無單字節收發介面。本文給出最小改動方案:重寫 fputc 實現 printf,並手寫中斷服務函數實現單字節接收回顯。
在 main.c 或獨立文件添加:
#include <stdio.h>
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE {
// 等待發送完成標誌(阻塞式,不開啟 TX 中斷)
huart1.Instance->DR = (uint8_t)ch;
while (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TC) == RESET);
return ch;
}
注意:必須關閉 UART 發送中斷(TXEIE),否則進入死等待。CubeMX 生成代碼默認未開啟 TX 中斷,滿足條件。
USART1_IRQHandlervoid USART1_IRQHandler(void) {
if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) == SET) {
static uint8_t buf[100], cnt = 0;
buf[cnt] = (uint8_t)(huart1.Instance->DR & 0xFF); // 讀取數據寄存器自動清標誌
huart1.Instance->DR = buf[cnt]; // 回顯發送
if (++cnt >= 100) cnt = 0;
}
}
HAL_UART_MspInit 中)void HAL_UART_MspInit(UART_HandleTypeDef* huart) {
if (huart->Instance == USART1) {
// ... GPIO、時鐘、NVIC 初始化 ...
HAL_NVIC_SetPriority(USART1_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
// 關鍵:手動置位 RXNEIE 位
huart->Instance->CR1 |= USART_CR1_RXNEIE;
}
}
void LED_Task2(void const * argument) {
while (1) {
HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_14);
printf("LED_Task2\r\n");
osDelay(2000);
}
}
| 功能 | 關鍵點 |
|---|---|
| printf 輸出 | 重寫 fputc/__io_putchar,阻塞輪詢 TC 標誌,不使用 HAL 發送函數 |
| 單字節接收 | 在 IRQHandler 讀 DR 寄存器,手動啟用 RXNEIE,不使用 HAL_UART_Receive_IT |
| 衝突避免 | 禁用 HAL 發送中斷,禁用 HAL 接收中斷回調,完全接管 UART 硬件 |