在Keil MDK(Keil5)中实现printf函数,需通过重定向字符输出到硬件接口(如串口),并配置工程选项。以下是完整步骤及注意事项:
一、实现步骤
1. 添加头文件与重定向函数
包含标准I/O库:
#include <stdio.h> // 提供printf函数声明
重写fputc函数(以STM32串口为例):
// 方法1:使用标准库函数(如HAL库)
int fputc(int ch, FILE *f) {
HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, 100); // 通过UART1发送字符
return ch;
}
// 方法2:直接操作寄存器(高效)[2](@ref)
int fputc(int ch, FILE *f) {
while (!(USART1->ISR & USART_ISR_TXE)); // 等待发送缓冲区空
USART1->TDR = (uint8_t)ch; // 写入数据寄存器
return ch;
}
关键:fputc是printf的底层输出函数,重定向后所有printf输出将发送至指定串口。
2. 初始化硬件接口
确保串口已初始化(波特率需与PC端一致,如115200):
void UART_Init(void) {
// 以STM32标准库为例[1](@ref)
USART_InitTypeDef USART_InitStruct = {0};
USART_InitStruct.USART_BaudRate = 115200;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_Mode = USART_Mode_Tx; // 仅发送模式
USART_Init(USART1, &USART_InitStruct);
USART_Cmd(USART1, ENABLE);
}
3. 工程配置
启用MicroLib(精简C库,减少资源占用):
点击魔术棒图标 → Options for Target → Target标签页;
勾选 Use MicroLIB;
点击OK保存。
检查链接设置:
若未启用MicroLib,需手动实现_write等系统调用。
4. 调用printf测试
printf("System Clock: %d MHz\r\n", SystemCoreClock / 1000000); // 输出系统时钟
二、常见问题及解决
1. 无输出或乱码
波特率不匹配:确保串口初始化波特率与PC端调试工具(如串口助手)一致;
硬件未初始化:确认串口时钟和引脚已配置;
发送未完成:在fputc中添加等待发送完成的循环(参考方法2)。
2. 中文乱码
Keil编码设置:
菜单栏 Edit → Configuration → Editor → 设置编码为 UTF-8;
在Misc Controls中添加 --no-multibyte-chars(防止多字节字符解析错误);
串口助手编码:选择与Keil一致的编码(如UTF-8或GBK)。
3. 代码体积过大
启用MicroLib可显著减小代码尺寸;
避免使用浮点数(如%f),改用整数运算(如%d放大100倍显示)。
三、替代方案
1. 使用sprintf格式化到缓冲区
char buffer[50];
sprintf(buffer, "ADC Value: %d\r\n", adc_value);
HAL_UART_Transmit(&huart1, (uint8_t*)buffer, strlen(buffer), 100); // 手动发送
优点:不依赖重定向,可多串口并行输出。
2. 自定义简化版printf
#include <stdarg.h>
void UART_Printf(char *format, ...) {
char buf[100];
va_list args;
va_start(args, format);
vsnprintf(buf, sizeof(buf), format, args); // 格式化到缓冲区
va_end(args);
HAL_UART_Transmit(&huart1, (uint8_t*)buf, strlen(buf), 100); // 发送
}
适用场景:需控制栈空间或避免库冲突时。
四、不同重定向方法对比

五、调试技巧
单步调试验证:在fputc函数内设断点,确认printf是否触发该函数;
检查串口引脚:用示波器测量TX引脚波形,确认数据是否发出;
简化输出内容:先发送固定字符串(如"TEST"),排除格式化参数的影响。
通过以上步骤,可稳定实现Keil5中的printf输出功能。若需更深入的优化(如降低延迟),可结合SWO或RTT等高级调试方式。
注意:最后要记得勾选Use MicroLIB微库的功能不然运行printf时会出现卡死现象.
————————————————
版权声明:本文为CSDN博主「风停了123」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/2301_79036162/article/details/148892974
|