1.makefile脚本
- makefile脚本帮助我们快速完成编译、链接、清空、反汇编等步骤,是快速开发的必备方法。
//定义变量
CROSS_COMPILE ?= arm-linux-gnueabihf-
NAME ?= ledc //只需要改这里就可以,其他全都一样
// $(),引用变量
CC := $(CROSS_COMPILE)gcc
LD := $(CROSS_COMPILE)ld //链接脚本文件
OBJCOPY := $(CROSS_COMPILE)objcopy //elf转为bin
OBJDUMP := $(CROSS_COMPILE)objdump //反汇编
OBJS := start.o main.o
//定义变量 OBJS, OBJS 包含着要生成 ledc.bin 所需的材料: start.o 和 main.o,
//就是当前工程下的 start.s 和 main.c 这两个文件编译后的.o 文件。
//注意 start.o 一定要放到最前面!因为在后面链接的时候 start.o 要在最前面,因为 start.o 是最先要执行的文件!
$(NAME).bin:$(OBJS)
//默认目标,目的是生成最终的可执行文件 ledc.bin, ledc.bin 依赖 start.o 和 main.o
//如果当前工程没有 start.o 和 main.o 的时候就会找到相应的规则去生成
//比如start.o 是 start.s 文件编译生成的,因此会执行%.o:%.s规则
$(LD) -Timx6ul.lds -o $(NAME).elf $^
//调用链接脚本imx6ul.lds生成ledc.elf文件
//“$^”这一自动变量的意思是所有依赖文件的集合,相当于生成ledc.elf需要用start.o main.o
$(OBJCOPY) -O binary -S $(NAME).elf $@
//将 ledc.elf 文件转为 ledc.bin
//“$@”的意思是目标集合,即ledc.bin
$(OBJDUMP) -D -m arm $(NAME).elf > $(NAME).dis
//反汇编,生成 ledc.dis 文件。
//下面三种针对不同的文件类型将其编译成对应的.o 文件
//其实就是汇编.s(.S)和.c 文件,比如 start.s 就会使用第 8 行的规则来生成对应的 start.o 文件。
%.o:%.s //“%”是通配符
$(CC) -Wall -nostdlib -c -O2 -o $@ $<
//其中“$<”的意思是依赖目标集合的第一个文件
%.o:%.S
$(CC) -Wall -nostdlib -c -O2 -o $@ $<
%.o:%.c
$(CC) -Wall -nostdlib -c -O2 -o $@ $<
clean: //清理工程
rm -rf *.o $(NAME).bin $(NAME).elf $(NAME).dis
2.链接脚本
- 链接脚本注解
- imx6ul.lds是链接脚本文件,-T后跟imx6ul.lds是使用该链接脚本;也可以直接跟地址如-Ttext 0X87800000,表示所有文件链接到此地址之后。
- 有时候我们很多文件需要链接到指定的区域,或者叫做段里面,比如在 Linux 里面初始化函数就会放到 init 段里面。因此我们需要能够自定义一些段,这些段的起始地址我们可以自由指定,同样的我们也可以指定一个文件或者函数应该存放到哪个段里面去。
- 链接脚本:用于描述文件应该如何被链接在一起形成最终的可执行文件。目的是描述输入文件中的段如何被映射到输出文件中,并且控制输出文件中的内存排布。
- 最简单的链接脚本可以只包含一个命令“SECTIONS”,我们可以在这一个“SECTIONS”里面来描述输出文件的内存布局。
- 我们一般编译出来的代码都包含在 text、 data、 bss 和 rodata 这四个段内
// imx6ul.lds 链接脚本代码
SECTIONS{
. = 0X87800000;
// . 是定位计数器值,代表链接地址,表示所有文件链接到此地址之后
.text : //段名
//{}填上要链接到“.text”这个段里面的所有文件
{
start.o //若是下面的makefile需要在前面加 obj/
//链接到开始位置的文件为start.o
//因为 start.o 里面包含着第一个要执行的指令,所以一定要链接到最开始的地方。
main.o
*(.text)
//“*(.text)”中的“*”是通配符,表示所有输入文件的.text段都放到“.text”中。
}
.rodata ALIGN(4) : {*(.rodata*)}
//定义了一个名为“.data”的段,然后所有文件的“.data”段都放到这里面
//ALIGN(4) 用来对“.data”这个段的起始地址做字节对齐的, 表示 4 字节对齐。
//也就是说段“.data”的起始地址要能被 4 整除,常见 ALIGN(4/8), 4 /8字节对齐。
.data ALIGN(4) : { *(.data) }
//__bss_start/__bss_end是符号,这两个符号用来保存.bss 段的起始地址和结束地址。
//.bss 段是定义了但是没有被初始化的变量,我们需要手动对.bss 段的变量清零的, //因此我们需要知道.bss 段的起始和结束地址,这样我们直接对这段内存赋 0 即可完成清零。
__bss_start = .; //对该符号赋值为定位计数器值
.bss ALIGN(4) : { *(.bss) *(COMMON) }
//“.bss”段,所有文件中的“.bss”数据都会被放到这个里面,
//“.bss”数据就是那些定义了但是没有被初始化的变量。
__bss_end = .; //对该符号赋值为定位计数器值
//地址就保存在了“__bss_start”和“__bss_end”中,
//我们就可以直接在汇编或者 C 文件里面使用这两个符号。
}
3. 文件烧写
.bin文件烧写的时候缺乏权限会提示premission denid
此时运行:
chmod 777 imxdownload
//给予 imxdownoad 可执行权限,一次即可
ls /dev/sd*
//检测sd卡是否存在
./imxdownload ledc.bin /dev/sdb
//下载到 SD 卡中sd(x)还是sdb需要看自己电脑
4.链接头文件设置
- CTRL+shift+p ->c/c++:Edit Configurations
- 在当前工作区创建.vscode文件夹,上面文件会自动存在其中
- 在终端中默认不显示以.开头的文件,可以通过ls -a来使其显示
- 在上述文件中的"includePath":{}中添加工程中有.h文件的文件夹
5. bsp下的通用makefile
CROSS_COMPILE ?= arm-linux-gnueabihf-
TARGET ?= bsp //文件名,改1(共3)
CC := $(CROSS_COMPILE)gcc
LD := $(CROSS_COMPILE)ld
OBJCOPY := $(CROSS_COMPILE)objcopy
OBJDUMP := $(CROSS_COMPILE)objdump
//头文件路径 改 2
INCDIRS := imx6ul \ //"\"表示下行继续
bsp/clk \
bsp/led \
bsp/delay
//源码路径 改3
SRCDIRS := project \
bsp/clk \
bsp/led \
bsp/delay
//$(函数名)返回函数执行结果。
//需要在所有文件夹之前加 -I
//$(patsubst <pattern>,<replacement>,<text>)
//patsubst 模式字符串替换函数,查找<text>中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式<pattern>,如果匹配的话,则以<replacement>替换。
INCLUDE := $(patsubst %, -I %, $(INCDIRS))
//print
@echo INCLUDE =$(INCLUDE)
执行 make print
返回 -I imx6u -I bsp/clk -i bsp/led -I bsp/delay
//获取工程中所有.S/.c文件
//$(wildcard)展开通配符, $(wildcard $(dir)/*.S))意为展开dir目录下的所有.S文件
//$(foreach <var>,<list>,<text>)。把参数<list>中的单词逐一取出放到参数<var>所指定的变量中,然后再执行<text>所包含的表达式。
SFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S)) //保存工程下所有.S文件
CFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))
//notdir会去除所有路径而只保留文件名
SFILENDIR := $(notdir $(SFILES))
CFILENDIR := $(notdir $(CFILES))
//将文件名的.s/.c变为.o,文件属性没变
SOBJS := $(patsubst %, obj/%, $(SFILENDIR:.S=.o))
COBJS := $(patsubst %, obj/%, $(CFILENDIR:.c=.o))
OBJS := $(SOBJS) $(COBJS)
//现在OBJS是所有.o文件的集合
//通过以上变量操作,准备好编译的原材料
//默认只会在当前路径下找源码或者头文件,通过VPATH添加新的搜寻路径
VPATH := $(SRCDIRS)
//伪目标
.PHONY: clean
$(TARGET).bin : $(OBJS)
$(LD) -Timx6ul.lds -o $(TARGET).elf $^
$(OBJCOPY) -O binary -S $(TARGET).elf $@
$(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis
//将所有的.S文件编译成.o并存其在SOBJ目录下
//静态模式
$(SOBJS) : obj/%.o : %.S
$(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ $<
$(COBJS) : obj/%.o : %.c
$(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ $<
clean:
rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)