【正点原子Linux连载】第十三章BSP工程管理实验--摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

1)实验平台:正点原子阿尔法Linux开发板
2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434
2)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-300792-1-1.html
3)对正点原子Linux感兴趣的同学可以加群讨论:935446741
4)关注正点原子公众号,获取最新资料更新
在这里插入图片描述

第十三章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所示:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210315155532536.png#pic_center
在这里插入图片描述

图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的时间间隔亮灭,实验现象和上一章一样。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值