在Keil MDK(Keil5)中实现printf
函数,需通过重定向字符输出到硬件接口(如串口),并配置工程选项。以下是完整步骤及注意事项:
#include // 提供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
输出将发送至指定串口。
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);
}
Options for Target
→ Target
标签页;Use MicroLIB
;OK
保存。_write
等系统调用。printf("System Clock: %d MHz\r\n", SystemCoreClock / 1000000); // 输出系统时钟
fputc
中添加等待发送完成的循环(参考方法2)。Edit → Configuration → Editor
→ 设置编码为 UTF-8
;Misc Controls
中添加 --no-multibyte-chars
(防止多字节字符解析错误);%f
),改用整数运算(如%d
放大100倍显示)。sprintf
格式化到缓冲区char buffer[50];
sprintf(buffer, "ADC Value: %d\r\n", adc_value);
HAL_UART_Transmit(&huart1, (uint8_t*)buffer, strlen(buffer), 100); // 手动发送
优点:不依赖重定向,可多串口并行输出。
printf
#include
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 +MicroLib |
代码简洁,支持标准printf 语法 |
依赖MicroLib,浮点数支持差 | 通用调试 |
寄存器操作 | 高效,不依赖库函数 | 需查阅芯片手册,移植性差 | 高性能或资源受限系统 |
sprintf +手动发送 |
灵活控制输出目标 | 需额外缓冲区,代码冗长 | 多串口或定制协议 |
自定义UART_Printf |
避免库依赖,节省内存 | 功能简化,不支持复杂格式 | 资源紧张的MCU |
fputc
函数内设断点,确认printf
是否触发该函数;"TEST"
),排除格式化参数的影响。通过以上步骤,可稳定实现Keil5中的
printf
输出功能。若需更深入的优化(如降低延迟),可结合SWO或RTT等高级调试方式。注意:最后要记得勾选Use MicroLIB微库的功能不然运行printf时会出现卡死现象.