单片机 printf 移植过程

在网上看到有人在单片机上使用printf函数,心痒痒,也想用,看了网上的一些方法,
大都是51单片机的,说的是在51单片机编译软件库中有自带printf函数,也有自己实现
一个自己的printf函数的,但是讲的不详细,看了好几个博客,东拼西凑,也在自己的
单片机上弄出来了。

1,先进行用到的知识讲解:
宏va_start(ap,fmt) 、 va_end(ap) 、va_arg(A,T) 以及类型定义va_list 主要用于获取 printf() 的参数,因为 printf() 参数数量可变,
// 参考 http://blog.csdn.net/lingran07/article/details/7680888
在我的编译器安装路径中找到文件 stdarg.h, 里面有如下定义:
#define va_arg(ap, T) \
((ap) += _Bnd(T, _AUPBND), *(T __near *)((ap) - _Bnd(T, _ADNBND)))
#define va_end(ap) (void)0
#define va_start(ap, A) \
(void)((ap) = (char __near *)&(A) + _Bnd(A, _AUPBND))
#define _Bnd(X, bnd) (sizeof (X) + (bnd) & ~(bnd))
在另一文件yvals.h 中找到 #define _AUPBND 1U   // 1U 即 1,后面的U是 unsigned 的意思
因此,可以得到 #define va_arg(ap, T)  ((ap) += sizeof(T), *(T*)((ap) - sizeof(T))
((ap) += sizeof(T), *(T*)((ap) - sizeof(T)) 是一个逗号表达式,功能是先将指针 ap 指向下一个参数,然后取出当前的参数数据。
#define va_start(ap, A) (void)((ap) = (char *)&A + sizeof(A)) 功能是将 ap 指向第二个参数,例如 printf("%s\n", "hello world"); 第一个参数"%s\n", 第二个参数"hello world"


2,下面贴出代码。(只需要确定存在文件stdarg.h ,实现针对特定芯片的函数 void Uart1WriteByte(unsigned char databyte),下面的代码即可直接使用)
主要参考;
http://blog.csdn.net/googlemi/article/details/8996605
http://blog.sina.com.cn/s/blog_6dee515f0100v75r.html


/*
* stdarg.h 针对特定芯片定义的头文件,主要定义了va_start(ap,fmt);  va_end(ap); va_arg(A,T); 
* 特定芯片是因为有的芯片栈是自下而上,有的相反
*/
#include   


typedef char *va_list;


/*
* 将整形数字转换成字符,如 0 转换成 '0'
*/
char number2char(int num) 
{
char ch;

switch(num)
{
case 0: ch = '0'; break;
case 1: ch = '1'; break;
case 2: ch = '2'; break;
case 3: ch = '3'; break;
case 4: ch = '4'; break;
case 5: ch = '5'; break;
case 6: ch = '6'; break;
case 7: ch = '7'; break;
case 8: ch = '8'; break;
case 9: ch = '9'; break;
case 10: ch = 'A'; break;
case 11: ch = 'B'; break;
case 12: ch = 'C'; break;
case 13: ch = 'D'; break;
case 14: ch = 'E'; break;
case 15: ch = 'F'; break;

default: ch = '0'; break;
}

return ch;
}


/*
* 将整数 ch 按照十进制/十六进制转换成字符, 因为我的单片机8bit够用了,所以使用unsigned char ch
*/
char * convert_func(char *str, unsigned char ch, int num) 
{
char *pstr = str;
int bw, sw, gw;
int hw, lw;


switch(num)
{
case 10:
if(bw = ch/100)
*pstr++ = number2char(bw);

if(sw = (ch%100)/10)
*pstr++ = number2char(sw);
else if(bw)   // 如果百位非零,而十位为零,则将十位(零)输出,下同
*pstr++ = number2char(0);

if(gw = ch%10)
*pstr++ = number2char(gw);
else// if(bw||sw)  // 如果个位为零,无论百位十位是否为零,均将个位输出
*pstr++ = number2char(0);


break;
case 16:
*pstr++ = '0';
*pstr++ = 'x';

if(hw = ch/16)
*pstr++ = number2char(hw);
else
*pstr++ = number2char(0);

if(lw = ch%16)
*pstr++ = number2char(lw);
else// if(hw)
*pstr++ = number2char(0);

break;
default:
break;
}


return pstr;
}


/*
* 将 const char *fmt 和 va_list args 转换成一个字符串,指针 buf 指向该字符串
* 例如 printf("hello %s\n", "world"); 此函数将 "hello %s\n" 和 "world" 组合成 "hello world\r\n"
* 此函数参考 http://blog.sina.com.cn/s/blog_6dee515f0100v75r.html 删减改写而成
*/
int my_vsprintf(char *buf, const char *fmt, va_list args)
{
int i;
char * str;
char *s;
unsigned char ch;


for (str=buf ; *fmt ; ++fmt) 
{
if (*fmt == '\n') // 如果是换行符,转换成回车符+换行符
{
*str++ = '\r';
*str++ = '\n';
continue;
}

if (*fmt != '%') // 如果不是'%',保存进str,如果不是,越过取下一个字符,见switch (*fmt)
{
*str++ = *fmt;
continue;
}

fmt++;


switch (*fmt)  // 这样只能解析%c, %s, %x, %X, %d. 像 %02d 这种可以自己扩展
{
case 'c':
*str++ = (unsigned char) va_arg(args, unsigned char);  // 返回args所指向参数,然后args指向下一个参数,由va_start(ap,fmt); 将args指向第二个参数,然后逐个往后取,下同
break;


case 's':
s = va_arg(args, char *);
while(*s != '\0')
*str++ = *s++;

break;


case 'x':
case 'X':
str = convert_func(str, va_arg(args, unsigned char), 16);
break;


case 'd':
str = convert_func(str, va_arg(args, unsigned char), 10);
break;


default:
break;
}
}

*str = '\0';

return str-buf;
}


/*
* 将字符串 unsigned char *string 逐个通过串口输出
* 参考:http://blog.csdn.net/googlemi/article/details/8996605
*/
void sendstring(unsigned char *string) 
{  
    while(*string!='\0')//判断是否到字符串末尾   
    {  
        UartWriteByte(*string);   // 串口输出函数,即很多博客中提到的putchar();与实际硬件相关,我的函数定义:void Uart1WriteByte(unsigned char databyte)
        string++;  // 指向下一个字符
    }  
}  




/*
* printf函数,例如:Uart_printf("hello %s\n", "world");
* 参考:http://blog.csdn.net/googlemi/article/details/8996605
*/
void Uart_printf(char *fmt,...)
{
va_list ap;   // typedef char *va_list;
unsigned char string[128];


va_start(ap,fmt);  
my_vsprintf(string,fmt,ap);
va_end(ap);
sendstring(string);  
}




参考:
1,http://blog.csdn.net/lingran07/article/details/7680888
2,http://blog.csdn.net/lanyzh0909/article/details/5698175
3,http://blog.sina.com.cn/s/blog_3f75e2c40100x4nj.html
4,http://blog.sina.com.cn/s/blog_6dee515f0100v75r.html
5,http://blog.csdn.net/googlemi/article/details/8988567#1536434-tsina-1-80220-66a1f5d8f89e9ad52626f6f40fdeadaa
6, http://blog.csdn.net/googlemi/article/details/8996605

你可能感兴趣的:(单片机)