[smart210] 裸板移植printf()与scanf()的步骤

平台:smart210

CPU:S5PV210

目标:在smart210裸板上移植stdio(标准输入输出)的两个核心函数,printf()与scanf()。

知识储备:

1.这里我们直接从主目录下的Makefile分析移植所需要的一系列操作

CC      = arm-linux-gcc
LD      = arm-linux-ld
AR      = arm-linux-ar
OBJCOPY = arm-linux-objcopy
OBJDUMP = arm-linux-objdump

INCLUDEDIR 	:= $(shell pwd)/include
CFLAGS 		:= -Wall -O2 -fno-builtin
CPPFLAGS   	:= -nostdinc -I$(INCLUDEDIR)

export 	CC AR LD OBJCOPY OBJDUMP INCLUDEDIR CFLAGS CPPFLAGS 

objs := start.o main.o uart.o clock.o lib/libc.a

stdio.bin: $(objs)
	${LD} -Tstdio.lds -o stdio.elf $^
	${OBJCOPY} -O binary -S stdio.elf $@
	${OBJDUMP} -D stdio.elf > stdio.dis

.PHONY : lib/libc.a
lib/libc.a:
	cd lib; make; cd ..
	
%.o:%.c
	${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<

%.o:%.S
	${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<

clean:
	make clean -C lib
	rm -f *.bin *.elf *.dis *.o
	

关键点在于stdio.bin,是我们最终生成的二进制文件,它依赖于start.o,main.o,uart.o,clock.o与lib/libc.a这些文件,其中.o的来源是经过arm-linux-gcc -c -o 命令生成的,libc.a是通过进入lib目录下静态编译生成的库文件,为什么要使用静态编译,主要是因为我们所需要的printf()和scanf()移植依赖于linux内核提供的内核函数,而我们现在是属于裸板无操作系统的状态下,即使采用动态编译成功生成了.bin文件,也无法在裸板开发板上运行。所以,我们需要手动建一个库,把linux内核支持的printk()函数的相关.c .h文件经过修改后都移植过来,于是我们采用了静态编译的方法生成libc.a库(可以注意到CPPFLAGS带了-nostdinc选项的意思就是不使用标准库,即不动态链接标准库,而是采用当前主目录下的include文件夹里面的.h)。在main.c程序里面包含的“stdio.h"文件里,我们能发现如下代码

extern int vsnprintf(char *buf, size_t size, const char *fmt, va_list args);
extern int snprintf(char * buf, size_t size, const char *fmt, ...);
extern int vsprintf(char *buf, const char *fmt, va_list args);
extern int sprintf(char * buf, const char *fmt, ...);
extern int vsscanf(const char * buf, const char * fmt, va_list args);
extern int sscanf(const char * buf, const char * fmt, ...);

extern void putc(unsigned char c);
extern unsigned char getc(void);

int printf(const char *fmt, ...);
int scanf(const char * fmt, ...);
这说明了一系列与printf()和scanf()相关的子函数都是外部函数,在编译器在链接的时候,就能把libc.a里面的各.o文件内的相关函数链接过来。强大的链接器帮我们把各个独立的.o文件根据函数名(地址)链接起来,生成链接文件elf,该文件又可以进一步改造成适用于开发板使用.bin文件!

2.从/lib/Makefile分析lib/libc.a库文件的生成

objs := div64.o lib1funcs.o ctype.o muldi3.o printf.o string.o vsprintf.o

libc.a: $(objs)
	${AR} -r -o $@ $^
	
%.o:%.c
	${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<

%.o:%.S
	${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<

clean:
	rm -f libc.a *.o		

可知,libc.a文件依赖于div64.o lib1funcs.o ctype.o muldi3.o printf.o string.o vsprintf.o ,由于采用了主目录下的Makefile所输出的CPPFLAGS与CFLAGS配置,届时arm-linux-cpp在编译.c文件的时候,就会自动跑去主目录的include库下去找需要的函数了。这些.o文件中,最核心的是printf.o,对应的.c文件实现的最关键的两个函数如下:

int printf(const char *fmt, ...)
{
	int i;
	int len;
	va_list args;

	va_start(args, fmt);
	len = vsprintf(g_pcOutBuf,fmt,args);
	va_end(args);
	for (i = 0; i < strlen(g_pcOutBuf); i++)
	{
		putc(g_pcOutBuf[i]);
	}
	return len;
}



int scanf(const char * fmt, ...)
{
	int i = 0;
	unsigned char c;
	va_list args;
	
	while(1)
	{
		c = getc();
		putc(c);
		if((c == 0x0d) || (c == 0x0a))
		{
			g_pcInBuf[i] = '\0';
			break;
		}
		else
		{
			g_pcInBuf[i++] = c;
		}
	}
	
	va_start(args,fmt);
	i = vsscanf(g_pcInBuf,fmt,args);
	va_end(args);

	return i;
}
拿printf()做分析,我们能发现程序内容先对变参数进行处理,把我们输入给printf的每一个参数都做字符串分析,比如%d和数字之类的,然后拷贝对应参数变量的值结合第一个字符串参数里的指令输出到g_pcOutBuf这个字符缓冲区里面,最后循环调用我们的串口单字符打印函数putc()来输出。最终我们就达到了printf()的使用效果了!而scanf()也是类似的!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值