六.项目的BSP工程管理

随着我们的代码功能完善,在一个文件夹下放置所有文件已经不太合适了,针对我们上一章使用NXP提供的库来说,简简单单一个点亮LED的试验,目录结构就很乱了

六.项目的BSP工程管理

 

 在做复杂功能项目的时候,需要对目录结构进行优化,这里引入一个新概念——BSP(Board Support Package 板级支持包),目录的结构先这样做

先按照下面结构建立目录

六.项目的BSP工程管理

 

 其中,bsp用来放驱动文件,imx6u放和芯片有关的文件,obj编译以后生成的.o文件,project放C代码和汇编代码。先把前一章的几个头文件放在imx6u文件夹下,把start.s和main.c放在project里,然后有些代码,比方LED初始化,时钟初始化什么的,可以拆解出来写成独立的代码放在bsp根据功能不同可以放在bsp下几个按照功能建立的文件夹中。

这次的试验还和前面一样,点亮LED。

工程分解

为了以后的驱动开发方便,我们把各个功能模块分开放,

/bsp文件夹

bsp文件夹下放下面的文件

六.项目的BSP工程管理

 

 

clk为时钟管理,delay为定时,led为led的驱动,代码直接放在下面

clk文件夹下文件:

六.项目的BSP工程管理
#ifndef __BSP_CLK_H
#define __BSP_CLK_H
#include "imx6ul.h"

void clk_enable(void);
#endif
bsp_clk.h 六.项目的BSP工程管理
#include "bsp_clk.h"

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

delay文件夹下文件

六.项目的BSP工程管理
#ifndef __BSP_DELAY_H
#define __BSP_DELAY_H

#include "imx6ul.h"

void delay(volatile unsigned int n);

#endif
bsp_delay.h 六.项目的BSP工程管理
#include "bsp_delay.h"
// 空操作,演示大约1ms
void delay_short(volatile unsigned int n)
{
    while(n--){}
}

void delay(volatile unsigned int n)
{
    while(n--){delay_short(0x7ff);}
}
bsp_delay.c

led文件夹下文件:

六.项目的BSP工程管理
#ifndef __BSP_LED_H
#define __BSP_LED_H

#include "imx6ul.h"

#define LED0    0

void led_init(void);
void led_on(void);
void led_off(void);
void led_switch(int led, int status);


#endif
bsp_led.h 六.项目的BSP工程管理
#include "bsp_led.h"

/*初始化LED*/
void led_init(void)
{   
    // 复用、电气属性寄存器初始化

    IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03,0);
    IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO03_GPIO1_IO03,0x10B0);
    // GPIO1方向寄存器,
    GPIO1->GDIR = 0x8;
}


// 点亮LED
void led_on(void)
{
    GPIO1->DR &= ~(1<<3); //bit3清零
}


// 关闭LED
void led_off(void)
{
    GPIO1->DR |=(1<<3);  //bit3置一
}


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

/project文件夹

上面是基础的驱动,然后是project文件夹

六.项目的BSP工程管理

 

 

里面是主函数和汇编的环境配置

六.项目的BSP工程管理
.global _start

_start:
    MRS R0,CPSR
    BIC R0,R0,#0x1f
    ORR R0,R0,#0x13
    MSR CPSR,R0

    ldr sp,=0x80200000

    b main
start.s 六.项目的BSP工程管理
#include "bsp_led.h"
#include "bsp_delay.h"
#include "bsp_clk.h"

int main(void)
{
    clk_enable();
    led_init();
    
    while(1)
    {
        led_switch(LED0,ON);
        delay(200);
        led_switch(LED0,OFF);
        delay(200);

    }
    return 0;
}
main.c

/imx6ul文件夹

imx6ul是芯片的库文件

六.项目的BSP工程管理

 

 

 其中,cc.h是我们定义的数据类型,imx6ul.h是自己定义的头文件,这里定义这个头文件是简化了驱动库里导入头文件的方法,因为这里的几个库文件几乎在驱动的.h文件里几乎都要导入,定义了这个文件以后只用导入这个头文件就等于导入所有库文件了。

六.项目的BSP工程管理

六.项目的BSP工程管理
#ifndef __CC_H
#define __CC_H

#define __I     volatile
#define __O     volatile
#define __IO    volatile

#define ON      1
#define OFF     0

typedef signed char         int8_t;
typedef signed short        int16_t;
typedef signed int          int32_t;
typedef unsigned char       uint8_t;
typedef unsigned short      uint16_t;
typedef unsigned int        uint32_t;
typedef unsigned long long  uint64_t;

typedef signed char         s8;
typedef signed short        s16;
typedef signed int          s32;
typedef long long           s64;
typedef unsigned char       u8;
typedef unsigned short      u16;
typedef unsigned int        u32;
typedef unsigned long long  u64;
#endif
cc.h 六.项目的BSP工程管理
#ifndef __IMX6UL_H
#define __IMX6UL_H

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

#endif
imx6ul.h

 

Makefile文件

这里的Makefile文件是个重要点,因为在工程目录里所有的文件是按照路径管理的,在根目录下单Makefile就找不到需要编译的文件了。先把Makefile放下来

CROSS_COMPILE    ?= arm-linux-gnueabihf-
TARGET            ?= bsp

CC                := $(CROSS_COMPILE)gcc
LD                 := $(CROSS_COMPILE)ld
OBJCOPY            := $(CROSS_COMPILE)objcopy 
OBJDUMP            := $(CROSS_COMPILE)objdump 

INCLUDIRS        := imx6ul    \
                    bsp/clk    \
                    bsp/led \
                    bsp/delay

SRCDIRS            := project \
                    bsp/clk    \
                    bsp/led \
                    bsp/delay

# 利用patsubst进行字符串修改,在INCLUDIRS每个路径前加-I \
$(patsubst <pattern>,<replacement>,<text>) \
名称:模式字符串替换函数——patsubst。\
功能:查找<text>中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符\
合模式<pattern>,如果匹配的话,则以<replacement>替换。这里,<pattern>可以包括通\
配符“%”,表示任意长度的字串。如果<replacement>中也包含“%”,那么,<replacement>\
中的这个“%”将是<pattern>中的那个“%”所代表的字串。\
(可以用“\”来转义,以“\%”来表示真实含义的“%”字符)\
返回:函数返回被替换过后的字符串
INCLUDE            :=$(patsubst %, -I % ,$(INCLUDIRS))


#获取项目中.c、.s文件 \
foreach 类似BASH里的for循环,用法是 $(foreach <var>,<list>,<text>)    \
就是把list里的单词逐一取出放在变量var中,然后执行text的表达式内,每次text会返回一个字符串。            \
最后循环结束时,<text>返回值为每个字符串所组成的整个字符串,之间以空格分格,                \
wildcard为展开指定对象下文件集合
SFILES            := $(foreach dir ,$(SRCDIRS),$(wildcard $(dir)/*.s))
CFILES            := $(foreach dir ,$(SRCDIRS),$(wildcard $(dir)/*.c))

#获取不带路径的文件名,用来修改后缀生成.o文件的文件名
SFILENDIR        := $(notdir $(SFILES))        # 不带路径的文件名,
CFILENDIR        := $(notdir $(CFILES))        # 不带路径的文件名


# 将不带路径的文件名转换成.o后缀名,对应编译后文件名
SOBJS            := $(patsubst %, obj/%,$(SFILENDIR:.s=.o))    #.s=.o是把.s用.o替换
COBJS            := $(patsubst %, obj/%,$(CFILENDIR:.c=.o))      #.c=.o是把.c用.o替换

OBJS             := $(SOBJS) $(COBJS)

VPATH            := $(SRCDIRS)            

.PHONY: clean

$(TARGET).bin : $(OBJS)

    $(LD) -Timx6ul.lds -o $(TARGET).elf $^                   #将所有依赖文件链接,生成.elf文件
    $(OBJCOPY) -O binary -S $(TARGET).elf $@                #将elf转换为依赖的目标集合
    $(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis        #将elf文件反汇编


# 静态模式 <Targets...>:<tatgets-pattern>:<prereq-patterns...>下面两天为自写
$(SOBJS) :    obj/%.o    :    %.s  #将所有的.s文件编译成.o文件放在obj文件夹内
    $(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ $<

$(COBJS) : obj/%.o : %.c 
    $(CC) -Wall -nostdlib -c -O2  $(INCLUDE) -o $@ $<

clean:
    rm -rf $(TARGET).elf $(TARGET).bin $(TARGET).dis $(OBJS)

print:
    @echo INCLUDE = $(INCLUDE)
    @echo OBJS = $(OBJS)

大体的说明我都注释出来了,就是用了好几种函数

包括但不限于替换字符串的patsubst,循环结构的foreach,静态模式等,这个Makefile文件就作为后期驱动开发用到的通用Makefile了。整个文件结构要比以前用到的Makefile复杂了好多,但是按照注释看应该好理解。调试的时候在一个地方卡了很久:

SOBJS            := $(patsubst %, obj/%,$(SFILENDIR:.s=.o))    #.s=.o是把.s用.o替换

patsubst后面第一个百分号后多了个空格,编译的时候一直报错

六.项目的BSP工程管理

 

 并且后面用打印的伪目标调试也能打印出来对应的变量,一定要注意。

 链接脚本

注意看一下Makefile里的链接,我们使用了一个叫做链接脚本的文件来替换链接地址0x87800000。这个脚本就是根目录下单一个文件,拷贝在路径下就可以了,大致作用就是用于规定如何把输入文件内的section放入输出文件内, 并控制输出文件内各部分在程序地址空间内的布局. 但也可以用连接命令做一些其他事情,具体作用以后有机会再讲。

六.项目的BSP工程管理
SECTIONS{
    . = 0X87800000;
    .text :
    {
        obj/start.o 
        *(.text)
    }
    .rodata ALIGN(4) : {*(.rodata*)}     
    .data ALIGN(4)   : { *(.data) }    
    __bss_start = .;    
    .bss ALIGN(4)  : { *(.bss)  *(COMMON) }    
    __bss_end = .;
}
imx6ul.lds

 

上一篇:Linux学习笔记(15.4)——LED设备驱动之面向对象演进4(用设备树)


下一篇:STM32F103ZE点亮发光二极管例程