实验一 简单io应用—流水灯控制示例程序_「正点原子Linux连载」第十三章BSP工程管理实验...

本文介绍了Linux驱动开发中如何对工程进行管理,以流水灯控制为例,详细阐述了从工程目录整理、硬件原理分析到编写LED、时钟和延迟驱动代码的过程,以及编译下载验证的步骤。通过实验,学习如何将程序模块化,便于管理和阅读。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1)实验平台:正点原子Linux开发板

2)摘自《正点原子I.MX6U嵌入式Linux驱动开发指南
关注官方微信号公众号,获取更多资料:正点原子

70e4532e8992acf88bb0117640934229.png

第十三章BSP工程管理实验

在前面的章节中,我们都是将所有的源码文件刚到工程的根目录下,如果工程文件比较少的话这样做无可厚非,但是如果工程源文件达到几十、甚至数百个的时候,这样一股脑全部放到根目录下就会使工程显得混乱不堪。所以我们必须对工程文件做管理,将不同功能的源码文件放到不同的目录中。另外我们也需要将源码文件中,所有完成同一个功能的代码提取出来放到一个单独的文件中,也就是对程序分功能管理。本章我们就来学习一下如何对一个工程进行整理,使其美观、功能模块清晰、易于阅读。

13.1工程管理简介

打开我们上一章的工程根目录,如图13.1.1所示:

b3e472f5b494a95c0586bac5aa3c8bd1.png

图13.1.1工程根目录

在图13.1.1中我们将所有的源码文件都放到工程根目录下,即使这个工程只是完成了一个简单的流水灯的功能,但是其工程根目录下的源码文件就已经不少了。如果在添加一些其他的功能文件,那么文档就会更大,显得很混乱,所以我们需要对这个工程进行整理,将源码文件分模块、分功能整理。我们可以打开一个STM32的例程,如图13.1.2所示:

b47235da35f0aa69b29e15c268e9a4ef.png

图13.1.2 STM32F103例程工程文件

图13.1.2中的工程目录就很美观、不同的功能模块文件放到不同的文件夹中,比如驱动文件就放到HARDWARE文件夹中,ST的官方库就放到STM32F10x_FWLib文件夹中,编译产生的过程文件放到OBJ文件夹中。我们可以参考这个工程目录结构来整理第十二章的例程工程,新建名为“5_ledc_bsp”的文件夹,在里面新建bsp、imx6ul、obj和project这4个文件夹,完成以后如图13.1.3所示:

0f2f02a40e5b5944af276d795b3bff28.png

图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所示:

68460adac438ac928901d9aeae74c8ef.png

图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所示:

6027fe7b9af6e78d6ce160965db60bcf.png

图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、付费专栏及课程。

余额充值