printf的格式化输出可以输出一下几种格式化类型:
%a(%A) 浮点数、十六进制数字和p-(P-)记数法(C99)
%c 字符
%d 有符号十进制整数
%f 浮点数,.xf表示保留几位小数
%e(%E) 浮点数指数输出[e-(E-)记数法]
%g(%G) 浮点数不显示无意义的0
%i 有符号十进制整数(%d)
%u 无符号十进制整数
%o 八进制整数
%x(%X) 十六进制整数
%p 内存地址
%s 字符串
%% "%"
其中%#x等,即加入#号表示会有格式存在(单位),如打印16进制会自动加上0x...
printf函数就是通过一次一次调用fputc函数完成对字符的格式化输出,调用一次就输出1字节ASCII码值。
printf为默认屏幕输出,而sprintf为标准的输出流,sprintf(char * p,format[a,b,c,])可以选择格式化输出到哪个内存当中,再通过其他方式读取出来,如LCD\OLED屏幕显示格式化的实时数据。
本文章介绍两种方法:
1.1第一种:使用微库
如下图所示,使用keil的微库。
使用微库可直接重定义fputc即可,其中fputc可加入预编译条件来选择哪个串口来进行printf输出,实际需求按如下代码修改即可:
/* MDK下需要重定义fputc函数, printf函数最终会通过调用fputc输出字符串到串口 */
int fputc(int ch, FILE *f)
{
while ((UART1->STATUS & (1<<1)) == 0); /* 等待上一个字符发送完成 */
UART1->TDR = (uint8_t)ch; /* 将要发送的字符 ch 写入到DR寄存器 */
return ch;
}
1.2不使用微库:使用半主机模式
不使用微库代码较为复杂,如下所示:
/******************************************************************************************/
/* 加入以下代码, 支持printf函数, 而不需要选择use MicroLIB */
#if 1
#if (__ARMCC_VERSION >= 6010050) /* 使用AC6编译器时 */
__asm(".global __use_no_semihosting\n\t"); /* 声明不使用半主机模式 */
__asm(".global __ARM_use_no_argv \n\t");
#else
/* 使用AC5编译器时, 要在这里定义__FILE 和 不使用半主机模式 */
#pragma import(__use_no_semihosting)
struct __FILE
{
int handle;
/* Whatever you require here. If the only file you are using is */
/* standard output using printf() for debugging, no file handling */
/* is required. */
};
#endif
/* 不使用半主机模式,至少需要重定义_ttywrch\_sys_exit\_sys_command_string函数,以同时兼容AC6和AC5模式 */
int _ttywrch(int ch)
{
ch = ch;
return ch;
}
/* 定义_sys_exit()以避免使用半主机模式 */
void _sys_exit(int x)
{
x = x;
}
char *_sys_command_string(char *cmd, int len)
{
return NULL;
}
/* FILE 在 stdio.h里面定义. */
FILE __stdout;
/* MDK下需要重定义fputc函数, printf函数最终会通过调用fputc输出字符串到串口 */
int fputc(int ch, FILE *f)
{
while ((UART1->STATUS & (1<<1)) == 0); /* 等待上一个字符发送完成 */
UART1->TDR = (uint8_t)ch; /* 将要发送的字符 ch 写入到DR寄存器 */
return ch;
}
#endif
/******************************************************************************************/
可加入条件编译实现通过宏定义定义使用哪一个串口作为printf的输出