如何使用BSP管理自己的工程文件(IMX6UL开发板)

        在讲解前面的实验时,我们都是直接将文件放在根目录下,在工程文件比较少的时候确实可以这么做,但是如果工程数量逐渐增多如果全部都放在根目录下就会显得非常杂乱无章。所以现在我们需要对共文件进行管理,将不同的功能的代码放到对应不同的文件夹中华,也需要将源码文件中,所有完成同一个功能的代码提取出来放到一个单独的文件中,也是对程序分功能管理,这一节我们就接着前面的代码,来学习如何对我们的工程文件使用BSP管理

        这里我先展示一下,使用了BSP和没有使用BSP它们之间的区别,首先左边的工程文件确实全部被放在了根目录下,但至少现在文件数量并不是很多,其次就是我main.c中代码中包含了时钟,GPIO等功能的初始化、以及函数功能的编写,全部放在显得主函数十分的冗杂

        下方使用BSP管理了我们的工程文件,看的出来多了很多个文件夹,每个文件夹的名字以及对应工程文件的名字,清晰的看出每个文件的作用是什么,其次就是我们的每个文件的代码也显得非常的干净简介,所以这也是我们在开发过程中必须掌握的一种方法

        首先新建一个新的工程文件,然后在这个文件二点根目录下新建4个工程文件,这四个工程文件下方存放着不同类型的文件

        bsp下存放着我们的设备驱动,可以看到下方有clk文件夹就是时钟驱动,delay就是延时函数的驱动,led就是时间驱动

        imx6ul存放着和芯片相关的代码,例如里面是我们之前一移植的SDK库工程文件,以及一些和芯片有关的代码

        obj用于存放我们在编译的过程中生成的那些文件,也就是我们在调试过程中可能会生成.o.、elf、.bin等文件

        最后project用来存放工程文件,也就是我们的系统驱动函数,以及main.c等主函数

        我们先将初始的文件夹创建好,然后就开始从bsp驱动文件开始编写代码,首先我们需要先添加一个文件路径,不然在例如在bsp_clk.h的文件中包含imx6ul中的sdk库文件的时候会报错,显示找不到
        首先Ctrl + shift + P打开搜索框,然后搜索下方,点击编辑配置后,路径会自动添加进入我们的根目录中

        也就是此文件,就是路径文件        

        然后将我们前面移植好的库添加到imx6ul文件夹下,然后新建一个头文件名字叫做imxul.h

        然后将所有的库文件都全部放在此文件夹中,因为之后在编写驱动代码的时候需要多次引用到这些库文件,如果每个都反复去输入显得非常的繁琐,所以这里我们就创建一个文件专门来引用这些头文件

        一切都准备好后,就开始编写驱动了,首先第一步仍然是时钟驱动,在bsp的文件下方创建一个文件夹名字叫做clk,在clk下创建两个文件bsp_clk,c和bsp_clk.h,

        然后再.h中写入代码,引入刚开始定义好的库文件imx6ul.h,这里的void clk_init();是我们在bsp_clk,c中创建的函数,我们在.c文件中编写的函数都需要在.h中引用一下

        下方是初始化时钟寄存器的操作,博主在前面的博客当中已经讲过了,要是不太清除的话可以去看看博主前面的博客,这里就不过多的赘述原理了,可以看到这里创建了时钟初始化函数void clk_init();,所以要在.h文件中引用

        然后创建的是在bsp下创建一个delay的文件夹,用来存放延时函数的.c和.h文件,代码原理不过多赘述,如图下

        在bsp下创建一个led的文件夹,然后创建bsp_led.c和bsp_ledc.h的文件,然后编写LED的驱动代码

        然后我们的驱动代码就全部放在了功工程管理中,然后下面就是写project中的工程代码,在project的下方创建一个main.c和一个start.S文件,main.c就是写我们的工程代码,start.S依然还是工程启动文件

        .c和.S的代码也不做讲解,在下方博主已经全部列出来了

        需要改变的地方是我们这里的这个链接文件,因为在之前我们的start.S文件都是直接被放在根目录下方的,现在我们的启动文件是被放在文件夹project下方,所以需要在链接text段的时候需要重新给定启动文件的文件路径,在前方加入obj/也就是说明我们这个文件obj文件夹的下方,之后链接文件便可以正确的找到这个文件的路径

        既然链接文件也发生了变化,那么Makefile文件肯定是要发生变化的,因为文件的路径发生了很大的变化,为了Makefile正常的使用所以我们需要花点时间修改一下,首先我们还是先把之间的执行文件imxdownload移植过来检查一下文件格式

        文件格式基本如下,因为没有修改Makefile无法编译代码,所以obj当中有没有编译生成的代码,但是可以正确看到其他文件的文件夹等构造

        现在开始修改Makefile文件

        看到这么长的Makefile文件,不用担心,我们还是一步一步开始分析这些代码的原理是什么

         首先的代码相同,创建环境变量以及变量,后面的INCDIRS定义包含路径的目录,这里包含了所有需要的头文件目录,imx6ul当中包含了此路径下的所有头文件,后面的都是bsp的文件夹下对应的文件夹的文件路径。
        然后SRCDIRS提供的同样是路径,上面通俗来讲提供的是头文件的路径,而下方添加的是.c文件的路径,严格来讲也是包含所有源代码路径,这些目录中的源代码会被编译成目标文件
        INCLUDE这个变量用于生成 -I 选项,这些选项会被传递给编译器,以告诉它在编译时需要查找的头文件目录$(patsubst %, -I %, $(INCDIRS)) 是一个 Makefile 函数,用于处理目录列表并生成 -I 选项。patsubst 是 make 内置的函数,它用于模式替换。这里的模式替换是将 INCDIRS 中的每个目录替换为 -I 选项后跟目录路径。


        首先在这里讲解一下patsubst函数,他是make的内置函数,用于模式替换,语法为
patsubst pattern(要切换的模式), replacement(用来替换匹配模式的内容), text(要进行模式替换的文本),$(INCDIRS) 是包含头文件目录的列表,例如 imx6ul bsp/clk bsp/led bsp/delay。patsubst %, -I %, $(INCDIRS) 会将 INCDIRS 列表中的每个目录用 -I 选项包围。对 bsp/clk,替换后得到 -I bsp/clk,以此类推。

        SFILES:查找所有目录(SRCDIRS)中的汇编源文件(.S)。$(foreach dir, $(SRCDIRS), ...):遍历 SRCDIRS 中的每个目录 dir。$(wildcard $(dir)/*.S):在当前目录 dir 中查找所有以 .S 结尾的文件。SFILES 将包含所有符合条件的 .S 文件的列表。
        foeeach也是Make提供的一个函数,用于遍历列表中的每一个元素并对每个元素执行指定的操作。语法是 $(foreach var(用于保存当前循环的元素), list(要遍历的列表), text(对每个元素执行的操作,var的元素在这里被直接替换))
        在我们的代码中foreach 函数会初始化一个循环,开始遍历 $(SRCDIRS) 列表中的每一个目录。在每次循环迭代中,dir 变量被赋值为 $(SRCDIRS) 列表中的一个目录路径。例如,第一次迭代时 dir 是 project,第二次迭代时 dir 是 bsp/clk,依此类推。
        对于后半的$(wildcard $(dir)/*.S)$(wildcard $(dir)/*.S):在每次循环中,这部分代码用于查找 $(dir) 目录中的所有 .S 文件。$(dir) 是当前循环中 foreach 函数的局部变量,代表当前目录。wildcard也是Make的一个函数用于根据给定的模式查找匹配的文件名。在这里,它用来查找当前目录中所有以 .S 结尾的汇编源文件。
        在每次迭代中,$(dir) 变量持有当前的目录路径。例如,第一次迭代时 $(dir) 是 project,那么 $(wildcard $(dir)/*.S) 就会查找 project 目录中的所有 .S 文件。wildcard 函数用于查找文件,返回一个以空格分隔的匹配文件列表,例如 file1.S file2.S。这个结果会被添加到 SFILES 变量中。
        总结来说循环开始:foreach 函数开始遍历 $(SRCDIRS) 列表中的每一个目录路径。当前目录:在每次循环中,dir 变量代表当前目录路径,并且在该目录下查找 .S 文件。这样,SFILES 变量最终会包含所有在 $(SRCDIRS) 列表中每个目录下找到的汇编源文件的路径。

        那么对于CFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))处理的文件类型不同,这里处理的是C源文件的路径,以及将此源文件添加到前面的变量CFILENDIR中

        SFILENDIR := $(notdir $(SFILES))和CFILENDIR := $(notdir $(CFILES))目的都是从文件路径中提取出文件名,而去掉路径部分。具体地,它们将 SFILESCFILES 列表中的路径信息转换为只包含文件名的列表。
        对于$(notdir)是Make中包含的一个函数,用于去掉文件路径,只保留文件名。所以这两段代码非常简单。从完整的文件路径列表中提取出文件名,方便后续的处理。简化的文件名将用于构建目标文件(.o 文件)的名称。

        对于SOBJS := $(patsubst %, obj/%, $(SFILENDIR:.S=.o)) COBJS := $(patsubst %, obj/%, $(CFILENDIR:.c=.o))这两段代码,首先将上面添加到  SFILENDIR和CFILENDIR中的文件的所有拓展名全部转换为.o文件,因为这些文件在之前已经删掉了文件路径,在这里通过函数patsubst再他们的前面添加上/obj的路径,因为之后我们要将编译过程中生成的文件全部放到OBJ的文件夹路径下方便管理  函数patsubs,这是一个模式替换函数,这里代码是用于在文件名之前添加路径 obj/。例如,将 file1.o 转换为 obj/file1.o
        这两段代码的作用也就是将所有在SFILENDIR和CFILENDIR的文件拓展名变为.o文件,以及添加上/obj的路径

        下面一段OBJS := $(SOBJS) $(COBJS),将前面两个变量包含的所有文件,全部都放到变量OBJS中,将包含所有 .S.c 文件的目标文件名列表,即所有源文件对应的目标文件名的集合。这样可以在编译过程中统一处理所有的目标文件。

        VPATH := $(SRCDIRS)VPATH 是 make 的一个特殊变量,用于指定 make 查找源文件的目录路径。当 make 执行构建时,它会在 VPATH 指定的目录中查找源文件,而不仅仅是在当前目录中。如果你的源文件位于多个目录中,比如 project、bsp/clk、bsp/led 等,VPATH 设置了这些目录,make 在编译源文件时会在这些目录下查找文件。这样,make 就能够找到位于不同目录的源文件,进行编译和链接。
        这个时候可能不太理解,前面我的文件都已经存放在了OBJS当中,为什么还需要Make去前面寻找文件的路径,那么前面删除文件路径有什么意义呢?SRCDIRS定义了所有可能包含源文件的目录。它告诉 make 在哪些目录下查找源文件,特别是当源文件分布在多个目录中时。VPATH指定了查找源文件的路径。make 在执行时会在这些路径中寻找源文件。make 需要知道源文件的实际位置,以便能够正确地编译它们并生成相应的目标文件。如果源文件在不同的目录中,make 需要这些路径信息来找到它们。
        OBJS是编译源文件后生成的目标文件的列表。它包含了所有需要编译的 .o 文件的路径。OBJS:是编译源文件后生成的目标文件的列表。它包含了所有需要编译的 .o 文件的路径。
        简单来说SRCDIRS 和 VPATH 是为了让 make 知道在哪里查找源文件,特别是当源文件分布在多个目录中时。OBJS 只是目标文件的列表,不包含源文件的位置。make 需要源文件的位置来执行编译和构建规则。

        对于.PHONY: clean,用于声明某些目标不是实际的文件名,而是执行某些命令的伪目标。其实简单来说为了防止会有防止之后会有clean命名的文件名字,这条代码就是不管之后会不会有clean的文件,make clean 的指令都会去执行clean目标中定义的命令,而不管是否冲突的clean命名的文件

        这四段的代码在一节博客详细讲解过,这里不多赘述,简单解释一下。$(TARGET).bin : $(OBJS)这行定义了一个名为 $(TARGET).bin 的目标文件,并指定了它的依赖文件是 存储在$(OBJS)中的变量。Target以及OBJS都是我们之前定义过的变量
        $(LD) -Timx6ul.lds -o $(TARGET).elf $^这行使用 $(LD) 命令(通常是 GNU 链接器 ld)来链接目标文件,生成一个 ELF 格式的可执行文件 $(TARGET).elf。-Timx6ul.lds  指定了链接器脚本 imx6ul.lds,$^ 代表所有的依赖文件(即 $(OBJS))
        $(OBJCOPY) -O binary -S $(TARGET).elf $@使用 $(OBJCOPY) 命令(通常是 GNU 的 objcopy 工具)将 $(TARGET).elf 文件转换成二进制格式的文件,并保存为 $(TARGET).bin。-O binary 指定输出格式为二进制,-S 选项表示从输出文件中去掉符号表
        $(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis这行使用 $(OBJDUMP) 命令(通常是 GNU 的 objdump 工具)来生成 $(TARGET).elf 文件的汇编代码(反汇编),并将结果输出到 $(TARGET).dis 文件中。-D 选项表示打印整个文件的反汇编,-m arm 指定了处理 ARM 架构的汇编代码
        代码定义了如何从目标文件生成一个二进制文件,并提供一个汇编代码的反汇编文件,以便进行进一步分析和调试。

        $(SOBJS) : obj/%.o : %.S
        $(CC) -Wall -nostdlib -c -O2  $(INCLUDE) -o $@ $<
        这行定义了一个规则,用于将汇编源文件(.S 文件)编译成目标文件(.o 文件)。$(SOBJS) 是目标文件的列表,obj/%.o 是目标模式,%.S 是源模式。这里的 obj/%.o 表示目标文件将被放置在 obj 目录下,%.S 表示匹配的汇编源文件。
        然后下方的$(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ $<也是讲解过,这行使用 $(CC) 命令(通常是 GCC 编译器)来编译汇编源文件。-Wall:启用所有警告信息。-nostdlib:不使用标准库。-c:编译成目标文件,而不链接。-O2:启用第二级优化。$(INCLUDE):包含的头文件路径(假设 $(INCLUDE) 是一个包含 -I 选项的变量)。-o $@:指定输出文件名为 $@(即 obj/%.o)。$<:表示第一个依赖文件(即 .S 文件)

        那么对于$(COBJS) : obj/%.o : %.c
        $(CC) -Wall -nostdlib -c -O2  $(INCLUDE) -o $@ $<
        用于将 C 源文件(.c 文件)编译成目标文件(.o 文件)。$(COBJS) 是目标文件的列表,obj/%.o 是目标模式,%.c 是源模式。这里的 obj/%.o 表示目标文件将被放置在 obj 目录下,%.c 表示匹配的 C 源文件。下方命令的GCC编译选项都是相同的,不多描述

        以及下方的clean:也同样在前一节描述过,也不多讲解,那么我们的Makefile文件就已经全部完成了,这样写虽然需要多加理解,但是这样的作用可以让我们之后在BSP工程模板下,只需要添加新的文件路径,而不需要每一次大量修改Makefile文件了才能编译代码了,对于Makefile可能需要多加理解,因为在之前基本没有接触过。

        那么新建终端开始验证,可以看到输入Make指令,代码也是被成功的编译了,并且所有的.o文件都被放在了OBJ文件下方,根目录下也生成了我们需要的.bin文件,就可以使用imxdownload编译我们的代码了

        在这里如何下载代码具体流程不描述了,也可以参照上一篇的教程,那么BSP工程管理的教程就讲解完了,如果有问题也欢迎联系博主

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值