Linux之ARM(IMX6U)BSP工程管理实验

发布者:咖啡狐狸最新更新时间:2021-10-27 来源: eefocus关键字:Linux  ARM 手机看文章 扫描二维码
随时随地手机看文章

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


1、工程管理简介

在我们上一篇博客中(Linux之ARM(MX6U)裸机官方SDK移植)中,文件有:

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

图 中的工程目录就很美观、不同的功能模块文件放到不同的文件夹中,比如驱动文件就放到bsp文件夹中, 官方库就放到 imx6ul文件夹中,编译产生的过程文件放到 OBJ 文件夹中


我们参考这个来整理一下(Linux之ARM(MX6U)裸机官方SDK移植)中的文件。


1.1、创建bsp、imx6ul、obj和project这四个文件夹

其中 bsp 用来存放驱动文件; imx6ul 用来存放跟芯片有关的文件,比如 NXP 官方的 SDK库文件; obj 用来存放编译生成的.o 文件; project 存放 start.S 和 main.c 文件,也就是应用文件;


1.2、文件分类

将(Linux之ARM(MX6U)裸机官方SDK移植)实验中的 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 中提取出来做成一个独立的驱动文件 。


2、实验程序编写

使用 VScode 新建工程,工程名字为“ledc_bsp”。

2.1、创建 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 就可以了。


2.2、创建个.vscode文件修改includePath

是为了vscode解决找不到工程的头文件的问题

在终端创建“ .vscode文件夹 ”,打开c_cpp_properties.json文件


2.2.1、修改includePath

把我们的头文件的文件夹路径添加进去

这样刷新一下,就可以包含所需的头文件了


2.3、编写led驱动文件

新建 bsp_led.h 和 bsp_led.c 两个文件,将这两个文件存放到 bsp/led 中,


2.2.1、 bsp_led.h

#ifndef __BSP_LED_H

#define __BSP_LED_H


#include "imx6ul.h"


#define LED0 0


/*函数声明*/

void  led_on(void);

void led_off(void );

void led_init(void);

void led_switch(int led, int status);


#endif /* __BSP_LED_H */


bsp_led.h 的内容很简单,就是一些函数声明和头文件的引用


2.2.1、 bsp_led.c

#include "bsp_led.h"



/*打开LED灯*/

void  led_on(void)

{

    GPIO1->DR&= ~(1<<3); //bit3清零


}

/*关闭LED灯*/

void led_off(void )

{

    GPIO1->DR |= (1<<3);  //bit3置1

}


/*初始化LED灯*/

void led_init(void)

{


    IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03,0); /*复用为GPIO--IO03 */



    IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO03_GPIO1_IO03,0x10B0);/*设置GPIO__IO03电器属性*/


    GPIO1->GDIR=0x8;//设置为输出


    GPIO1->DR=0x0; //设置为低电平,打开LED灯


}


/*LED 灯控制函数*/

void led_switch(int led, int status)

{

    switch(status)

    {

        case  LED0:

            if(status == ON)

                GPIO1->DR |= (1<<3);  //bit3置1

            else if(status == OFF)

                GPIO1->DR&= ~(1<<3); //bit3清零

            break;


    }



}


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


2.4、编写时钟驱动代码

新建 bsp_clk.h 和 bsp_clk.c 两个文件,将这两个文件存放到 bsp/clk 中


2.4.1、bsp_clk.h

#ifndef __BSP_CLK_H

#define __BSP_CLK_H


#include "imx6ul.h"


void clk_enable(void);


#endif // !__BSP_CLK_H


2.4.2、bsp_clk.c

#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 只有一个 clk_enable 函数,用来使能所有的外设时钟。


2.5、编写延时驱动代码

新建 bsp_delay.h 和 bsp_delay.c 两个文件,将这两个文件存放到 bsp/delay 中


2.5.1、bsp_delay.h

#ifndef __BSP_DELAY_H

#define __BSP_DELAY_H


#include "imx6ul.h"


void delay_short(volatile unsigned int n);

void delay(volatile unsigned int n);


#endif // !__BSP_CLK_H


2.5.2、bsp_delay.c

#include "bsp_delay.h"


/*短延时*/

void delay_short(volatile unsigned int n)

{

    while(n--){}


}

/*

 * 延时  一次循环大概是1ms 在主频396MHz

 * n:延时ms数

*/

void delay(volatile unsigned int n)

{

    while (n--)

    {

        delay_short(0x7ff);

    }

    

}


2.6、修改main.c代码

2.6.1、创建一个main.h文件,用来包含头文件

main.h


#ifndef __MAIN_H

#define __MAIN_H



#include "imx6ul.h"

#include "bsp_clk.h"

#include "bsp_delay.h"

#include "bsp_led.h"


#endif // !__MAIN_H


2.6.2、main.c

#include "main.h"


int main() 

{

    clk_enable();  //使能外设时钟


    led_init(); //初始化LED

    

    while(1)

    {

        led_off();  

        delay(1000);


        led_on();

        delay(1000);

    }


    return 0;

}


在 main.c 中我们仅仅留下了 main 函数,至此,本例程跟程序相关的内容就全部编写好了


3、编译下载与验证

3.1、编写链接文件


SECTIONS

{

    . = 0x87800000;

    .text :

    {

        obj/start.o

        *(.text)

    }

    .rodata ALIGN(4) : {*(.rodata*)}

    .data ALIGN(4) : {*(.data)}

    __bss_start = .;

    .bss ALIGN(4) : { *(.bss) *(COMMON)}

    __bss_end = .;

}


注意第 5 行设置的 start.o 文件路径


3.2、编写Makefile文件

CROSS_COMPILE ?= arm-linux-gnueabihf-

TARGET   ?= bsp


CC := $(CROSS_COMPILE)gcc

LD := $(CROSS_COMPILE)ld

OBJCOPY := $(CROSS_COMPILE)objcopy

OBJDUMP := $(CROSS_COMPILE)objdump


INCDIRS := imx6ul

   bsp/clk

   bsp/led

   bsp/delay 

       

SRCDIRS := project

   bsp/clk

   bsp/led

   bsp/delay 

   

   

INCLUDE := $(patsubst %, -I %, $(INCDIRS))


SFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.s))

CFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))


SFILENDIR := $(notdir  $(SFILES))

CFILENDIR := $(notdir  $(CFILES))


SOBJS := $(patsubst %, obj/%, $(SFILENDIR:.S=.o))

COBJS := $(patsubst %, obj/%, $(CFILENDIR:.c=.o))

OBJS := $(SOBJS) $(COBJS)


VPATH := $(SRCDIRS)


.PHONY: clean

$(TARGET).bin : $(OBJS)

$(LD) -Timx6ul.lds -o $(TARGET).elf $^

$(OBJCOPY) -O binary -S $(TARGET).elf $@

$(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis


$(SOBJS) : obj/%.o : %.s

$(CC) -Wall -nostdlib -c -O2  $(INCLUDE) -o $@ $<


$(COBJS) : obj/%.o : %.c

$(CC) -Wall -nostdlib -c -O2  $(INCLUDE) -o $@ $<

clean:

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 目录下

第 31 行的 VPATH 是指定搜索目录的,这里指定的搜素目录就是变量 SRCDIRS 所保存的目录,这样当编译的时候所需的.s 和.c 文件就会在 SRCDIRS 中指定的目录中查找。


第 33 行指定了一个伪目标 clean。


第 35~47 行就很熟悉了


3.3、编译下载

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


chmod 777 imxdownload //给予 imxdownload 可执行权限,一次即可

./imxdownload bsp.bin /dev/sdd //烧写到 SD 卡中


烧写成功以后将 SD 卡插到开发板的 SD 卡槽中,然后复位开发板,如果代码运行正常的

话 LED0 就会以 1000ms 的时间间隔亮灭

关键字:Linux  ARM 引用地址:Linux之ARM(IMX6U)BSP工程管理实验

上一篇:Linux之ARM(IMX6U)裸机C语言蜂鸣器驱动实验--驱动编写,编译
下一篇:Linux之ARM(IMX6U)裸机官方SDK移植

推荐阅读最新更新时间:2024-11-06 10:28

ARM2440的启动模式
研究arm也有2个月了,现在才感觉理解了arm在Nand flash模式下的启动过程,现在来这里记录下来以表达我无比喜悦的心情。闲话少说,趁着还没有忘记学习过程中的感受,直接进入正题。 大家都知道,arm在Nand flash启动模式下启动时系统会将Nand flash中的前4KB代码拷贝到SRAM(也就是Steppingstone中),由SRAM配置中断向量表和完成Nand flash访问的必要初始化,然后将Nand flash中的全部程序代码拷贝到SDRAM中,最后由SRAM跳转到SDRAM,然后程序就正常执行了,这一过程看上去很简单,但是真正理解这一过程还是不简单的,尽管这样,还是想告诉大家仔细理解还是比较容易理解这个过程
[单片机]
基于ARM的宿舍智能安防监测系统设计
摘要:该设计是基于ARM Cortex—M3处理器内核LM3S1138为主控制器的宿舍智能防火防盗报警系统。该系统可以判断宿舍是否发生火情,检测人员进出及非法入室情况,监测不同贵重物品的移动情况。宿舍节点控制器接收信号实现声光提示和液晶显示,并通过无线通讯模块传送给监控机实现异地监控,一部监控机可以远程监控多个宿舍,有利于学生宿舍的安全管理。 随着社会的不断发展和高校的扩招,校园的安全隐患层出不穷,特别是学生宿舍安全问题越来越受到各个高校的重视。为解决该问题,很多高校采用雇佣安全人员巡视的方案,但成本颇高且效果不佳。部分高校则采取安装宿舍监控摄像头的方法,虽然达到了一些效果,但引起了广大师生对自身隐私权受到侵犯的质疑。考虑以上
[单片机]
基于<font color='red'>ARM</font>的宿舍智能安防监测系统设计
ARM开发板的调试方法概述
  用户选用ARM处理器开发嵌入式系统时,选择合适的开发工具可以加快开发进度,节省开发成本。因此一套含有编辑软件、编译软件、汇编软件、链接软件、调试软件、工程管理及函数库的集成开发环境(IDE)一般来说是必不可少的,至于嵌入式实时操作系统、评估板等其他开发工具则可以根据应用软件规模和开发计划选用。   使用集成开发环境开发基于ARM的应用软件,包括编辑、编译、汇编、链接等工作全部在PC机上即可完成,调试工作则需要配合其他的模块或产品方可完成,目前常见的调试方法有以下几种:   1、指令集模拟器   部分集成开发环境提供了指令集模拟器,可方便用户在PC机上完成一部分简单的调试工作,但是由于指令集模拟器与真实的硬件环境相差很大,因此
[单片机]
ARM裸机程序之存储管理器控制SDRAM
文讲的是s3c2440A芯片的存储管理器,配套的开发板是友善之臂mini2440,首先贴出代码 head.s的代码: .equ MEM_CTL_BASE, 0x48000000 @定义13个寄存器的首地址 .equ SDRAM_BASE, 0x30000000 @定义SDRAM的首地址 .text .global _start _start: bl disable_watch_dog bl memsetup bl copy_steppingstone_to_sdram @把代码从片内的SRAM复制到SDRAM里面 ldr pc, =on_sdram on_sdram: ldr
[单片机]
ARM9(S3C2440)添加驱动的三种方法
(这里我就以beep驱动为例子) 方式一:动态添加(不推荐) 先下载或者找到驱动,一个是.c文件另一个是Makefile(注意makefile里面的命令是要修改的,参考下面的改),将两个文件储存到一个文件夹下,然后make编译,将.ko文件复制到开发板的S3C2440_recover_nogui 的home文件下使用命令insmod+drivername.ko(注释:insmod是指载入模块),利用命令lsmod查看。完毕。 方式二: 先将驱动的.c文件拷贝到/utu-Linux2.6.24_for_utu2440_2009-07-18/drivers/char目录下然后再此目录下的中的Makefile文件中添加 Obj –m
[单片机]
给<font color='red'>ARM</font>9(S3C2440)添加驱动的三种方法
基于ARM处理器的TSC2046触摸屏控制器的应用
0 引言 随着信息技术的不断发展,嵌入式系统正在越来越广泛地应用到消费类电子、通信设备等便携式电子类产品中。触摸屏由于其轻便、占用空间少、灵活等优点,已经逐渐取代键盘,成为嵌入式系统中最简单、方便、自然的一种人机交互方式。触摸屏分为电阻、电容、表面声波、红外线扫描等类型,其中使用最多的是四线或五线电阻触摸屏。四线电阻触摸屏是由两个透明电阻膜构成的,在它的水平和垂直电阻网上施加电压,就可通过转换面板在触摸点测量出电压而对应出坐标值。 TSC2046是典型的逐次逼近寄存器型A/D变换器,其结构以电容再分布为基础,包含了取样/保持功能,支持低电压的I/O接口。本文介绍了利用飞利浦公司的LPC2100系列ARM芯片LPC2132、T
[单片机]
基于<font color='red'>ARM</font>处理器的TSC2046触摸屏控制器的应用
英伟达CEO对收购Arm信心满满,欧盟却表示十分担忧
近期在台北电脑展的线上交流环节中,英伟达 CEO 黄仁勋再次谈及 400 亿 美金收购 Arm 交易。黄仁勋表示,对这笔交易最终达成很有信心,因为英伟达与 Arm 是互补的,二者走到一起会迸发出更多创新,这有利于促进竞争,是政府愿意看到的。同时,黄仁勋预计这笔交易将需要 18 个月完成,也就是今年年底或者明年年初。 对此,欧盟相关官员表示担忧。主管欧盟内部市场的欧委会委员布雷顿在接受美国媒体采访时表示,他“紧紧盯着”这项交易,并且“极其了解”这背后的战略意义。 欧盟为什么如此担忧? 英伟达在 2020 年 9 月宣布了 400 亿美元收购 Arm 的计划,这让欧盟感受到威胁。Arm 为全球 500 多家企业提供芯片设计
[半导体设计/制造]
Part2_lesson1---arm家族大检阅
芯片(比如2440、6410、210等等)包含ARM核。 指令结构和ARM核有关系: ARM9对应指令架构版本ARMV4 ARM11对应指令架构版本ARMV6 cortex A8对应指令架构版本ARMV7 6410芯片的概况: 2440芯片的概况: 210芯片的概况:
[单片机]
Part2_lesson1---<font color='red'>arm</font>家族大检阅
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved