正点原子 发表于 2020-5-29 15:02:26

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

本帖最后由 正点原子 于 2020-10-24 15:57 编辑

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




第十三章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/***************************************************************
4Copyright © 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/***************************************************************
5Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
6文件名    : bsp_led.h
7作者      : 左忠凯
8版本      : V1.0
9描述      : LED驱动头文件。
10其他      : 无
11论坛      : www.openedv.com12日志      : 初版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 #endifbsp_led.h的内容很简单,就是一些函数声明,在bsp_led.c中输入如下内容:示例代码13.3.2.2 bsp_led.c文件代码
1#include "bsp_led.h"
2/***************************************************************
3Copyright © 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/***************************************************************
4Copyright © 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 #endifbsp_clk.h很简单,在bsp_clk.c中输入内容:示例代码13.3.3.2 bsp_clk.c文件代码
1#include "bsp_clk.h"
2
3 /***************************************************************
4Copyright © 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 /***************************************************************
4Copyright © 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);
2021      /* 关闭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.1Makefile文件代码
1CROSS_COMPILE      ?= arm-linux-gnueabihf-
2TARGET         ?= bsp
3
4CC      :=$(CROSS_COMPILE)gcc
5LD      :=$(CROSS_COMPILE)ld
6OBJCOPY      :=$(CROSS_COMPILE)objcopy
7OBJDUMP      :=$(CROSS_COMPILE)objdump
8
9INCDIRS      := 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 $@ [      DISCUZ_CODE_444      ]lt;
42
43$(COBJS): obj/%.o : %.c
44      $(CC) -Wall -nostdlib -c -O2$(INCLUDE) -o $@ [      DISCUZ_CODE_444      ]lt;
45
46 clean:
47rm -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连接脚本
1SECTIONS{
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的时间间隔亮灭,实验现象和上一章一样。




页: [1]
查看完整版本: 【正点原子Linux连载】第十三章BSP工程管理实验--摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南