printf输出重定向的方法

描述

前言

在 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 来设置字节宽度:

  • b - 8位
  • h - 16bit (默认)
  • l - 32位

在 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语言的弱函数相关内容,这里不再展开来讲,感兴趣的小伙伴可自行查阅相关资料。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分