3. 实现串口驱动并重定向printf
任务目标
在嵌入式目标板上实现串口(UART)底层驱动,并将C标准库的printf输出重定向到该串口,使调试信息能够通过串口工具打印到PC终端。本案例以STM32F103系列MCU和STM32CubeIDE环境为例,给出最小可复现实现。
硬件与依赖
- 硬件:STM32F103最小系统板(USART1,PA9=TX,PA10=RX),USB转TTL模块
- 软件:STM32CubeIDE(含HAL库),串口终端工具(如PuTTY、MobaXterm)
- CubeMX初始化:USART1异步模式,波特率115200,8数据位,1停止位,无校验
关键完整代码
在CubeMX生成的工程基础上,核心实现分为两部分:串口发送函数封装,以及printf重定向。以下代码直接添加到usart.c或main.c中(需包含stdio.h)。
/* 在 usart.c 或 main.c 中添加 */
/* 1. 串口发送单个字节(HAL库底层已实现,此处仅做重定向接口) */
int _write(int file, char *ptr, int len)
{
HAL_UART_Transmit(&huart1, (uint8_t *)ptr, len, HAL_MAX_DELAY);
return len;
}
/* 2. 若使用MicroLIB,则改用以下实现(二选一,不可同时启用) */
#if 0
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
return ch;
}
#endif
/* 3. 在 main() 中调用示例 */
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
/* 重定向后,printf 输出将发送到 USART1 */
printf("UART printf redirect OK\r\n");
printf("System Clock: %d MHz\r\n", (int)HAL_RCC_GetSysClockFreq() / 1000000);
while (1)
{
printf("Hello from STM32, tick: %lu\r\n", HAL_GetTick());
HAL_Delay(1000);
}
}
工程配置要点
- MicroLIB设置:在工程属性 → C/C++ Build → Settings → MCU Settings 中勾选
Use MicroLIB。若使用标准库,则必须实现_write函数(上述代码已提供);若使用MicroLIB,则需改用fputc实现(代码中#if 0部分)。 - 头文件:确保包含
#include <stdio.h>。 - 中断优先级:本案例使用阻塞式发送(
HAL_MAX_DELAY),无需配置中断。若需非阻塞发送,可改用中断或DMA方式,但不在本案例范围内。
运行与验证
- 使用USB转TTL模块连接:
GND→GND,TX→PA10(STM32的RX),RX→PA9(STM32的TX)。 - 打开串口终端,选择对应COM口,设置波特率115200、8N1。
- 编译并烧录程序,复位开发板。
- 通过条件:终端窗口每秒打印一次
Hello from STM32, tick: N(N为递增的毫秒计数值),且首次上电时打印两行初始化信息。若能看到上述输出,说明串口驱动和printf重定向均正常工作。
常见问题
- 无输出:检查USB转TTL模块的TX/RX是否交叉连接;确认波特率设置一致;检查PA9/PA10是否被其他外设占用。
- 输出乱码:多为波特率不匹配或时钟配置错误导致,检查CubeMX中USART1的波特率配置与实际终端设置。
- 编译报错
_write未定义:若使用MicroLIB,请改用fputc实现;若使用标准库,确认已包含stdio.h且函数签名正确。