本节介绍如何重定向printf输出到串口输出的多种方法,包括调用MDK微库(MicroLib)的方法,调用标准库的方法
开发环境:CubeMX+MDK5.27
芯片型号:STM32F103ZET6
时间:2020/07/14
Printf和fputc:
相信学过C语言的你一定不会对printf感到陌生,因为每个程序员的第一个程序就是printf(“Hello World”);
我们可以用printf打印各种各样的数据,无论是数字还是字符串,甚至是图形。
而在嵌入式开发的过程中,我们经常需要调试进行debug,而调试时涉及到数据的地方,我们就可以通过printf观察数据是否正确,这也是嵌入式开发的调试技巧。在电赛中,做到测量题时就可以将测量的数据用串口打印出来,通过串口助手观察数据变化,并生成excel表格,再使用MATLAB去分析数据之间的线性关系。
这么强大的printf,你了解多少呢?下面就简单介绍一下printf的来龙去脉。
在学习C语言时,我们写的第一句就是#include
int __cdecl printf(const char * __restrict__ _Format,...);
printf函数会根据_Format字符串给出的格式打印输出到stdout中,在输出时printf会调用更加底层的I/O函数fputc去逐个字符打印,fputc也在stdio.h中定义过,格式如下
int __cdecl fputc(int _Ch,FILE *_File);
在fputc函数中,会将传入的参数 _Ch写入给定输出流_File,printf函数调用fputc函数时会向_File参数传入stdout从而打印数据到标准输出。
以上就是printf实现的整个流程,那么我们想要通过单片机的串口实现printf就很简单明了,只需要重新定义fputc函数,在fputc中将数据通过串口发送就实现了串口printf。业内把这个过程称为fputc重定向或printf重定向。
说了这么多,下面开始实战!
实现前提:完成串口的配置,并且测试收发功能正常。
实现步骤
第一步、打开MDK并打开Options for Target窗口(点击锤子图标),勾选Use MicroLib
MircoLib是对标准的C库进行了高度优化的库,供给MDK默认使用,相比于标准的C库,MicroLib的代码更少,资源占用也更少。
第二步、重写fputc函数
重新编写fputc函数,编写代码将字符通过串口发送,这里我们提供两种方法,在结尾会对比分析两种方法的优劣
①调用库函数发送字符串
本代码编写在usart.c下**
#if 1
#include <stdio.h>
//重定向c库函数printf到串口1,重定向后可使用prinf打印到串口
int fputc(int ch, FILE *File)
{
HAL_UART_Transmit(&htim2,(uint8_t*)&ch, 1, 0xFFFF);
return (ch)
}
#endif
②使用寄存器编程发送字符串
本代码编写在usart.c下
#if 1
#include <stdio.h>
int fputc(int ch, FILE *File)
{
//判断串口1是否堵塞,即数据是否发送完
while((USART1->SR & 0X40) == 0);
//串口发送完成,发送该ch字符
USART1->DR = (uint8_t)ch;
return ch;
}
#endif
以上两种方式都可以实现printf到串口,但方式②更具优点,同时不建议使用方式①,因为方式②采用的寄存器编程是直接操作硬件,速度和效率会比方式①调用库函数更高,因此建议使用方式②的写法!
第三步,重写完fputc后,我们就可以在main.c中使用printf函数随心所欲的打印了,但要注意两个点。
第一:记得在main.c里添加#include
第二:不要在串口初始化函数之前就调用printf,这样硬件还没初始化,你调用printf也无意义,还有可能出现莫名其妙的问题。
总结:串口通信是嵌入式开发中最最最常用的一种通信方式,因为它接线很简单,只有TX,RX,VCC,GND四根线,并且没有那么多的难以理解的问题,在调试模块时,最容易调试的就是串口通信的模块了,个人是这样觉得。到后面,会接触到更加多的通信方式,例如IIC通信、SPI通信、OneWire通信、CAN通信等。在这之前把串口通信好好掌握,会帮助你更好的理解后面的各种通信方式,其次在嵌入式开发中,好好利用printf来调试自己的系统是很有必要的,很多时候你没办法通过屏幕直接观察到现象,这个时候不妨用串口printf一下,简单又高效!
