目录
[1. 代码体积优化](#1. 代码体积优化)
[2. 重定向机制不同](#2. 重定向机制不同)
[3. 性能优化](#3. 性能优化)
一、什么是重定向?怎么重定向?
在C语言中,标准库函数(如printf、scanf、getchar、putchar等)默认使用标准输入输出设备。在桌面环境中,这些通常是键盘和屏幕。但在嵌入式系统中,没有这样的标准设备,因此我们需要将标准输入输出重定向到我们拥有的硬件设备上,比如UART、USB、LCD等。
重定向的过程就是重新实现C标准库中与输入输出相关的底层函数。这些底层函数在标准库中被高级函数(如printf)调用,具体需要重写哪些函数取决于使用的编译器和C库。
以重定向printf为例:重定向printf的核心是重写C标准库的底层I/O函数,让它们输出到硬件接口(如UART)。
函数调用链: printf() → vfprintf() → fputc() / _write() → 硬件接口不同编译环境的重定向函数各不相同
编译器/库 需要重写的函数 头文件 Keil + MicroLIB fputc()stdio.hKeil + 标准库 _write()stdio.hGCC (STM32CubeIDE) _write()unistd.hIAR __write()stdio.h
#include <stdio.h>
#include "stm32f10x.h" // 根据你的芯片型号调整,这里是STM32F1系列
// 假设你使用的是USART1
// USART初始化代码应该已经在其他地方配置好
// 方法1:重写fputc(最常用)
int fputc(int ch, FILE *f) {
// 将字符发送到USART1
USART_SendData(USART1, (uint16_t)ch);
// 等待发送完成(轮询方式)
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
// 等待传输完成(可选,确保数据真正发送出去)
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
return ch;
}
// 方法2:重写fgetc(用于scanf等输入函数)
int fgetc(FILE *f) {
// 等待接收到数据
while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
// 读取接收到的数据
return (int)USART_ReceiveData(USART1);
}
#include <stdio.h>
#include "stm32f10x.h" // 根据你的芯片型号调整
// 假设已经初始化了USART1和USART2
// 方法1:使用标准FILE指针区分不同串口
int fputc(int ch, FILE *f) {
// 检查文件指针,确定输出到哪个串口
if (f == stdout) {
// 输出到USART1(stdout)
USART_SendData(USART1, (uint16_t)ch);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
else if (f == stderr) {
// 输出到USART2(stderr)
USART_SendData(USART2, (uint16_t)ch);
while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
}
else {
// 默认输出到USART1
USART_SendData(USART1, (uint16_t)ch);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
return ch;
}
二、使用MicroLIB库重定向的优势
在STM32中使用标准库函数printf时,勾选MicroLIB主要是为了减小代码体积和优化性能,具体原因如下:
1. 代码体积优化
-
MicroLIB是Keil MDK专门为嵌入式系统设计的简化C库,相比标准C库(如newlib-nano)体积更小
-
标准C库可能占用10-20KB ROM,而MicroLIB通常只需2-5KB
-
这对于Flash空间有限的STM32芯片(特别是低端型号)非常重要
2. 重定向机制不同
-
MicroLIB使用更简单的重定向机制:
-
只需要重写
fputc()和fgetc()函数 -
或重写
__stdout和__stdin相关函数
-
-
标准库可能需要更复杂的重定向:
-
需要处理更完整的文件描述符
-
可能需要重写
_write()、_read()等底层系统调用
-
3. 性能优化
-
MicroLIB针对无操作系统环境优化,去除了许多不必要的功能
-
没有线程安全锁等复杂机制
-
标准I/O缓冲区管理更简单
三、注意事项:
-
不支持浮点数格式化(
printf("%f")需要额外设置)// 在Keil中需要额外设置 // Project Options → Target → 勾选"Use MicroLIB" // 并在代码中添加: #pragma import(__use_no_semihosting) -
某些标准C函数可能缺失或不完整
-
文件操作功能有限