Linux之ARM(IMX6U)裸机官方SDK移植

发布者:自在堂最新更新时间:2021-10-29 来源: eefocus关键字:Linux  ARM 手机看文章 扫描二维码
随时随地手机看文章

1、I.MX6ULL 官方 SDK 包简介

NXP 针对 I.MX6ULL 编写了一个 SDK 包,这个 SDK 包就类似于 STM32 的 STD 库或者HAL 库,这个 SDK 包提供了 Windows 和 Linux 两种版本,分别针对主机系统是 Windows 和Linux。因为我们是在 Windows 下使用 Source Insight 来编写代码的,因此我们使用的是 Windows版本的。 Windows 版本 SDK 里面的例程提供了 IAR 版本,肯定有人会问既然 NXP 提供了 IAR版本的 SDK,那我们为什么不用 IAR 来完成裸机试验,偏偏要用复杂的 GCC?因为我们要从简单的裸机开始掌握 Linux 下的 GCC 开发方法,包括 Ubuntu 操作系统的使用、 Makefile 的编写、 shell 等等。如果为了偷懒而使用 IAR 开发裸机的话,那么后续学习 Uboot 移植、 Linux 移植和 Linux 驱动开发就会很难上手,因为开发环境都不熟悉!再者,不是所有的半导体厂商都会为 Cortex-A 架构的芯片编写裸机 SDK 包,我使用过那么多的 Cotex-A 系列芯片,也就发现了 NXP 给 I.MX6ULL 编写了裸机 SDK 包。而且去 NXP 官网看一下,会发现只有 I.MX6ULL这一款 Cotex-A 内核的芯片有裸机 SDK 包, NXP 的其它 Cotex-A 芯片都没有。说明在 NXP 的定位里面, I.MX6ULL 就是一个 Cotex-A 内核的高端单片机,定位类似 ST 的 STM32H7。说这么多的目的就是想告诉大家,使用 Cortex-A 内核芯片的时候不要想着有类似 STM32 库一样的东西, I.MX6ULL 是一个特例,基本所有的 Cortex-A 内核的芯片都不会提供裸机 SDK 包。因此在使用 STM32 的时候那些用起来很顺手的库文件,在 Cotex-A 芯片下基本都需要我们自行编写,比如.s 启动文件、寄存器定义等等


下载地址:

1.官方下载:https://www.nxp.com/

2.网盘:https://pan.baidu.com/s/17jrT_DhMZof4_KYHjgWOWg

提取码:8hdo


下载后安装,安装后SDK包如图:

在这里插入图片描述

我们不是讲解 SDK 包如何开发的,我们只是需要 SDK 包里面的几个文件,所以就不去详细的讲解这个 SDK 包了,感兴趣的可以看一下,所有的例程都在 boards 这个文件夹里面。我们重点是需要 SDK 包里面与寄存器定义相关的文件,一共需要如下三个文件:


fsl_common.h:位置为 SDK_2.2_MCIM6ULLdevicesMCIMX6Y2driversfsl_common.h。

fsl_iomuxc.h: 位置为 SDK_2.2_MCIM6ULLdevicesMCIMX6Y2driversfsl_iomuxc.h。

MCIMX6Y2.h: 位置为 SDK_2.2_MCIM6ULLdevicesMCIMX6Y2MCIMX6YH2.h。


整个 SDK 包我们就需要上面这三个文件,把这三个文件准备好,我们后面移植要用。


2、实验程序的编写

2.1、SDK文件的移植

使用 VSCode 新建工程,将 fsl_common.h、 fsl_iomuxc.h 和 MCIMX6Y2.h 这三个文件拷贝到工程中,这三个文件直接编译的话肯定会出错的!需要对其做删减,因为这三个文件里面的代码都比较大,所以就不详细列出这三个文件删减以后的内容了。


删减后的资源下载地址:


链接:https://pan.baidu.com/s/1FTyJe-AZUtjjXAlgUcHbIw

提取码:y2r4


2.2、创建cc.h文件

新建一个名为 cc.h 的头文件, cc.h 里面存放一些 SDK 库文件需要使用到的数据类型,在cc.h 里面输入如下代码:



#ifndef __CC_H

#define __CC_H



/*

 * 自定义一些数据类型供库文件使用

 */

#define     __I     volatile 

#define     __O     volatile 

#define     __IO    volatile


typedef   signed          char int8_t;

typedef   signed short     int int16_t;

typedef   signed           int int32_t;

typedef unsigned          char uint8_t;

typedef unsigned short     int uint16_t;

typedef unsigned           int uint32_t;

typedef unsigned long     long uint64_t;

typedef   signed char     s8;

typedef   signed short   int  s16;

typedef   signed int    s32;

typedef   signed long long int s64;

typedef unsigned char    u8;

typedef unsigned short int     u16;

typedef unsigned int    u32;

typedef unsigned long long int u64;


#endif


在 cc.h 文件中我们定义了很多的数据类型,因为有些第三方库会用到这些变量类型。


2.3、编写实验代码

新建 start.S 和 main.c 这两个文件


2.3.1、main.c

#include "fsl_common.h"

#include "fsl_iomuxc.h"

#include "MCIMX6Y2.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;


}


/*初始化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灯


}

/*短延时*/

void delay_short(volatile unsigned int n)

{

    while(n--){}


}

/*

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

 * n:延时ms数

*/

void delay(volatile unsigned int n)

{

    while (n--)

    {

        delay_short(0x7ff);

    }

    

}

/*打开LED灯*/

void  led_on(void)

{

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


}

/*关闭LED灯*/

void led_off(void )

{

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

}

int main() 

{

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


    led_init(); //初始化LED

    

    while(1)

    {

        led_off();  

        delay(1000);


        led_on();

        delay(1000);

    }


    return 0;

}


我们重点来看一下 led_init 函数中的第 22 行和第 25 行,这两行的内容如下:


IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0);

IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0X10B0);


这 里 使 用 了 两 个 函 数 IOMUXC_SetPinMux 和 IOMUXC_SetPinConfig , 其 中 函 数IOMUXC_SetPinMux 是 用 来 设 置 IO 复 用 功 能 的 , 最 终 肯 定 设 置 的 是 寄 存 器“IOMUXC_SW_MUX_CTL_PAD_XX”。函数 IOMUXC_SetPinConfig 设置的是 IO 的上下拉、速度等的,也就是寄存器“IOMUXC_SW_PAD_CTL_PAD_XX”,所以上面两个函数其实就是上一章中的:


IOMUX_SW_MUX->GPIO1_IO03 = 0X5;

IOMUX_SW_PAD->GPIO1_IO03 = 0X10B0;


函数 IOMUXC_SetPinMux 在文件 fsl_iomuxc.h 中定义,函数源码如下:


static inline void IOMUXC_SetPinMux(uint32_t muxRegister,

uint32_t muxMode,

uint32_t inputRegister,

uint32_t inputDaisy,

uint32_t configRegister,

uint32_t inputOnfield)

{

*((volatile uint32_t *)muxRegister) =

IOMUXC_SW_MUX_CTL_PAD_MUX_MODE(muxMode) |

IOMUXC_SW_MUX_CTL_PAD_SION(inputOnfield);

if (inputRegister)

{

*((volatile uint32_t *)inputRegister) =

IOMUXC_SELECT_INPUT_DAISY(inputDaisy);

}

}


函数 IOMUXC_SetPinMux 有 6 个参数,这 6 个参数的函数如下:


muxRegister : IO 的 复 用 寄 存 器 地 址 , 比 如 GPIO1_IO03 的 IO 复 用 寄 存 SW_MUX_CTL_PAD_GPIO1_IO03 的地址为 0X020E0068。


muxMode: IO 复用值,也就是 ALT0~ALT8,对应数字 0~8,比如要将 GPIO1_IO03 设置为 GPIO 功能的话此参数就要设置为 5。


inputRegister: 外设输入 IO 选择寄存器地址,有些 IO 在设置为其他的复用功能以后还需要设置 IO 输入寄存器,比如 GPIO1_IO03 要复用为 UART1_RX 的话还需要设置寄存器UART1_RX_DATA_SELECT_INPUT,此寄存器地址为0X020E0624。


inputDaisy: 寄存器 inputRegister 的值,比如 GPIO1_IO03 要作为 UART1_RX 引脚的话此参数就是 1。


configRegister:未使用,函数 IOMUXC_SetPinConfig 会使用这个寄存器。

inputOnfield : IO 软 件 输 入 使 能 , 以 GPIO1_IO03 为 例 就 是 寄 存 器SW_MUX_CTL_PAD_GPIO1_IO03 的 SION 位(bit4)。如果需要使能 GPIO1_IO03 的软件输入功能的话此参数应该为 1,否则的话就为 0。


第一次看到上面代码的时候肯定会奇怪,为何只有两个参数?不是应该 6 个参数的吗?不要着急,先看一个 IOMUXC_GPIO1_IO03_GPIO1_IO03 是个什么玩意。这是个宏,在文件fsl_iomuxc.h 中有定义, NXP 的 SDK 库将一个 IO 的所有复用功能都定义了一个宏,比如GPIO1_IO03 就有如下 9 个宏定义:


IOMUXC_GPIO1_IO03_I2C1_SDA

IOMUXC_GPIO1_IO03_GPT1_COMPARE3

IOMUXC_GPIO1_IO03_USB_OTG2_OC

IOMUXC_GPIO1_IO03_USDHC1_CD_B

IOMUXC_GPIO1_IO03_GPIO1_IO03

IOMUXC_GPIO1_IO03_CCM_DI0_EXT_CLK

IOMUXC_GPIO1_IO03_SRC_TESTER_ACK

IOMUXC_GPIO1_IO03_UART1_RX

IOMUXC_GPIO1_IO03_UART1_TX


上面 9 个宏定义分别对应着 GPIO1_IO03 的九种复用功能,比如复用为 GPIO 的宏定义就是:


#define IOMUXC_GPIO1_IO03_GPIO1_IO03 0x020E0068U, 0x5U, 0x00000000U, 0x0U, 0x020E02F4U


将这个宏带入到“示例代码 22 行以后就是:


IOMUXC_SetPinMux (0x020E0068U, 0x5U, 0x00000000U, 0x0U, 0x020E02F4U, 0);


这样就与函数 IOMUXC_SetPinMux 的 6 个参数对应起来了,如果我们要将 GPIO1_IO03 复用为 I2C1_SDA 的话就可以使用如下代码:


IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_I2C1_SDA, 0);


函数 IOMUXC_SetPinConfig,此函数同样在文件 fsl_iomuxc.h 中有定义,函数源码如下:


static inline void IOMUXC_SetPinConfig(uint32_t muxRegister,

uint32_t muxMode,

uint32_t inputRegister,

uint32_t inputDaisy,

uint32_t configRegister,

uint32_t configValue)

{

if (configRegister)

{

*((volatile uint32_t *)configRegister) = configValue;

}

}


函数 IOMUXC_SetPinConfig 有 6 个参数,其中前五个参数和函数 IOMUXC_SetPinMux 一样,但是此函数只使用了参数 configRegister 和 configValue, cofigRegister 参数是 IO 配置寄存器地址,比如 GPIO1_IO03 的 IO 配置寄存器为 IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03,其地址为 0X020E02F4,参数 configValue 就是要写入到寄存器 configRegister 的值。同理,25 行展开以后就是:


IOMUXC_SetPinConfig(0x020E0068U, 0x5U, 0x00000000U, 0x0U, 0x020E02F4U, 0X10B0);


根据函数 IOMUXC_SetPinConfig 的源码可以知道,上面函数就是将寄存器 0x020E02F4 的值设置为 0X10B0。函数 IOMUXC_SetPinMux 和 IOMUXC_SetPinConfig 就讲解到这里,我们以后就可以使用这两个函数来方便的配置 IO 的复用功能和 IO 配置。


2.3.2、start.s

.global _start

.global _bss_start

_bss_start:

    .word __bss_start

.global _bss_end

_bss_end:

    .word __bss_end


_start:


    /*设置处理器进入SVC模式 */

    mrs r0,cpsr      /*读取cpsr到r0 */

    bic r0,r0,#0x1f  /*清除cpsr的bit4--0 */

    orr r0,r0,#0x13  /*使用SVC模式 */

    msr cpsr,r0      /*将r0写入到cpsr中去 */


    /*清除bss段 */

    ldr r0, _bss_start

    ldr r1, _bss_end

    mov r2, #0


bss_loop:

    stmia r0!, {r2}

    cmp r0, r1

    ble bss_loop


    /*设置SP指针 */


    ldr sp,=0x80200000

    b main /*跳转到C语言main函数 */


2.3.3、编写链接脚本

SECTIONS

{

    . = 0x87800000;

    .text :

    {

        start.o

        *(.text)

    }

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

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

    __bss_start = .;

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

    __bss_end = .;

}


2.3.4、编写Makefile

CROSS_COMPILE  ?= arm-linux-gnueabihf-

NAME    ?= ledc


CC    := $(CROSS_COMPILE)gcc 

LD    := $(CROSS_COMPILE)ld

OBJCOPY    := $(CROSS_COMPILE)objcopy

OBJDUMP    := $(CROSS_COMPILE)objdump


OBJS           := start.o  main.o



$(NAME).bin : $(OBJS)

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

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

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


%.o : %.c 

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


%.o : %.s

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


clean:

rm -rf *.o $(NAME).elf $(NAME).bin $(NAME).dis


3.编译下载

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


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

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


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


具体参考前面几篇博文

关键字:Linux  ARM 引用地址:Linux之ARM(IMX6U)裸机官方SDK移植

上一篇:Linux之ARM(IMX6U)BSP工程管理实验
下一篇:Linux之ARM(IMX6U)裸机模仿STM32驱动开发格式

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

ARM 指令的寻址方式
1、立即寻址 操作数在指令中直接给出 ADD R0,R0,#1 ;R0 R0+1 ADD R0,R0,#0x3f ;R0 R0+0x3f 2、寄存器寻址 操作数在寄存器 ADD R0,R1,R2 ;R0 R1+R2 3、寄存器间接寻址 操作数的地址在寄存器 ADD R0,R1, ;R0 R1+ LDR R0, ;R0 STR R0, ; R0 4、基址变址寻址 操作数地址 = 基址寄存器 + 指令中给出的地址偏移 LDR R0, ;R0 LDR R0, ! ;R0 、R1 R1+4 LDR R0, ,#4 ;R0 、R1 R1+4 LDR R0, ;R0 5、多寄存器寻址 类似寄存器寻址
[单片机]
基于DSP和ARM的激光粒度仪关键电路设计
  O 引言   激光粒度仪是一种最先进的、最具有广泛发展前景的粒度测量仪器,它的测量原理基于米氏(Mie)散射理论。Mie散射理论是一个经典的光散射理论,它最大的特点是可用于任何尺寸段颗粒的测量,但它的计算相当复杂限制了数据处理速度及精度。   DSP技术实现MIE散射算法有很多优点:它是专为算法计算而设计的专用CPU,所以它运算速度很快;与通用CPU相比它成本低,所以有很好的性价比;而且它的体积小,能实现仪器一体化等等优点。ARM具有丰富的片上资源,适合嵌入式系统的开发,主要负责操作系统的运行、任务管理和协调以及DSP的控制任务,外部可扩展多种外设,如通用串口、LCD显示屏、以太网接口。   1 系统总体设计及工作原理
[单片机]
基于DSP和<font color='red'>ARM</font>的激光粒度仪关键电路设计
基于PXA255的ARM Linux操作系统移植
1.引言 ARM处理器是当今应用最为广泛的处理器芯片,它功耗小、成本低、性能优越,在消费电子类产品中占据主导地位。Linux操作系统近年来Linux移植/ARM在嵌入式领域中发展很快,由于其强大的性能和开源免费的特点,越来越受到嵌入式系统开发商的青睐,信息家电、网络设备、手持终端等都是嵌入式Linux应用的广大市场。 在Linux移植/ARM嵌入式开发中,把操作系统移植到开发板是进行嵌入式应用开发的前提和基础。ARM Linux是针对ARM体系结构的嵌入式Linux操作系统。本文主要阐述了将ARM Linux系统移植到基于PXA255处理器的Linux移植/ARM开发板CSB226上的方法和关键技术。
[单片机]
基于PXA255的<font color='red'>ARM</font> <font color='red'>Linux</font>操作系统<font color='red'>移植</font>
基于Linux操作系统的射频识别安检设计方案
引言   射频识别(RFID)是一种非接触式的自动识别技术,它通过射频信号自动识别目标对象并获取相关数据,识别工作无需人工干预,可工作于各种恶劣环境下。RFID技术可识别高速运动物体并可同时识别多个标签, 操作快捷方便。非接触IC卡是目前RFID系统中最常用的一种电子标签,它诞生于20世纪90年代初,是世界上最近几年发展起来的一项新技术,它成功地将射频识技术和IC卡技术结合起来,解决了无源和免接触这一难题,是电子器件领域的一大突破。由于存在着磁卡和接触式IC卡不可比拟的优点,使之一经问世,便立即引起广泛的关注,并以惊人的速度得到推广应用,如我国的第二代公民身份证、公交卡、ETC免停车付费卡等。可以说RFID技术越来越多地应用到
[网络通信]
基于ARM的APT控制系统设计
  空间光通信是以光波作为载波,在空间中进行信息无线传输的一种新型通信技术,其具有保密性高,抗干扰性强,通信速率高等优点,将会在卫星与卫星、卫星与地面控制站的无线通信领域发挥重要的作用,具有广阔的应用前景。但是由于光波波束窄,空间环境又比较复杂,而给通信链路的建立造成了极大的困难,所以对于空间光通信,必须先使用一套捕获、瞄准与跟踪(Acquisition,Pointing and Tracking,APT)系统来建立和维持光通信链路。嵌入式系统具有高性能、低功耗、低成本的优点,使其在运动控制上的应用具有很大优势,以ARM嵌入式处理器为基础的控制系统现在已经得到了广泛应用。针对目前卫星通信终端必须具有高实时性、高集成度、低功耗、体积
[单片机]
基于<font color='red'>ARM</font>的APT控制系统设计
从四个方面区别arm与fpga
概念上 ARM是应用,FPGA是芯片设计,前者是软件,后面是硬件,ARM就像单片机,但是它本身的资源是生产厂家固定了的,可以把它看成一个比较优秀的单片机来使用。 而 FPGA 需要通过自己编程,让它具备一切你想让他具备的功能。比如,你想让它是一个计数器,或者只是一个非门,那么这个芯片就是一个非门,只不过是个很昂贵的非门。你也可以在一款内部资源充分的FPGA 上,让这颗FPGA 成为ARM芯片,并且加上你想要加上的外设,比如网络,内存控制,LCD,等等,只要资源够用! 从意义上 ARM是可以是一种处理器,利用ARM架构体系的处理器,里面整合了很多现成的硬件资源供你编程调用。比如运算器、串口、usb接口等各种现成硬件。可以通
[单片机]
从四个方面区别<font color='red'>arm</font>与fpga
ARM设计的无线网卡设备驱动技术
随着移动通信和便携通信的发展,无线局域网WLAN日渐普及。嵌入式系统中无线局域网的接入,既可以实现对嵌人式系统的无线控制和数据传输,又可以满足一些特殊应用的场合。这里通过对USB无线网卡的Linux设备驱动的深入理解和分析,成功地移植在Atmel 9261 ARM处理器上。实现了嵌入式系统的无线局域网接入。利用该平台,可以进一步设计完善医用伽马相机和小型SPECT设备的手持数据采集系统,使得控制人员能够远离数据采集现场,而通过远程终端来控制现场数据和各种控制信号,较好地解决了安全性问题。 1 硬件系统构成 1.1 USB无线网卡介绍 无线网卡是无线局域网(WLAN)的重要组成部分,WLAN的物理层及MAC层是用无线网卡的硬件及
[单片机]
<font color='red'>ARM</font>设计的无线网卡设备驱动技术
专题1-MMU-lesson1-MMU功能解析
1、Memory Management Unit(存储器管理单元) 单片机与ARM在硬件体系上的一些区别:其中就有MMU的区别。 虚拟地址的使用 把p1.c复制成P2.c,把变量a改成b,再进行编译看看情况如何 可以看出两个程序在同样地址读出不同的值,在这里就用到了MMU。 在这里程序当中的地址都是虚拟地址;
[单片机]
专题1-MMU-lesson1-MMU功能解析
小广播
设计资源 培训 开发板 精华推荐

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

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

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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