历史上的今天
返回首页

历史上的今天

今天是:2024年10月29日(星期二)

正在发生

2021年10月29日 | Linux之ARM(IMX6U)裸机官方SDK移植

2021-10-29 来源:eefocus

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 的时间间隔亮灭


具体参考前面几篇博文

推荐阅读

史海拾趣

Asian Best Components Co Ltd公司的发展小趣事

品质是Asian Best Components Co Ltd的生命线。公司自成立之初就高度重视品质管理,建立了严格的质量控制体系。从原材料的采购到生产过程的每一个环节,再到产品的最终检验,公司都严格把关,确保每一件产品都符合高标准的质量要求。这种对品质的执着追求,使得Asian Best的产品在市场上赢得了广泛的认可和信赖,也铸就了公司的良好品牌形象。

Elprotronic Inc.公司的发展小趣事

品质管理一直是Elprotronic Inc.非常重视的一项工作。公司建立了一套完善的品质管理体系,从原材料采购到产品生产、检测、包装等各个环节都进行严格把关。同时,公司还引入了先进的生产设备和技术手段,提高了产品的生产效率和质量水平。此外,公司还定期组织员工进行品质管理培训和学习交流活动,不断提升员工的品质意识和技能水平。这些措施的实施使得Elprotronic Inc.的产品质量得到了进一步提升和保障。

GeneSiC Semiconductor公司的发展小趣事

品质管理一直是Elprotronic Inc.非常重视的一项工作。公司建立了一套完善的品质管理体系,从原材料采购到产品生产、检测、包装等各个环节都进行严格把关。同时,公司还引入了先进的生产设备和技术手段,提高了产品的生产效率和质量水平。此外,公司还定期组织员工进行品质管理培训和学习交流活动,不断提升员工的品质意识和技能水平。这些措施的实施使得Elprotronic Inc.的产品质量得到了进一步提升和保障。

AXSEM公司的发展小趣事

随着电子行业的不断发展,客户对电子元器件的需求也在不断变化。Elprotronic Inc.紧跟市场趋势,不断推出具有创新性的产品和技术。其中,一项名为“高速数据传输技术”的创新成果引起了业界的广泛关注。这项技术可以大幅提高数据传输速度,同时保证数据的稳定性和安全性。这一突破性的技术创新为Elprotronic Inc.赢得了更多客户的信赖和支持,也进一步巩固了公司在行业中的地位。

CIF公司的发展小趣事

E公司是一家注重绿色环保的电子产品CIF公司。在生产过程中,E公司积极采用环保材料和节能技术,努力降低产品对环境的影响。同时,E公司还积极参与国际环保组织的活动,推动电子行业的绿色发展。这种负责任的态度赢得了客户和市场的广泛认可。

这些故事仅用于说明电子行业CIF公司可能的发展路径和策略,并不代表现实中任何具体公司的真实经历。实际上,每个公司的发展都受到多种因素的影响,包括市场环境、竞争态势、技术创新等。因此,在现实中,电子行业CIF公司的发展故事会更加丰富多彩和复杂多变。

Hamamatsu公司的发展小趣事

B公司原本是一家地区性的电子产品供应商,为了拓展国际市场,决定采用CIF交易模式。通过与国外客户的深入沟通和合作,B公司成功打开了多个海外市场。同时,B公司还积极参与国际电子产品展会,提升品牌知名度,进一步巩固了其在全球市场的地位。

问答坊 | AI 解惑

飞利浦最新推出 超薄时尚GPS车载导航仪

随着科技水平的飞速发展,自助旅行已经逐渐成为一种时尚,而为自己的爱车搭载一款优良的GPS卫星导航产品也成为一种日常需要。为了满足人们在这方面的需求,车载导航产品逐渐走进人们的视线,其市场迅速火热起来,并有不断扩大的趋势。面对日益扩大 ...…

查看全部问答>

求助:冠林梯口机原程序

冠林梯口机原程序!!!!…

查看全部问答>

【FPGA设计实例】(7,4)线性分组码译码器

module decoder1(c,y,clk);output[6:0] c;input[6:0] y;input clk;reg[2:0] s;reg[6:0] e,c;always @(posedge clk) begins[0] = y[0] ^ y[3] ^ y[5] ^ y[6];s[1] = y[1] ^ y[3] ^ y[4] ^ y[5];s[2] = y[2] ^ y[4] ^ y[5] ^ y[6]; //s[0]~ s[2]为伴 ...…

查看全部问答>

vxworks NAT的问题 .只要回复都给分...分不够再加...

版上的各位大侠,有人在vxworks5.4下做过NAT的东西没,能给小弟一点提示吗,现在这个东西已径把我折磨的半死了。 也可以把资料发到我的email中。zhouxiongjun@hotmail.com 谢谢啦。 …

查看全部问答>

wince 自动更新应用程序

请教大虾们如何实现如下功能: 平台:wince6.0 假如现在已经有一个应用程序myap.exe运行了。当我插入sd卡时(sk卡上有新版的myap.exe),点击更新程序按钮时,把sd卡新版本的myap.exe程序拷贝到旧版本的myap.exe处(把旧版的覆盖掉),然后自动运 ...…

查看全部问答>

各位大虾,请教一个内核移植的问题,

用的是linux的内核,ARM的CPU 我用make zImage编译好2.6.13的内核文件后,把/arch/arm/boot/zImage 文件烧进板子后,linux解压出错.错误信息如下:   VIVI version 0.1.4 (root@localhost.localdomain) (gcc version 2.95.2 20000516 (re ...…

查看全部问答>

debug调试中view----》terminalIO怎么用呢

                                  …

查看全部问答>

请教MSP430F149的谐波检测FFT的C语言程序

最近在用MSP430F149做一个谐波检测的功能,听说FFT能够实现,但是无从下手,希望高手们能够指教,或给出FFT程序,或给出学习的方法均可,谢谢大家能帮忙了~~~…

查看全部问答>

spi,的cs,我如果没用的话,是否可以当做一个io口使用

比如我只是用了SPI的三条线,clk,miso,mosi,那么我cs引脚是否可以单独作为一个io口来使用…

查看全部问答>

LPC1114是否可以做片内数据掉电存储?

看datasheet上好像没有片内eeprom,那是否可以做数据存储呢?还是要用其他方法实现呢?…

查看全部问答>