彻底搞清printf
在STM32
上的使用
重定向printf
ARMCC版本(keil MDK)
下面这段代码,在实现串口发送一个字节的函数后,可以在勾不勾选”微库“的情况下都可以正常使用printf
函数。__MICROLIB
是勾选微库后会被定义的宏,因而可以通过条件编译的方式兼容。
对应文件要包含<stdio.h>
头文件,否则会提示FILE
无定义。
#if !defined(__MICROLIB)
#pragma import(__use_no_semihosting)
void _sys_exit(int x) //避免使用半主机模式
{
x = x;
}
struct __FILE
{
int handle;
};
FILE __stdout;
#endif
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
/* 实现串口发送一个字节数据的函数 */
serial_write(&serial1, (uint8_t)ch); //发送一个自己的数据到串口
return ch;
}
ARMGCC版本(GCC)
ARMGCC库对printf函数的实现和ARMCC对printf函数的实现底层是不一样的,因而重定向的底层实现也有区别。
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
/* 实现串口发送一个字节数据的函数 */
serial_write(&serial1, (uint8_t)ch); //发送一个自己的数据到串口
return ch;
}
int _write(int file, char *ptr, int len)
{
int DataIdx;
for(DataIdx = 0; DataIdx < len; DataIdx++)
{
__io_putchar(*ptr++);
}
return len;
}
注意:在使用gcc进行printf进行打印输出时要加\r\n
或\n
参数,否则有可能打印不输出。
重写printf
如果在使用多个串口的过程中都想是想用printf函数可以通过下面的方式来实现,只不过就不要使用printf的函数名了。
/* 用于实现自己的printf函数的原型 */
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
void serial2_printf( const char * format, ... )
{
char buffer[256];
va_list args;
va_start (args, format);
vsprintf (buffer,format, args);
//send_via_USART1 (buffer);
uint8_t len = strlen((const char*)buffer);
for(uint8_t i = 0; i < len; ++i)
{
/* 通过串口发送一个字节的数据 */
serial_write(&serial2, (uint8_t)buffer[i]);
}
va_end (args);
}
GCC下printf打印浮点数
默认情况下为了减小编译后的代码大小,GCC是不支持打印%f的浮点数的。需要通过添加链接指令开启-u_printf_float
,如在makefile中做如下修改便可以打印浮点数:
# printf float
USE_FLOAT_PTINT = -u_printf_float
# libraries
LIBS = -lc -lm -lnosys
LIBDIR =
LDFLAGS = $(MCU) -specs=nano.specs -T$(LDSCRIPT) $(LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections $(USE_FLOAT_PTINT)
注意:
- 无论是重定向printf函数还是重写printf函数都需要增加
-u_printf_float
编译指令后才能打印浮点数 - 在打开浮点数打印的指令后代码尺寸会比之前大 10KB左右,在flash较小的单片机上斟酌使用