自定义格式化输出函数
1.写在前面
在嵌入式开发过程中,经常需用到格式化打印函数进行打印、调试源码,即C库中的“printf”函数。printf可以重定向到串口(UART)、USB输出,从而输出至调试终端打印,如串口助手、Scure CRT等。对于大部分常见应用情况,只要重定向printf输出函数即可,而特殊情况下,可能需自定义实现格式化函数;比如为节约资源使用,没有使用到全部printf功能,此时只需实现部分所需要的格式化功能。
2.标准格式化输出
C库标准格式化函数是“printf”,可以输出到显示终端、文件、重定向到处理端口等,可以自定义字符、数字、进制、长度输出。
常用输出格式有:
| 控制字 | 涵义 |
|---|---|
| %d | 以十进制有符号整型数输出 |
| %ld | 以十进制有符号长整型数输出 |
| %nd | 指定输出字段的宽度(n),如果数据的位数小于 n,则左端补以空格;否则按实际位数输出 |
| %u | 以无符号整型输出 |
| %f | 以浮点数输出 |
| %.nf | 以浮点数输出,小数点后保留n位,注意 n 前面有个点 |
| %o | 以八进制整型数输出 |
| %x | 以十六进制整型数输出,小写字母表示 |
| %X | 以十六进制整型数输出,大写字母表示 |
| %c | 字符输出 |
| %s | 字符串输出 |
3.自定义格式化输出
3.1 C语言函数可变形参
由于使用到了可变形参问题,C库提供了一组解决该问题的“宏”——va_list,该宏位于“stdarg.h”头文件 中,因此使用时需先包函该头文件。va_list宏有几个关键的宏调用。
| 宏 | 涵义 |
|---|---|
| va_start | 获取可变参数列表的第一个参数的地址 |
| va_arg | 获取可变参数的当前参数,返回指定类型并将指针指向下一参数 |
| va_end | 清空可变参数列表 |
va_list宏使用比较简单,基本步骤可以归纳为几点。
【1】函数里定义va_list型的指针变量,指向函数形参;
【2】调用va_start初始化va_list变量;
【3】依次调用va_arg返回可变的参数,va_arg的第二个参数为待获取参数的类型;
【4】调用完毕需调用va_end清空va_list。
3.2 格式函数
标准格式化函数功能强大,而一般情况下,常用的格式基本是字符、字符串、整型、十六进制等,因此自定义暂时实现常规的格式。
/**
* @brief 格式化输出,暂时支持 %c、%d、%s、%u、%x 、%X
* @param put:重定向输出(如UART)
* @retval none
*/
void uart_debug_printf(int (*put)(char data),char *str, ...)
{
va_list va_print;
uint32_t va_temp,temp,base_value;
char ch,ch1;
char *pstr;
if(put == 0)
return;
va_start(va_print, str);
while(*str)
{
for(; (*str != '%') && (*str != '\0'); str++)
{
(*put)(*str);
}
if(*str == '%')
{
str++;
switch(*str)
{
case 'd':
case 'u':
va_temp = va_arg(va_print, uint32_t);
base_value = 1;
if(va_temp == 0)
{
(*put)('0');
break;
}
if(*str == 'd')
{
if((int)va_temp < 0)
{
va_temp = -(uint32_t)va_temp;
(*put)('-');
}
}
temp = va_temp;
while(temp)
{
temp /= 10;
if(temp)
base_value *= 10;
}
temp = va_temp;
while(base_value)
{
ch = temp / base_value;
temp %= base_value;
base_value /= 10;
(*put)(ch + '0');
}
break;
case 'x':
case 'X':
va_temp = va_arg(va_print, uint32_t);
base_value = 8;
ch1 = 0;
while(base_value--)
{
ch = (va_temp & 0xf0000000) >> 28;
va_temp <<= 4;
if((ch==0) && (ch1==0))
{
continue; /* 左边0不打印 */
}
if(ch!=0)
{
ch1=1;
}
if(ch <= 9)
{
(*put)(ch + '0');
}
else
{
if(*str == 'x')
(*put)(ch - 10 + 'a');
else
(*put)(ch - 10 + 'A');
}
}
break;
case 'c':
va_temp = va_arg(va_print, uint32_t);
(*put)(va_temp);
break;
case 's':
pstr = (char *)va_arg(va_print, char *);
for(; *pstr != '\0'; pstr++)
{
(*put)(*pstr);
}
break;
case '%':
(*put)(*str);
break;
default:
break;
}
str++;
}
}
va_end(va_print);
}
另外对于十进制数整型的输出,也可以考虑递归函数,会比较容易理解,但会占用一定栈空间。
void printf_dec(int(*put)(char data), int value)
{
if(value > 10)
{
printf_dec(put, value/10);
value %= 10;
}
else
{
(*pur)(put, value + '0');
}
}

浙公网安备 33010602011771号