I.MX6ULL的BSP工程管理实验

系列文章目录

I.MX6ULL的BSP工程管理实验



一、前言

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


二、工程管理简介

打开之前的工程根目录,如图所示:

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

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

在这里插入图片描述

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 中提取出来做成一个独立的驱动文件 ,整理完成如图。
在这里插入图片描述


三、实验程序编写

还是以之前的点灯为例子,点灯例子,使用 VScode 新建工程,工程名字为“ledc_bsp”。

3.1创建 imx6ul.h 文件

新建文件 imx6ul.h,然后保存到文件夹 imx6ul 中,在 imx6ul.h 中输入如下内容:

#ifndef __IMX6UL_H
#define __IMX6UL_H

#include "cc.h"
#include "MCIMX6Y2.h"
#include "fsl_common.h"
#include "fsl_iomuxc.h"

#endif

文件 imx6ul.h 很简单,就是引用了一些头文件,以后我们就可以在其他文件中需要引用
imx6ul.h 就可以了。

3.2编写 led 驱动代码

新建 bsp_led.h 和 bsp_led.c 两个文件,将这两个文件存放到 bsp/led 中,在 bsp_led.h 中输入输入如下内容:

#ifndef __BSP_LED_H
#define __BSP_LED_H
#include "imx6ul.h"

#define LED0 0

/* 函数声明 */
void led_init(void);
void led_switch(int led, int status);
#endif

bsp_led.h 的内容很简单,就是一些函数声明,在 bsp_led.c 中输入如下内容:

#include "bsp_led.h"
/*
* @description : 初始化 LED 对应的 GPIO
* @param : 无
* @return : 无
*/
void led_init(void)
{
/* 1、初始化 IO 复用 */
IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03,0); 

/* 2、、配置 GPIO1_IO03 的 IO 属性 */
IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO03_GPIO1_IO03,0X10B0);

/* 3、初始化 GPIO,GPIO1_IO03 设置为输出*/
GPIO1->GDIR |= (1 << 3); 

/* 4、设置 GPIO1_IO03 输出低电平,打开 LED0*/
GPIO1->DR &= ~(1 << 3); 
}

/*
* @description : LED 控制函数,控制 LED 打开还是关闭
* @param - led : 要控制的 LED 灯编号
* @param - status : 0,关闭 LED0,1 打开 LED0
* @return : 无
*/
void led_switch(int led, int status)
{ 
	switch(led)
	{
		case LED0:
		if(status == ON)
			GPIO1->DR &= ~(1<<3); /* 打开 LED0 */
		else if(status == OFF)
			GPIO1->DR |= (1<<3); /* 关闭 LED0 */
		break;
	}
}

bsp_led.c 里面就两个函数 led_init 和 led_switch,led_init 函数用来初始化 LED 所使用的IO,led_switch 函数是控制 LED 灯的打开和关闭,这两个函数都很简单。

3.3编写时钟驱动代码

新建 bsp_clk.h 和 bsp_clk.c 两个文件,将这两个文件存放到 bsp/clk 中,在 bsp_clk.h 中输入如下内容:

#ifndef __BSP_CLK_H
#define __BSP_CLK_H

#include "imx6ul.h"
/* 函数声明 */
void clk_enable(void);

#endif

bsp_clk.h 很简单,在 bsp_clk.c 中输入内容:

#include "bsp_clk.h"
/*
* @description : 使能 I.MX6U 所有外设时钟
* @param : 无
* @return : 无
*/
void clk_enable(void)
{
CCM->CCGR0 = 0XFFFFFFFF;
CCM->CCGR1 = 0XFFFFFFFF;
CCM->CCGR2 = 0XFFFFFFFF;
CCM->CCGR3 = 0XFFFFFFFF;
CCM->CCGR4 = 0XFFFFFFFF;
CCM->CCGR5 = 0XFFFFFFFF;
CCM->CCGR6 = 0XFFFFFFFF;
}

bsp_clk.c 只有一个 clk_enable 函数,用来使能所有的外设时钟。

3.4编写延时驱动代码

新建 bsp_delay.h 和 bsp_delay.c 两个文件,将这两个文件存放到 bsp/delay 中,在 bsp_delay.h中输入输入如下内容:

#ifndef __BSP_DELAY_H
#define __BSP_DELAY_H
#include "imx6ul.h"

/* 函数声明 */
void delay(volatile unsigned int n);

#endif

在 bsp_delay.c 中输入内容:

1 #include "bsp_delay.h"
2 
3 /*
4 * @description : 短时间延时函数
5 * @param - n : 要延时循环次数(空操作循环次数,模式延时)
6 * @return : 无
7 */
8 void delay_short(volatile unsigned int n)
9 {
10 while(n--){}
11 }
12
13 /*
14 * @description : 延时函数,在 396Mhz 的主频下
15 * 延时时间大约为 1ms
16 * @param - n : 要延时的 ms 数
17 * @return : 无
18 */
19 void delay(volatile unsigned int n)
20 {
21 while(n--)
22 {
23 delay_short(0x7ff);
24 }
25 }

bsp_delay.c 里面就两个函数,delay_short 和 delay,这两个其实就是第十二章中 main.c 里面的函数。

3.5修改 main.c 文件

1 #include "bsp_clk.h"
2 #include "bsp_delay.h"
3 #include "bsp_led.h"
4 
5 /*
6 * @description : main 函数
7 * @param : 无
8 * @return : 无
9 */
10 int 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 return 0;
27 }

在 main.c 中我们仅仅留下了 main 函数,至此,本例程跟程序相关的内容就全部编写好了。相较于之前杂乱的main.c,现在干净整洁多了

四、编译下载验证

4.1编写 Makefile 和链接脚本

在工程根目录下新建 Makefile 和 imx6ul.lds 这两个文件

在文件 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 文件要比前面的实验复杂很多,因为“代码中的 Makefile 代码是一个通用 Makefile,我们以后所有的裸机例程都使用这个 Makefile。使用时候只要将所需要编译的源文件所在的目录添加到 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 := project bsp/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 所示
在这里插入图片描述第 31 行的 VPATH 是指定搜索目录的,这里指定的搜素目录就是变量 SRCDIRS 所保存的目录,这样当编译的时候所需的.S 和.c 文件就会在 SRCDIRS 中指定的目录中查找。
第 33 行指定了一个伪目标 clean,伪目标前面讲解 Makefile 的时候已经讲解过了。
代码中的 Makefile 文件内容重点工作是找到要编译哪些文件?编译的.o文件存放到哪里?使用到的编译命令和前面实验使用的一样,其实 Makefile 的重点工作就是解决“从哪里来到哪里去的”问题,也就是找到要编译的源文件、编译结果存放到哪里?真正的编译命令很简洁。


链接脚本 imx6ul.lds 的内容基本和之前一样,主要是 start.o 文件路径不同,本次实验所使用的 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 文件路径,这里和之前的不同


4.2编译下载

使用 Make 命令编译代码,编译成功以后使用软件 imxdownload 将编译完成的 bsp.bin 文件下载到 SD 卡中,命令如下:

chmod 777 imxdownload //给予 imxdownload 可执行权限,一次即可
./imxdownload bsp.bin /dev/sdd //烧写到 SD 卡中,不能烧写到/dev/sda 或 sda1 设备里面!

烧写成功以后将 SD 卡插到开发板的 SD 卡槽中,然后复位开发板,如果代码运行正常的
话 LED0 就会以 500ms 的时间间隔亮灭.


END
在这里插入图片描述

  • 30
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值