完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
本帖最后由 正点原子运营官 于 2019-12-20 15:32 编辑
1)实验平台:正点原子Linux开发板 2)摘自《正点原子I.MX6U嵌入式Linux驱动开发指南》关注官方微信号公众号,获取更多资料:正点原子 第十三章BSP工程管理实验 在前面的章节中,我们都是将所有的源码文件刚到工程的根目录下,如果工程文件比较少的话这样做无可厚非,但是如果工程源文件达到几十、甚至数百个的时候,这样一股脑全部放到根目录下就会使工程显得混乱不堪。所以我们必须对工程文件做管理,将不同功能的源码文件放到不同的目录中。另外我们也需要将源码文件中,所有完成同一个功能的代码提取出来放到一个单独的文件中,也就是对程序分功能管理。本章我们就来学习一下如何对一个工程进行整理,使其美观、功能模块清晰、易于阅读。 13.1工程管理简介 打开我们上一章的工程根目录,如图13.1.1所示: 图13.1.1工程根目录 在图13.1.1中我们将所有的源码文件都放到工程根目录下,即使这个工程只是完成了一个简单的流水灯的功能,但是其工程根目录下的源码文件就已经不少了。如果在添加一些其他的功能文件,那么文档就会更大,显得很混乱,所以我们需要对这个工程进行整理,将源码文件分模块、分功能整理。我们可以打开一个STM32的例程,如图13.1.2所示: 图13.1.2 STM32F103例程工程文件 图13.1.2中的工程目录就很美观、不同的功能模块文件放到不同的文件夹中,比如驱动文件就放到HARDWARE文件夹中,ST的官方库就放到STM32F10x_FWLib文件夹中,编译产生的过程文件放到OBJ文件夹中。我们可以参考这个工程目录结构来整理第十二章的例程工程,新建名为“5_ledc_bsp”的文件夹,在里面新建bsp、imx6ul、obj和project这4个文件夹,完成以后如图13.1.3所示: 图13.1.3新建的工程根目录文件夹 其中bsp用来存放驱动文件;imx6ul用来存放跟芯片有关的文件,比如NXP官方的SDK库文件;obj用来存放编译生成的.o文件;project存放start.S和main.c文件,也就是应用文件; 将十二章实验中的cc.h、fsl_common.h、fsl_iomuxc.h和MCIMX6Y2.h这四个文件拷贝到文件夹imx6ul中;将start.S和main.c这两个文件拷贝到文件夹project中。我们前面的实验中所有的驱动相关的函数都写到了main.c文件中,比如函数clk_enable、led_init和delay,这三个函数可以分为三类:时钟驱动、LED驱动和延时驱动。因此我们可以在bsp文件夹下创建三个子文件夹:clk、delay和led,分别用来存放时钟驱动文件、延时驱动文件和LED驱动文件,这样main.c函数就会清爽很多,程序功能模块清晰。工程文件夹都创建好了,接下来就是编写代码了,其实就是将时钟驱动、LED驱动和延时驱动相关的函数从main.c中提取出来做成一个独立的驱动文件。 13.2 硬件原理分析 本章使用到的硬件资源和第八章一样,就是一个LED0。 13.3 实验程序编写本实验对应的例程路径为:开发板光盘-> 1、裸机例程->5_ledc_bsp。 使用VScode新建工程,工程名字为“ledc_bsp”。 13.3.1 创建imx6ul.h文件 新建文件imx6ul.h,然后保存到文件夹imx6ul中,在imx6ul.h中输入如下内容: 示例代码13.3.1.1 imx6ul.h文件代码 1 #ifndef __IMX6UL_H 2 #define __IMX6UL_H 3/*************************************************************** 4 Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved. 5文件名 : imx6ul.h 6作者 : 左忠凯 7版本 : V1.0 8描述 : 包含一些常用的头文件。 9其他 : 无 10论坛 : www.openedv.com 11日志 : 初版V1.0 2019/1/3 左忠凯创建 12 ***************************************************************/ 13 #include "cc.h" 14 #include "MCIMX6Y2.h" 15 #include "fsl_common.h" 16 #include "fsl_iomuxc.h" 17 18 #endif 文件imx6ul.h很简单,就是引用了一些头文件,以后我们就可以在其他文件中需要引用imx6ul.h就可以了。 13.3.2编写led驱动代码 新建bsp_led.h和bsp_led.c两个文件,将这两个文件存放到bsp/led中,在bsp_led.h中输入输入如下内容: 示例代码13.3.2.1 bsp_led.h文件代码 1 #ifndef __BSP_LED_H 2 #define __BSP_LED_H 3 #include "imx6ul.h" 4/*************************************************************** 5 Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved. 6文件名 : bsp_led.h 7作者 : 左忠凯 8版本 : V1.0 9描述 : LED驱动头文件。 10其他 : 无 11论坛 : www.openedv.com 12日志 : 初版V1.0 2019/1/4 左忠凯创建 13 ***************************************************************/ 14 15 #define LED0 0 16 17/* 函数声明 */ 18void led_init(void); 19void led_switch(int led,int status); 20 #endif bsp_led.h的内容很简单,就是一些函数声明,在bsp_led.c中输入如下内容: 示例代码13.3.2.2 bsp_led.c文件代码 1 #include "bsp_led.h" 2/*************************************************************** 3 Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved. 4文件名 : bsp_led.c 5 作者 : 左忠凯 6版本 : V1.0 7描述 : LED驱动文件。 8其他 : 无 9论坛 : www.openedv.com 10日志 : 初版V1.0 2019/1/4 左忠凯创建 11 ***************************************************************/ 12 13/* 14 * @description : 初始化LED对应的GPIO 15 * @param : 无 16 * @Return : 无 17 */ 18void led_init(void) 19{ 20 /* 1、初始化IO复用 */ 21 IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03,0); 22 23 /* 2、、配置GPIO1_IO03的IO属性 */ 24 IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO03_GPIO1_IO03,0X10B0); 25 26 /* 3、初始化GPIO,GPIO1_IO03设置为输出*/ 27 GPIO1->GDIR |=(1<<3); 28 29 /* 4、设置GPIO1_IO03输出低电平,打开LED0*/ 30 GPIO1->DR &=~(1<<3); 31} 32 33/* 34 * @description : LED控制函数,控制LED打开还是关闭 35 * @param - led : 要控制的LED灯编号 36 * @param - status : 0,关闭LED0,1 打开LED0 37 * @return : 无 38 */ 39void led_switch(int led,int status) 40{ 41 switch(led) 42 { 43 case LED0: 44 if(status == ON) 45 GPIO1->DR &=~(1<<3);/* 打开LED0 */ 46 elseif(status == OFF) 47 GPIO1->DR |=(1<<3);/* 关闭LED0 */ 48 break; 49 } 50} bsp_led.c里面就两个函数led_init和led_switch,led_init函数用来初始化LED所使用的IO,led_switch函数是控制LED灯的打开和关闭,这两个函数都很简单。 13.3.3编写时钟驱动代码 新建bsp_clk.h和bsp_clk.c两个文件,将这两个文件存放到bsp/clk中,在bsp_clk.h中输入输入如下内容: 示例代码13.3.3.1 bsp_clk.h文件代码 1 #ifndef __BSP_CLK_H 2 #define __BSP_CLK_H 3/*************************************************************** 4 Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved. 5文件名 : bsp_clk.h 6作者 : 左忠凯 7版本 : V1.0 8描述 : 系统时钟驱动头文件。 9其他 : 无 10论坛 : www.openedv.com 11日志 : 初版V1.0 2019/1/4 左忠凯创建 12 ***************************************************************/ 13 14 #include "imx6ul.h" 15 16/* 函数声明 */ 17void clk_enable(void); 18 19 #endif bsp_clk.h很简单,在bsp_clk.c中输入内容: 示例代码13.3.3.2 bsp_clk.c文件代码 1 #include "bsp_clk.h" 2 3 /*************************************************************** 4 Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved. 5文件名 : bsp_clk.c 6作者 : 左忠凯 7版本 : V1.0 8描述 : 系统时钟驱动。 9 其他 : 无 10论坛 : www.openedv.com 11日志 : 初版V1.0 2019/1/4 左忠凯创建 12 ***************************************************************/ 13 14/* 15 * @description : 使能I.MX6U所有外设时钟 16 * @param : 无 17 * @return : 无 18 */ 19void clk_enable(void) 20{ 21 CCM->CCGR0 =0XFFFFFFFF; 22 CCM->CCGR1 =0XFFFFFFFF; 23 CCM->CCGR2 =0XFFFFFFFF; 24 CCM->CCGR3 =0XFFFFFFFF; 25 CCM->CCGR4 =0XFFFFFFFF; 26 CCM->CCGR5 =0XFFFFFFFF; 27 CCM->CCGR6 =0XFFFFFFFF; 28} bsp_clk.c只有一个clk_enable函数,用来使能所有的外设时钟。 13.3.4编写延时驱动代码 新建bsp_delay.h和bsp_delay.c两个文件,将这两个文件存放到bsp/delay中,在bsp_delay.h中输入输入如下内容: 示例代码13.3.4.1 bsp_delay.h文件代码 1 #ifndef __BSP_DELAY_H 2 #define __BSP_DELAY_H 3 /*************************************************************** 4 Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved. 5文件名 : bsp_delay.h 6作者 : 左忠凯 7版本 : V1.0 8描述 : 延时头文件。 9其他 : 无 10论坛 : www.openedv.com 11日志 : 初版V1.0 2019/1/4 左忠凯创建 12 ***************************************************************/ 13 #include "imx6ul.h" 14 15/* 函数声明 */ 16void delay(volatileunsignedint n); 17 18 #endif 在bsp_delay.c中输入内容: 示例代码13.3.4.2 bsp_delay.c文件代码 /*************************************************************** Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved. 文件名 : bsp_delay.c 作者 : 左忠凯 版本 : V1.0 描述 : 延时文件。 其他 : 无 论坛 : www.openedv.com 日志 : 初版V1.0 2019/1/4 左忠凯创建 ***************************************************************/ 1 #include "bsp_delay.h" 2 3/* 4 * @description : 短时间延时函数 5 * @param - n : 要延时循环次数(空操作循环次数,模式延时) 6 * @return : 无 7 */ 8void delay_short(volatileunsignedint n) 9{ 10 while(n--){} 11} 12 13/* 14 * @description : 延时函数,在396Mhz的主频下 15 * 延时时间大约为1ms 16 * @param - n : 要延时的ms数 17 * @return : 无 18 */ 19void delay(volatileunsignedint n) 20{ 21 while(n--) 22 { 23 delay_short(0x7ff); 24 } 25} bsp_delay.c里面就两个函数,delay_short和delay,这两个其实就是第十二章中main.c里面的函数。 13.3.5修改main.c文件 在第十二章中,led驱动、延时驱动和时钟驱动相关的函数全部都写到了main.c中,本章我们在前几节已经将这些驱动根据功能模块放置到相应的地方,所以main.c里面的内容就得修改,将main.c里面的内容改为如下所示代码: 示例代码13.3.5.1 main.c文件代码 /************************************************************** Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved. 文件名 : mian.c 作者 : 左忠凯 版本 : V1.0 描述 : I.MX6U开发板裸机实验5 BSP形式的LED驱动 其他 : 本实验学习目的: 1、将各个不同的文件进行分类,学习如何整理工程、就和学习STM32一样创建工程 的各个文件夹分类,实现工程文件的分类化和模块化,便于管理。 2、深入学习Makefile,学习Makefile的高级技巧,学习编写通用Makefile。 论坛 : www.openedv.com 日志 : 初版V1.0 2019/1/4 左忠凯创建 **************************************************************/ 1 #include "bsp_clk.h" 2 #include "bsp_delay.h" 3 #include "bsp_led.h" 4 5/* 6 * @description : mian函数 7 * @param : 无 8 * @return : 无 9 */ 10int main(void) 11{ 12 clk_enable();/* 使能所有的时钟 */ 13 led_init();/* 初始化led */ 14 15 while(1) 16 { 17 /* 打开LED0 */ 18 led_switch(LED0,ON); 19 delay(500); 20 21 /* 关闭LED0 */ 22 led_switch(LED0,OFF); 23 delay(500); 24 } 25 26 return0; 27} 在main.c中我们仅仅留下了main函数,至此,本例程跟程序相关的内容就全部编写好了。 13.4编译下载验证13.4.1编写Makefile和链接脚本 在工程根目录下新建Makefile和imx6ul.lds这两个文件,创建完成以后的工程如图13.4.1.1所示: 图13.4.1最终的工程目录 在文件Makefile中输入如下所示内容: 示例代码13.4.1.1 Makefile文件代码 1 CROSS_COMPILE ?= arm-linux-gnueabihf- 2 TARGET ?= bsp 3 4 CC :=$(CROSS_COMPILE)gcc 5 LD :=$(CROSS_COMPILE)ld 6 OBJCOPY :=$(CROSS_COMPILE)objcopy 7 OBJDUMP :=$(CROSS_COMPILE)objdump 8 9 INCDIRS := imx6ul 10 bsp/clk 11 bsp/led 12 bsp/delay 13 14 SRCDIRS:= project 15 bsp/clk 16 bsp/led 17 bsp/delay 18 19 INCLUDE:=$(patsubst %, -I %, $(INCDIRS)) 20 21 SFILES :=$(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S)) 22 CFILES :=$(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c)) 23 24 SFILENDIR :=$(notdir $(SFILES)) 25 CFILENDIR :=$(notdir $(CFILES)) 26 27 SOBJS :=$(patsubst %, obj/%, $(SFILENDIR:.S=.o)) 28 COBJS :=$(patsubst %, obj/%, $(CFILENDIR:.c=.o)) 29 OBJS :=$(SOBJS)$(COBJS) 30 31 VPATH :=$(SRCDIRS) 32 33 .PHONY: clean 34 35$(TARGET).bin:$(OBJS) 36 $(LD) -Timx6ul.lds -o $(TARGET).elf $^ 37 $(OBJCOPY) -O binary -S $(TARGET).elf $@ 38 $(OBJDUMP) -D -m arm $(TARGET).elf >$(TARGET).dis 39 40$(SOBJS): obj/%.o : %.S 41 $(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ $< 42 43$(COBJS): obj/%.o : %.c 44 $(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ $< 45 46 clean: 47 rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS)$(SOBJS) 可以看出本章实验的Makefile文件要比前面的实验复杂很多,因为“示例代码13.4.1.1”中的Makefile代码是一个通用Makefile,我们以后所有的裸机例程都使用这个Makefile。使用时候只要将所需要编译的源文件所在的目录添加到Makefile中即可,我们接下来详细分析一下“示例代码13.4.1.1”中的Makefile源码: 第1~7行定义了一些变量,除了第2行以外其它的都是跟编译器有关的,如果使用其它编译器的话只需要修改第1行即可。第2行的变量TARGET目标名字,不同的例程肯定名字不一一样。 第9行的变量INCDIRS包含整个工程的.h头文件目录,文件中的所有头文件目录都要添加到变量INCDIRS中。比如本例程中包含.h头文件的目录有imx6ul、bsp/clk、bsp/delay和bsp/led,所以就需要在变量INCDIRS中添加这些目录,即: INCDIRS := imx6ul bsp/clk bsp/led bsp/delay 仔细观察的话会发现第9~11行后面都会有一个符号“”,这个相当于“换行符”,表示本行和下一行属于同一行,一般一行写不下的时候就用符号“”来换行。在后面的裸机例程中我们会根据实际情况来在变量INCDIRS中添加头文件目录。 第14行是变量SRCDIRS,和变量INCDIRS一样,只是SRCDIRS包含的是整个工程的所有.c和.S文件目录。比如本例程包含有.c和.S的目录有bsp/clk、bsp/delay、bsp/led和project,即: SRCDIRS := projectbsp/clk bsp/led bsp/delay 同样的,后面的裸机例程中我们也要根据实际情况在变量SRCDIRS中添加相应的文件目录。 第19行的变量INCLUDE是用到了函数patsubst,通过函数patsubst给变量INCDIRS添加一个“-I”,即: INCLUDE := -I imx6ul -I bsp/clk -I bsp/led -I bsp/delay 加“-I”的目的是因为Makefile语法要求指明头文件目录的时候需要加上“-I”。 第21行变量SFILES保存工程中所有的.s汇编文件(包含绝对路径),变量SRCDIRS已经存放了工程中所有的.c和.S文件,所以我们只需要从里面挑出所有的.S汇编文件即可,这里借助了函数foreach和函数wildcard,最终SFILES如下: SFILES := project/start.S 第22行变量CFILES和变量SFILES一样,只是CFILES保存工程中所有的.c文件(包含绝对路径),最终CFILES如下: CFILES = project/main.c bsp/clk/bsp_clk.c bsp/led/bsp_led.c bsp/delay/bsp_delay.c 第24和25行的变量SFILENDIR和CFILENDIR包含所有的.S汇编文件和.c文件,相比变量SFILES和CFILES,SFILENDIR和CFILNDIR只是文件名,不包含文件的绝对路径。使用函数notdir将SFILES和CFILES中的路径去掉即可,SFILENDIR和CFILENDIR如下: SFILENDIR = start.S CFILENDIR = main.c bsp_clk.c bsp_led.c bsp_delay.c 第27和28行的变量SOBJS和COBJS是.S和.c文件编译以后对应的.o文件目录,默认所有的文件编译出来的.o文件和源文件在同一个目录中,这里我们将所有的.o文件都放到obj文件夹下,SOBJS和COBJS内容如下: SOBJS = obj/start.o COBJS = obj/main.o obj/bsp_clk.o obj/bsp_led.o obj/bsp_delay.o 第29行变量OBJS是变量SOBJS和COBJS的集合,如下: OBJS = obj/start.o obj/main.o obj/bsp_clk.o obj/bsp_led.o obj/bsp_delay.o 编译完成以后所有的.o文件就全部存放到了obj目录下,如图13.4.1.1所示: 图13.4.1.1编译完成后的obj文件夹 第31行的VPATH是指定搜索目录的,这里指定的搜素目录就是变量SRCDIRS所保存的目录,这样当编译的时候所需的.S和.c文件就会在SRCDIRS中指定的目录中查找。 第34行指定了一个伪目标clean,伪目标前面讲解Makefile的时候已经讲解过了。 第35~47行就很熟悉了,前几章都已经详细的讲解过了。 “示例代码13.4.1.1”中的Makefile文件内容重点工作是找到要编译哪些文件?编译的.o文件存放到哪里?使用到的编译命令和前面实验使用的一样,其实Makefile的重点工作就是解决“从哪里来到哪里去的”问题,也就是找到要编译的源文件、编译结果存放到哪里?真正的编译命令很简洁。 链接脚本imx6ul.lds的内容基本和上一章一样,主要是start.o文件路径不同,本章所使用的imx6ul.lds链接脚本内容如下所示: 示例代码13.4.1.2 imx6ul.lds连接脚本 1 SECTIONS{ 2.=0X87800000; 3.text : 4{ 5 obj/start.o 6*(.text) 7} 8.rodata ALIGN(4):{*(.rodata*)} 9.data ALIGN(4):{*(.data)} 10 __bss_start =.; 11.bss ALIGN(4):{*(.bss)*(COMMON)} 12 __bss_end =.; 13} 注意第5行设置的start.o文件路径,这里和上一章的链接脚本不同。 13.4.2 编译下载 使用Make命令编译代码,编译成功以后使用软件imxdownload将编译完成的bsp.bin文件下载到SD卡中,命令如下: chmod 777 imxdownload //给予imxdownload可执行权限,一次即可 ./imxdownload bsp.bin /dev/sdd //烧写到SD卡中 烧写成功以后将SD卡插到开发板的SD卡槽中,然后复位开发板,如果代码运行正常的话LED0就会以500ms的时间间隔亮灭,实验现象和上一章一样。 |
|
相关推荐
|
|
1004 浏览 0 评论
AI模型部署边缘设备的奇妙之旅:如何在边缘端部署OpenCV
3272 浏览 0 评论
tms320280021 adc采样波形,为什么adc采样频率上来波形就不好了?
1443 浏览 0 评论
2102 浏览 0 评论
1614 浏览 0 评论
75262 浏览 21 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-27 19:42 , Processed in 0.500746 second(s), Total 61, Slave 44 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号