1、printf函数的移植思路
- printf函数可以分为纯软件层和硬件相关层
- 纯软件层:
- 负责printf函数的参数解析,包括字符串的格式化和匹配等,比如:%d、%p
- 纯软件层是和硬件无关,所以移植printf函数时不需要修改纯软件层的
- 硬件相关层:
- 将字符发生出去的具体方式,比如:可以通过uart输出字符串,可以通过屏幕屏幕输出字符串
- 在移植的平台中,你想把字符显示到哪里,就把底层发送字符的函数替换成相应的发送函数
- printf函数的工作流程:
- printf函数是可变参数,printf函数首先会进行字符串匹配,就是处理“%d、%p…”
- 解析好字符串后,是以字符为单位进行发送的
- 想要把打印显示在哪里,就把字符发送函数重定位到哪里
- 总结:纯软件层不需要修改,只需要替换底层的字符发送函数,也就是根据硬件平台替换putc函数
2、移植printf函数的步骤
2.1、printf函数源码获取
- 去linux内核源码、C标准库的源码中裁剪出printf函数源码——适合有经验的
- 直接移植别人现成的printf函数源码,只需要根据硬件平台替换putc函数——适合新手
- 注意:gcc标准库里已经实现printf函数,如果你要使用自己实现的printf函数,添加编译参数“-nostdlib、-nostdin”,不使用C标准库和头文件,防止冲突;或者向我的源码一样,不要使用printf这个名字,自己加一些前缀,解决冲突问题
2.2、printf.c
#include "printf.h"
#include "uart.h"
#define ASCII_LF 10
#define ASCII_0 48
#define va_start(v, l) __builtin_va_start((v), l)
#define va_end __builtin_va_end
#define va_arg __builtin_va_arg
#define DEC_BASE 10
#define HEX_BASE 16
#define IS_UNSIGNED 1
#define NOT_UNSIGNED 0
#define PRINT_BUF_LEN 64
#define PAD_ZERO 2
#define PAD_PREFIX 4
typedef __builtin_va_list va_list;
void my_putc(const char c)
{
if(c == '\n')
{
}
else
{
}
}
void my_puts(const char *str)
{
while(*str)
{
my_putc(*str);
str++;
}
}
static void my_printi(long long num, int base, int u, int width, int pad_flag, int letbase)
{
char print_buf[PRINT_BUF_LEN];
int neg = 0;
char *str;
unsigned long long val = num;
unsigned long long t;
int count = 0, pad = 0;
if(u == NOT_UNSIGNED && base == DEC_BASE && num < 0)
{
neg = 1;
val = num;
}
str = print_buf + PRINT_BUF_LEN - 1;
*str = '\0';
if(!val)
{
*--str = '0';
count++;
}
else
{
while (val)
{
t = val % base;
val = val / base;
if(t >= 10)
{
t = t- 10 + letbase;
*--str = t;
}
else
{
*--str = t + ASCII_0;
}
count++;
}
}
count = width - count;
if((count > 0) && (pad_flag & PAD_ZERO))
{
while(count--)
{
*--str = ASCII_0;
}
pad = 1;
}
if(pad_flag & PAD_PREFIX)
{
if((base == HEX_BASE) && (letbase == 'A'))
{
pad ? (*(str + 1) = 'X') : (*--str = 'X');
}
if((base == HEX_BASE) && (letbase == 'a'))
{
pad ? (*(str + 1) = 'x') : (*--str = 'x');
}
pad ? (*(str) = '0') : (*--str = '0');
}
if(neg)
{
if(width && (pad_flag & PAD_ZERO))
{
*--str = '-';
}
else
{
*--str = '-';
}
}
while(*str)
{
my_putc(*str);
str++;
}
}
static void my_vprintf(const char * format, va_list args)
{
int width = 0;
int pad_flag = 0;
for(; *format != 0; ++format)
{
if(*format == '%')
{
++format;
width = 0;
if(*format == '\0')
{
break;
}
if(*format == '%')
{
my_putc(*format);
}
if(*format == '#')
{
pad_flag |= PAD_PREFIX;
format++;
}
if(*format == '0')
{
pad_flag |= PAD_ZERO;
format++;
}
for(; *format >= '0' && *format <= '9'; ++format)
{
width = (width * 10) + *format - '0';
}
if(*format == 's')
{
char *str = va_arg(args, char *);
int count = 0;
while (*str)
{
my_putc(*str);
count++;
str++;
}
count = width - count;
while(count > 0)
{
(pad_flag & PAD_ZERO) ? my_putc('0') : my_putc(' ');
count--;
}
continue;
}
if(*format == 'c')
{
char ch = va_arg(args, int);
my_putc(ch);
continue;
}
if(*format == 'd' || *format == 'i')
{
int num = va_arg(args, int);
my_printi(num, DEC_BASE, NOT_UNSIGNED, width, pad_flag, '0');
continue;
}
if(*format == 'u')
{
int num = va_arg(args, unsigned int);
my_printi(num, DEC_BASE, IS_UNSIGNED, width, pad_flag, '0');
continue;
}
if(*format == 'x')
{
unsigned int num = va_arg(args, unsigned int);
my_printi(num, HEX_BASE, IS_UNSIGNED, width, pad_flag, 'a');
continue;
}
if(*format == 'X')
{
unsigned int num = va_arg(args, unsigned int);
my_printi(num, HEX_BASE, IS_UNSIGNED, width, pad_flag, 'A');
continue;
}
if(*format == 'p')
{
unsigned long num = va_arg(args, unsigned long);
my_printi(num, HEX_BASE, IS_UNSIGNED, width, pad_flag, 'a');
continue;
}
if(*format == 'P')
{
unsigned long num = va_arg(args, unsigned long);
my_printi(num, HEX_BASE, IS_UNSIGNED, width, pad_flag, 'A');
continue;
}
if(*format == 'l' && *(format + 1) == 'l')
{
unsigned long long num = va_arg(args, unsigned long long);
if(*(format + 2) == 'u')
{
format += 2;
my_printi(num, DEC_BASE, IS_UNSIGNED, width, pad_flag, '0');
}
else if(*(format + 2) == 'x')
{
format += 2;
my_printi(num, HEX_BASE, IS_UNSIGNED, width, pad_flag, 'a');
}
else if(*(format + 2) == 'X')
{
format += 2;
my_printi(num, HEX_BASE, IS_UNSIGNED, width, pad_flag, 'A');
}
else if(*(format + 2) == 'd')
{
format += 2;
my_printi(num, DEC_BASE, NOT_UNSIGNED, width, pad_flag, '0');
}
else
{
format += 1;
my_printi(num, DEC_BASE, NOT_UNSIGNED, width, pad_flag, '0');
}
continue;
}
else
{
unsigned long num = va_arg(args, unsigned long);
if(*(format + 1) == 'u')
{
format += 1;
my_printi(num, DEC_BASE, IS_UNSIGNED, width, pad_flag, '0');
}
else if(*(format + 1) == 'x')
{
format += 1;
my_printi(num, HEX_BASE, IS_UNSIGNED, width, pad_flag, 'a');
}
else if(*(format + 1) == 'X')
{
format += 1;
my_printi(num, HEX_BASE, IS_UNSIGNED, width, pad_flag, 'A');
}
else if(*(format + 1) == 'd')
{
format += 1;
my_printi(num, DEC_BASE, NOT_UNSIGNED, width, pad_flag, '0');
}
else
{
my_printi(num, DEC_BASE, NOT_UNSIGNED, width, pad_flag, '0');
}
continue;
}
}
else
{
my_putc(*format);
}
}
}
void my_printf(const char *format, ...)
{
va_list args;
va_start(args, format);
my_vprintf(format, args);
va_end(args);
}
2.3、printf.h
#ifndef __PRINTF_H__
#define __PRINTF_H__
void my_putc(const char c);
void my_puts(const char *buffer);
void my_printf(const char *fmt, ...);
#endif