首页 > 代码库 > 从零开始写一个arm下的裸板程序
从零开始写一个arm下的裸板程序
从零开始写一个arm下的裸板程序.我们整个程序是基于uboot运行的. 所有我们可以借助uboot中的printf来输出,默认开发版的标准输出是串口. 电脑的默认标准输出的屏幕. 1.需要创建的文件由include文件夹,用来存放头文件. 2.创建一个hw.h头文件. 3.编写一个common.h,它定义了借用uboot的printf的宏.和NULL这个宏的定义. 4.hw.c 硬件相关的文件. 5.main.c c文件. 6.start.s 汇编文件. 7.ld.lds 链接脚本, 8.Makefile 用来管理编写项目的配置文件. 下面开始是每个步骤的详细解析: /**********************************************/ 1.创建include文件. zshh@HP:~/work/android/code/hardware$ mkdir my01porject //创建一个项目文件. zshh@HP:~/work/android/code/hardware$ cd my01porject/ //切换到当前项目路径. zshh@HP:~/work/android/code/hardware/my01porject$ mkdir include //创建一个include文件夹. /**********************************************/ 2.创建hw.h硬件相关的操作, #ifndef __MY_HW_H #define __MY_HW_H extern int hw_init(void);//这个是硬件初始化操作. extern int hw_opts(void);//这个是硬件操作函数. #endif /**********************************************/ 3创建一个common.h. #ifndef __MY_COMMON_H #define __MY_COMMON_H #define NULL (void *)0 //定义了宏 NULL //定义了一个printf宏,(__VA_ARGS__)在定义变参数的时候必须要加这个表示, // int (*)(const char *, ...)是一个函数指针的类型. // 我们把这个0x43e11434数字转化成当前类型的函数指针类型. #define printf(...) (((int (*)(const char *, ...))0x43e11434)(__VA_ARGS__)) #if 0 //需要注意的是我们需要说下他的由来.0x43e11434, //切换到uboot源代码所在目录. zshh@HP:~/work/arm/arm资料/exynos4412_lzy/src/uboot/uboot-2012-12$ ls api COPYING examples Makefile README u-boot arch COPYING.txt fs mkconfig rules.mk u-boot.bin board CREDITS include nand_spl sd_fuse u-boot.lds boards.cfg disk lib net snapshot.commit u-boot.map common doc MAINTAINERS onenand_ipl System.map u-boot.srec config.mk drivers MAKEALL post tools #endif 在Makefile中有怎么一段,就是告示你,当前函数的所有链接地址都放在.System.map中. SYSTEM_MAP = $(NM) $1 | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | LC_ALL=C sort $(obj)System.map: $(obj)u-boot @$(call SYSTEM_MAP,$<) > $(obj)System.map 这个是我拷贝这个System.map打开的一部分.其中43e11434 T printf就是uboot的链接地址. 43e11294 T serial_printf 43e112d8 T fgetc 43e11304 T ftstc 43e11330 T fputc 43e11358 T fputs 43e11380 T fprintf 43e113cc T getc 43e113e4 T tstc 43e113fc T putc 43e11418 T puts 43e11434 T printf 43e11478 T vprintf //再c中我们找到一个函数的地址.就可以调用给函数.方式如上. #endif /**********************************************/ 4.编写一个测试类hw.c 文件. #include<hw.h> //初始化当前设置的值, int hw_init(void) { //我们做个参数.只输出当前函数的名称,和当前行所在的值. printf("%s ,%d",__FUNCTION__, __LINE__); return 0 ; } //对当前设备进行操作. int hw_opts(void) { //我们做个参数.只输出当前函数的名称,和当前行所在的值. printf("%s ,%d",__FUNCTION__, __LINE__); return 0 ; } /**********************************************/ 5.编写main.c进行调用. #include<stdio.h> int main(void) { hw_init(); hw_opts(); return 0; } /**********************************************/ 6.编写start.s文件.这个是程序执行的入口. //这个表示该_start是一个外部标号,如果不使用.global,进行修饰它默认是是一个内部标号. //当你你可以把他下载到内存的某个地址上.如何使用go 50000000执行.它相当于执行了 //如下操作. bl _start,再跳转之前他会把uboot中的bl _start的下一条指令存放的 lr寄存器中. .global _start _start: b reset //b reset则是跳转到reset:标号下执行. reset: stmfd sp!, {r0-r12, lr} //这句话的意思是讲.r0-r12, 和lr进行压栈.是为了保存这些值, bl main //这只就是跳转到我们c函数中的main中执行.再跳转之前它会讲当前lr赋值为ldmfd的值. //执行完毕之后.会跳转回来执行.ldmfd sp!, {r0-r12, lr} ,将压栈的东西拿出来, ldmfd sp!, {r0-r12, lr} @mov pc, lr //这个是注释行.在汇编中使用@进行注释. bx lr //之后跳转会uboot执行.为什么步b指针进行跳转, //是以为lr是一个寄存器.b不能操作寄存器,它只能操作标号.而bx可以.因为他只能操作寄存器. /**********************************************/ 7. 每一个链接过程都由链接脚本(linker script, 一般以lds作为文件的后缀名)控制. 链接脚本主要用于规定如何把输入文件内的section放入输出文件内, 并控制输出文件内各部分在程序地址空间内的布局. 但你也可以用连接命令做一些其他事情. OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") // 定义三种输出文件的格式(大小端) OUTPUT_ARCH(arm)//设置输出文件的machine architecture(体系结构),BFDARCH为被BFD库使用的名字之一。可以用命令objdump -f查看 ENTRY(_start) //ENTRY(SYMBOL) :将符号SYMBOL的值设置成入口地址。 SECTIONS { . = 0x50000000; //这个说明的是一个链接地址. . = ALIGN(4); //进行4字节对齐. .text : //定义一个.text段. { ./start.o (.text) //链接的第一个文件是./start.o 其他的文件链接. *(.text) //其他文件的文件.不管他们的顺序. 下面是定义其他段. } . = ALIGN(4); .rodata : { *(.rodata) } . = ALIGN(4); .data : { *(.data) } . = ALIGN(4); .bss : { *(.bss) } } /**********************************************/ 编写Makefile文件. TARGET :=arm //定义目标变量名称. BIN :=$(TARGET).bin //定义生成目标文件的bin文件. START_OBJ :=start.o //定义start.s生成的目标名称. OBJS :=main.o hw.o //定义其他 需要编译的.c文件生成的.o文件的名称. LD_ADDR :=0x50000000 //定义链接地址. CFLAGS += -Wall -I./include //CFLAGES += -Wall表示打开警告.编译是自动查找./include目录下的头文件. CROSS_COMPILE :=arm-linux- //定义一个前缀名称. CC :=$(CROSS_COMPILE)gcc $(CFLAGS) //定义编译编译命令 AS :=$(CROSS_COMPILE)as //定义汇编命令. LD :=$(CROSS_COMPILE)ld //定义链接命令. OBJCOPY :=$(CROSS_COMPILE)objcopy -O binary //定义去掉elf格式的命令. OBJDUMP :=$(CROSS_COMPILE)objdump -D //定义反汇编命令. NM :=$(CROSS_COMPILE)nm //定义链接之后各个函数对应的链接地址.命令. RM :=rm -rf //定义一个rm 命令.也就是删除命令. ############################################## all:$(TARGET) //定义一个目标. @$(OBJCOPY) $< $(BIN) @echo OBJCOPY $< @$(OBJDUMP) $< >$<.s @echo OBJDUMP $< @$(NM) $< >System.map @echo NM $< @$(RM) $< # ./down.sh $(TARGET):$(START_OBJ) $(OBJS) #@$(LD) $^ -o $@ -Ttext $(LD_ADDR) @$(LD) $(OBJS) -o $@ -T ld.lds @echo LD $@ %.o:%.s //这里是要生成start.o依赖start.s文件. @$(AS) $< -o $@ //$<会自动匹配所有的依赖, $@会自动匹配所有目标. @echo AS $@ //输出生成的目标. %.o:%.c @$(CC) $< -c -o $@ @echo CC $@ clean: @$(RM) $(START_OBJ) $(OBJS) $(BIN) @echo RM ./
从零开始写一个arm下的裸板程序
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。