前言
在 PC 上运行 C 语言时,prinf 输出的内容会打印在电脑显示器上,这是因为 prinf 默认的输出设备就是显示器。 而当我们在单片机上,需要通过 printf 函数将信息打印到串口,就要对 printf 函数的输出进行重定向。
printf 输出重定向的方法
printf 函数声明如下:
int printf(const char *format, ...);
printf 函数根据 format 字符串给出的格式打印输出到 stdout(标准输出)中,当然,printf 函数是不会一个字符一个字符去输出,它会调用更底层的 I/O 函数去逐个字符打印。
printf 是库函数,不同编译器对 C库的底层实现机制是不同的,因此 printf 中调用了哪个底层 I/O 函数来输出字符,需要根据当前使用的编译器来确定。
我们实现 printf 输出重定向的方法就是找到当前使用的编译器中,printf 调用了哪个底层 I/O 函数来输出字符,再改写该函数,将字符通过串口输出。
如何确定输出字符的底层 I/O 函数?
以 Keil 为例,点击菜单栏 Help ==> μVision Help 选项,打开帮助文档。
如果你是51单片机项目,那么使用的编译器是 Keil C51,打开的就是 C51 的帮助文档; 如果你是 ARM 单片机项目,那么使用的编译器是 Keil MDK,打开的就是 ARM 的帮助文档。
查找 printf 关键字,可以看到 C51 的 printf 底层是调用 putchar 函数实现字符输出的:
而 ARM 的 printf 函数底层是调用 fputc函数实现字符输出的:
从上述的结果可知,要想通过 printf 向串口打印调试信息,C51 单片机需要改写 putchar( ) 函数,而 ARM 单片机则需要改写 fputc( ) 函数。
C51 和 ARM 项目中,printf 输出重定向的方法是不一样的,这就是有些人把 STM32 的 printf 搬到 C51 中会出错的原因之一。
C51 重定向 printf 输出的注意事项
C51 重定向 printf 函数的输出到串口,需要改写 putchar 函数,伪代码如下:
char putchar (char ch)
{
SBUF0 = ch;
while( !(SCON0 & (1<<1)));
SCON0 &=~(1<<1);
return 0;
}
使用 printf 函数前,需要包含
51单片机重定向 printf 函数后,如果直接像 PC机或者是 32位单片机那样使用 %d 占位符打印数值,输出的数值可能是错误的,例如下面的代码输出结果可能就是不正确:
int i = 10;
printf("%d", i);
Keil 中扩展了 b、h、l 来设置字节宽度:
在 Keil C51中,用 printf 输出一个单字节变量时,要使用%bd,例如:
unsigned char x = 'A';
printf("x: %bd\\n", x);
这些内容在 Keil C51 帮助文档关于 printf 的章节中有提到:
扩展知识
不知道有没小伙伴发现,如果项目中没有重写 putchar 或 fputc 函数,直接调用 printf 也不会报错,只是 printf 打印的内容不知道输出到哪里罢了。
printf 函数里面调用了更加底层的 putchar 或 fputc 函数而没有报错,说明在 C库里面已经实现了 putchar 或 fputc 函数,那为什么我们在 C库外重新实现 putchar 或 fputc 函数时,编译器没有报重复定义的错误呢?
这是因为在 C库里,putchar 或 fputc 函数被定义成了弱函数(weak),当你定义了 putchar 或 fputc 函数,那么编译时就使用你定义的函数,否则就使用 C库中的 putchar 或 fputc 函数。
关于 C语言的弱函数相关内容,这里不再展开来讲,感兴趣的小伙伴可自行查阅相关资料。
全部0条评论
快来发表一下你的评论吧 !