自定义格式化输出函数

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');
	}
}
posted @ 2019-05-12 16:16  Acuity  阅读(207)  评论(0)    收藏  举报