做嵌入式开发的朋友都知道,要获取正在调试的代码运行情况的相比PC开发要难得多.首要问题是数据显示的通路少,其次是嵌入式C开发经常发现很多基本的C运行库函数都没有在所使用的平台实现,即使有,也可能庞大得吓人。今天要DIY的就是PC平台使用最广泛的printf函数。参考了部分网上程序,从代码里有注明.
printf()函数在有的单片机开发环境下往串口发送数据,若你使用的环境恰好有这么个函数功能,那我很羡慕。没有也没关系,今天我们就来实现它这里要介绍实现的printf函数,也是往串口发送数据的,当然,要改变所发送的端口是轻而易举的,后面从代码里就很容易看出来了,实际上,我还在VS2008上模拟了这个函数,其中就将其改成发送到电脑屏幕上。区分标准器printf起见,我们将其命名为myprintf()。
下面先来看一下函数声明。
int myprintf(const char *fmt, ...);
这是一个可变参数,不明白的朋友可以Google一下,或在VS帮助文档里检索va_list,va_arg, va_end, va_start都行.
接下来贴出函数体
/********************************************
*输出到串口
* 只支持以下格式输出:
* s:字符串
* d:十进制
* x:十六进制
* b:二进制
* f:默认输出3位精度小数(没有做四舍五入)
*注意:不能转换太大的数/精度要求太高的数,以防溢出错误
* f:在vs2008中不能通过,
*作者:王桂杰,参考了一个网上程序
********************************************/
int myprintf(const char *fmt, ...)
{
#define DEFAULT_PRECI 3
const char *s;
int value;
int preci = DEFAULT_PRECI;
float fdata;
char buf[16];
va_list ap;
va_start(ap, fmt);
while (*fmt) {
if (*fmt != '%') {
term_putc(*fmt++);
continue;
}
switch (*++fmt) {
case 's':
s = va_arg(ap, const char *);
for ( ; *s != '/0'; s++) {
//putchar(*s);
term_putc(*s);
}
break;
case 'd': //Signed decimal integer
value = va_arg(ap, int); //不同编译器/CPU位数,类型可能不同,uint8_t,uint16_t,uint32_t等
itoa(value, buf, 10);
for (s = buf; *s != '/0'; s++) {
//putchar(*s);
term_putc(*s);
}
break;
case 'x': //Unsigned hexadecimal integer
value = va_arg(ap, int);
term_puts_P("0x");
term_puthex_byte(value);
break;
case 'b': //二进制打印
value = va_arg(ap,int);
term_putbin_byte(value);
term_putc('b');
break;
case 'f': //浮点数输出,默认精度为3位小数
//fdata = va_arg(ap, float);//MSP430F149(16位CPU),IAR平台可以,
fdata = va_arg(ap, double);//windows默认存储方式是double类型,使用float将获不到正确结果(vs2008,64位CPU)
ftoa(fdata, buf, DEFAULT_PRECI);
for (s = buf; *s != '/0'; s++) {
//putchar(*s);
term_putc(*s);
}
break;
case '.': //浮点数精度控制
if(isdigit(*++fmt)){
preci = *fmt - '0';
if(*++fmt == 'f'){
//fdata = va_arg(ap, float);
fdata = va_arg(ap, double);
ftoa(fdata, buf, preci);
for (s = buf; *s != '/0'; s++){
//putchar(*s);
term_putc(*s);
}
break;
}
preci = DEFAULT_PRECI;
}
term_putc(*fmt);
break;
/* 在下面增加其他格式 */
default:
//putchar(*fmt);
term_putc(*fmt);
#if 0
s = "UNSURPORT FORMAT!";
for( ; *s != '/0'; s++){
term_putc(*s);
}
va_end(ap);
return 0;
#endif
}
fmt++;
}
va_end(ap);
return 1; /* Dummy return value */
}
上面是函数是用于MSP430F149 IAR开发环境的,很用于看出,term_putc即是往单片机串口发送数据的。可想而知,在使用这个函数前应该先初始化串口,抽象出term_putc接口,本文就不涉及这个问题了。下面给出的就是通过#define term_puc putchar 偷梁换柱,在PC上模拟的程序.
/********************************************
* 只支持以下格式输出:
* s:字符串
* d:十进制
* x:十六进制
* b:二进制
* f:默认输出3位精度小数
*注意:不能转换太大的数/精度要求太高的数,以防溢出错误
*作者:王桂杰 参考了网上一个程序
********************************************/
#define WINDOWS_VS2008
int myprintf(const char *fmt, ...)
{
#define DEFAULT_PRECI 3
#ifdef WINDOWS_VS2008
#define term_putc putchar //用于vs2008测试
#endif
const char *s;
int value;
int preci = DEFAULT_PRECI;
float fdata;
char buf[32];//char buf[16];
va_list ap;
va_start(ap, fmt);
while (*fmt) {
if (*fmt != '%') {
//putchar(*fmt++);
term_putc(*fmt++);
continue;
}
swit