前段时间在一个DSP上做音视频解码,没有操作系统 ,调试的时候又不能用仿真器狂打断点,不然同步逻辑跟时间相关不好调试,逼得自己写了个串口printf,现在把它整理出来。原来觉得printf的可变参数多神秘,这下算又搞清楚一些了。
#define swap(x, y)
{
asm( "swap %1, %0"
: "+r" (*(x)), "+r" (*(y)));
}
unsigned char printf_buffer[50] = {0};
#define MOD(x, y) ((x) - (y) * ((unsigned long)(x) / (unsigned long)(y)))
unsigned char alphabet[] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
} ;
void number(unsigned char* dst, unsigned long *offset,
long num, int base)
{
long remain, i, len;
unsigned long start;
unsigned long end;
if (num < 0)
{
num = -num;
dst[(*offset)++] = '-';
}
start = *offset;
while (num >= 0)
{
dst[(*offset)++] = alphabet[MOD(num, base)];
remain = num / base;
num = remain;
if (num == 0)
break;
}
end = *offset - 1;
len = ((end - start) + 1) / 2;
for (i = 0; i < len; i++)
swap(dst + start + i, dst + end -i);
}
int myprintf(const char* fmt, ...)
{
va_list args;
unsigned long i = 0;
char* s;
va_start(args, fmt);
while (*fmt)
{
if (*fmt == '%')
{
fmt++;
switch (*fmt)
{
case 'd':
number(printf_buffer, &i, va_arg(args, int), 10);
break;
case 'l':
number(printf_buffer, &i, va_arg(args, long), 10);
break;
case 'x':
number(printf_buffer, &i, va_arg(args, long), 16);
break;
case 's':
s = va_arg(args, char*);
while (*s)
{
printf_buffer[i++] = *s++;
}
break;
default:
break;
}
fmt++;
}
else
{
printf_buffer[i++] = *fmt++;
}
}
va_end(args);
printf_buffer[i++] = '/0';
return uart_send((const char*)printf_buffer);
}
uart_send 是串口驱动提供的发送函数.
编译器在调用函数的时候会把可变参数依次压入fmt参数后面,va_start将args赋值为fmt后一个地址,然后根据fmt里面的描述在适当的时候,依次用va_arg取得可变参数并改变args的值使其指向下一个参数的地址。