完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
2个回答
|
|
1.I.MX6U 启动方式详解
I.MX6U 支持多种启动方式以及启动设备,比如可以从 SD/EMMC、NAND Flash、QSPI Flash等启动。用户可以根据实际情况,选择合适的启动设备。 不同的启动方式其启动方式和启动要求也不一样,比如上一章中的从 SD 卡启动就需要在 bin 文件前面添加一个数据头,其它的启动设备也是需要这个 数据头的。本章我们就来学习一下 I.MX6U 的启动方式,以及不同设备启动的要求。 1)启动方式选择 BOOT 的处理过程是发生在 I.MX6U 芯片上电以后,芯片会根据 BOOT_MODE[1:0]的设置来选择 BOOT 方式。BOOT_MODE[1:0]的值是可以改变的, 有两种方式,一种是改写 eFUSE(熔丝),一种是修改相应的 GPIO 高低电平。第一种修改 eFUSE 的方式只能修改一次,后面就不能再修改了,所以 我们不使用。我们使用的是通过修改 BOOT_MODE[1:0]对应的 GPIO 高低电平来选择启动方式,所有的开发板都使用的这种方式,I.MX6U 有一个 BOOT_MODE1 引脚和BOOT_MODE0 引脚,这两个引脚对应这 BOOT_MODE[1:0]。I.MX6U-ALPHA 开发板的这两个引脚原理图如图1所示: 图1 BOOT_MODE 原理图 I.MX6U 有四个 BOOT 模式,这四个 BOOT 模式由 BOOT_MODE[1:0]来控制,也就是BOOT_MODE1 和 BOOT_MODE0 这两 IO,BOOT 模式配置如图2所示: 图2 BOOT 类型 1、串行下载 当 BOOT_MODE1 为 0,BOOT_MODE0 为 1 的时候此模式使能,串行下载的意思就是可以通过 USB 或者UART 将代码下载到板子上的外置存储设备中, 我们可以使用 OTG1这个USB口向开发板上的 SD/EMMC、NAND 等存储设备下载代码。我们需要将 BOOT_MODE1 拨到“OFF”,将 BOOT_MODE0 拨到“ON”。 这个下载是需要用到 NXP 提供的一个软件,一般用来最终量产的时候将代码烧写到外置存储设备中。 2、内部 BOOT 模式 当 BOOT_MODE1 为 1,BOOT_MODE0 为 0 的时候此模式使能,在此模式下,芯片会执行内部的 boot ROM 代码,这段 boot ROM 代码会进行 硬件初始化(一部分外设),然后从 boot 设备(就是存放代码的设备、比如 SD/EMMC、NAND)中将代码拷贝出来复制到指定的 RAM 中,一般是 DDR。 2)BOOT ROM 初始化内容 当我们设置 BOOT 模式为“内部 BOOT 模式”以后,I.MX6U 内部的 boot ROM 代码就会执行,这个 boot ROM 代码都会做什么处理呢?首先肯定是初始化时钟, boot ROM 设置的系统时钟如图3 所示: 图3 boot ROM 系统时钟设置 在图3中 BT_FREQ 模式为 0,可以看到,boot ROM 会将 I.MX6U 的内核时钟设置为396MHz,也就是主频为 396Mhz。System PLL=528Mhz,USB PLL=480MHz, AHB=132MHz,IPG=66MHz。 3)启动设备 当 BOOT_MODE 设置为内部 BOOT 模式以后,可以从以下设备中启动: 1.接到 EIM 接口的 CS0 上的 16 位 NOR Flash。 2.接到 EIM 接口的 CS0 上的 OneNAND Flash。 3.接到 GPMI 接口上的 MLC/SLC NAND Flash,NAND Flash 页大小支持 2KByte、4KByte和 8KByte,8 位宽。 4.Quad SPI Flash。 5.接到 USDHC 接口上的 SD/MMC/eSD/SDXC/eMMC 等设备。 6.SPI 接口的 EEPROM。 启动设备是通过BOOT_CFG1[7:0]、BOOT_CFG2[7:0]和 BOOT_CFG4[7:0]这 24 个配置 IO,这 24 个配置 IO 刚好对应着 LCD 的 24 根数据线 LCD_DATA0~LCDDATA23, 当启动完成以后这 24 个 IO 就可以作为 LCD 的数据线使用。这 24 根线和 BOOT_MODE1、BOOT_MODE0 共同组成了 I.MX6U的启动选择引脚,如图 4 所示: 图4 启动引脚 底板上启动设备选择拨码开关原理图如图5所示: 图5 BOOT 选择拨码开关 在 图5 中 , 除了BOOT_MODE1 和 BOOT_MODE0 必须引出来 ,LCD_DATA3~LCDDATA7、LCD_DATA11 这 6 个 IO 也被引出来了,可以通过拨码开关来设置 其对应的高低电平,拨码开关拨到“ON”就是 1,拨到“OFF”就是 0。其中 LCD_DATA11 就是 BOOT_CFG2[3],LCD_DATA3~LCD_DATA7 就是 BOOT_CFG1[3]~BOOT_CFG1[7], 这 6 个IO 的配置含义如图6所示: 图6 BOOT IO 含义 根据图6中的 BOOT IO 含义,I.MX6U-ALPHA 开发板从 SD 卡、EMMC、NAND 启动的时候拨码开关各个位设置方式如图7所示: 图7 I.MX6U-ALPHA 开发板启动设置 2.C语言版LED灯实验 1)C语言版LED灯简介 在开始部分用汇编来初始化一下C语言环境,比如初始化DDR、设置堆栈指针SP等等,当这些工作都做完以后就可以进入 C 语言环境。 以 STM32F103 为例,其启动文件 startup_stm32f10x_hd.s 这个汇编文件就是完成C语言环境搭建的,当然还有一些其他的处理,比如中断向量表等等。 当 startup_stm32f10x_hd.s 把 C 语言环境初始化完成以后就会进入 C 语言环境。 2)实验程序编写 1、汇编部分实验程序编写 在 STM32 中,启动文件 startup_stm32f10x_hd.s 就是完成 C 语言环境搭建的,当然还有一些其他的处理,比如中断向量表等等。startup_stm32f10x_hd.s 中 堆栈初始化代码如下图8所示: 图8 堆栈初始化和跳转到C语言 第 1 行代码就是设置栈大小,这里是设置为 0X400=1024 字节。 第 5 行的__initial_sp 就是初始化 SP 指针。 第 11 行是设置堆大小。 第 18 行是复位中断服务函数,STM32 复位完成以后会执行此中断服务函数。 第 22 行调用 SystemInit()函数来完成其他初始化工作。 第 24 行调用__main,__main 是库函数,其会调用 main()函数。 I.MX6U 的汇编部分代码和 STM32 的启动文件 startup_stm32f10x_hd.s 基本类似的,只是本实验我们不考虑中断向量表,只考虑初始化 C 环境即可。 在前面创建的 start.s 中输入如下代码: .global _start /* 全局标号 */ /* * 描述: _start函数,程序从此函数开始执行,此函数主要功能是设置C * 运行环境。 */ _start: /* 进入SVC模式 */ mrs r0, cpsr bic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */ orr r0, r0, #0x13 /* r0或上0x13,表示使用SVC模式 */ msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */ ldr sp, =0X80200000 /* 设置栈指针 */ b main /* 跳转到main函数 */ 以上通过 ldr 指令设置 SVC 模式下的 SP 指针=0X80200000,因为 I.MX6U-ALPHA 开发 板 上 的 DDR3 地 址 范 围 是 0X80000000~0XA0000000(512MB) 或 者 0X80000000~0X90000000(256MB),不管是 512MB 版本还是 256MB 版本的,其 DDR3 起始地址都是 0X80000000。由于Cortex-A7的堆栈是向下增长的, 所以将SP指针设置为0X80200000,因此 SVC 模式的栈大小 0X80200000-0X80000000=0X200000=2MB,2MB 的栈空间已经很大了,如果做裸机开发的话绰绰有余。 |
|
|
|
2、C 语言部分实验程序编写
C 语言部分有两个文件 main.c 和 main.h,main.h 里面主要是定义的寄存器地址,在 main.h里面输入代码: /* * CCM相关寄存器地址 */ #define CCM_CCGR0 *((volatile unsigned int *)0X020C4068) #define CCM_CCGR1 *((volatile unsigned int *)0X020C406C) #define CCM_CCGR2 *((volatile unsigned int *)0X020C4070) #define CCM_CCGR3 *((volatile unsigned int *)0X020C4074) #define CCM_CCGR4 *((volatile unsigned int *)0X020C4078) #define CCM_CCGR5 *((volatile unsigned int *)0X020C407C) #define CCM_CCGR6 *((volatile unsigned int *)0X020C4080) /* * IOMUX相关寄存器地址 */ #define SW_MUX_GPIO1_IO03 *((volatile unsigned int *)0X020E0068) #define SW_PAD_GPIO1_IO03 *((volatile unsigned int *)0X020E02F4) /* * GPIO1相关寄存器地址 */ #define GPIO1_DR *((volatile unsigned int *)0X0209C000) #define GPIO1_GDIR *((volatile unsigned int *)0X0209C004) #define GPIO1_PSR *((volatile unsigned int *)0X0209C008) #define GPIO1_ICR1 *((volatile unsigned int *)0X0209C00C) #define GPIO1_ICR2 *((volatile unsigned int *)0X0209C010) #define GPIO1_IMR *((volatile unsigned int *)0X0209C014) #define GPIO1_ISR *((volatile unsigned int *)0X0209C018) #define GPIO1_EDGE_SEL *((volatile unsigned int *)0X0209C01C) 接下看一下 main.c 文件,在 main.c 里面输入如下所示代码: #include "main.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; } /* * @description : 初始化LED对应的GPIO * @param : 无 * @return : 无 */ void led_init(void) { /* 1、初始化IO复用 */ SW_MUX_GPIO1_IO03 = 0x5; /* 复用为GPIO1_IO03 */ /* 2、、配置GPIO1_IO03的IO属性 *bit 16:0 HYS关闭 *bit [15:14]: 00 默认下拉 *bit [13]: 0 kepper功能 *bit [12]: 1 pull/keeper使能 *bit [11]: 0 关闭开路输出 *bit [7:6]: 10 速度100Mhz *bit [5:3]: 110 R0/6驱动能力 *bit [0]: 0 低转换率 */ SW_PAD_GPIO1_IO03 = 0X10B0; /* 3、初始化GPIO */ GPIO1_GDIR = 0X0000008; /* GPIO1_IO03设置为输出 */ /* 4、设置GPIO1_IO03输出低电平,打开LED0 */ GPIO1_DR = 0X0; } /* * @description : 打开LED灯 * @param : 无 * @return : 无 */ void led_on(void) { /* * 将GPIO1_DR的bit3清零 */ GPIO1_DR &= ~(1<<3); } /* * @description : 关闭LED灯 * @param : 无 * @return : 无 */ void led_off(void) { /* * 将GPIO1_DR的bit3置1 */ GPIO1_DR |= (1<<3); } /* * @description : 短时间延时函数 * @param - n : 要延时循环次数(空操作循环次数,模式延时) * @return : 无 */ void delay_short(volatile unsigned int n) { while(n--){} } /* * @description : 延时函数,在396Mhz的主频下 * 延时时间大约为1ms * @param - n : 要延时的ms数 * @return : 无 */ void delay(volatile unsigned int n) { while(n--) { delay_short(0x7ff); } } /* * @description : mian函数 * @param : 无 * @return : 无 */ int main(void) { clk_enable(); /* 使能所有的时钟 */ led_init(); /* 初始化led */ while(1) /* 死循环 */ { led_off(); /* 关闭LED */ delay(500); /* 延时大约500ms */ led_on(); /* 打开LED */ delay(500); /* 延时大约500ms */ } return 0; } 3)编译下载验证 1、编写 Makefile 新建 Makefile 文件,在 Makefile 文件里面输入如下内容: objs := start.o main.o ledc.bin:$(objs) arm-linux-gnueabihf-ld -Ttext 0X87800000 -o ledc.elf $^ arm-linux-gnueabihf-objcopy -O binary -S ledc.elf $@ arm-linux-gnueabihf-objdump -D -m arm ledc.elf > ledc.dis %.o:%.s arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $< %.o:%.S arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $< %.o:%.c arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $< clean: rm -rf *.o ledc.bin ledc.elf ledc.dis 第1行定义了一个变量objs,objs包含着要生成ledc.bin所需的材料:start.o和main.o,也就是当前工程下的start.s和main.c这两个文件编译后的.o文件。 这里要注意start.o一定要放到最前面!因为在后面链接的时候start.o要在最前面,因为start.o是最先要执行的文件! 第3行就是默认目标,目的是生成最终的可执行文件ledc.bin,ledc.bin依赖start.o和main.o,如果当前工程没有start.o和main.o的时候就会找到相应的 规则去生成start.o和main.o。比如start.o是start.s文件编译生成的,因此会执行第8行的规则。 第4行是使用arm-linux-gnueabihf-ld进行链接,链接起始地址是0X87800000,但是这一行用到了自动变量“$^”,“$^”的意思是所有依赖文件的集合,在 这里就是objs这个变量的值:start.o和main.o。链接的时候start.o要链接到最前面,因为第一行代码就是start.o里面的。 第5行使用arm-linux-gnueabihf-objcopy来将ledc.elf文件转为ledc.bin,本行也用到了自动变量“$@”,“$@”的意思是目标集合,在这里就是“ledc.bin”。 第8~15行就是针对不同的文件类型将其编译成对应的.o文件,其实就是汇编.s(.S)和.c文件,比如start.s就会使用第8行的规则来生成对应的start.o文件。 第9行就是具体的命令,这行也用到了自动变量“$@”和“$<”,其中“$<”的意思是依赖目标集合的第一个文件。如果依赖文件是以模式(即“%”)定义的,那么 “$<”就是符合模式的一系列的文件集合。 2、链接脚本 在上面的 Makefile 中我们链接代码的时候使用如下语句: arm-linux-gnueabihf-ld -Ttext 0X87800000 -o ledc.elf $^ 上面语句中我们是通过“-Ttext”来指定链接地址是0X87800000的,这样的话所有的文件都会链接到以0X87800000为起始地址的区域。但是有时候我们很多 文件需要链接到指定的区域,或者叫做段里面,比如在Linux里面初始化函数就会放到init段里面。因此我们需要能够自定义一些段,这些段的起始地址我们 可以自由指定,同样的我们也可以指定一个文件或者函数应该存放到哪个段里面去。要完成这个功能我们就需要使用到链接脚本,看名字就知道链接脚本主 要用于链接的,用于描述文件应该如何被链接在一起形成最终的可执行文件。其主要目的是描述输入文件中的段如何被映射到输出文件中,并且控制输出文 件中的内存排布。比如我们编译生成的文件一般都包含text段、data段等等。 链接脚本的语法很简单,就是编写一系列的命令,这些命令组成了链接脚本,每个命令是一个带有参数的关键字或者一个对符号的赋值,可以使用分号分隔 命令。像文件名之类的字符串可以直接键入,也可以使用通配符“*”。最简单的链接脚本可以只包含一个命令“SECTIONS”,我们可以在这一个“SECTIONS”里面 来描述输出文件的内存布局。我们一般编译出来的代码都包含在text、data、bss和rodata这四个段内,假设现在的代码要被链接到0X10000000这个地址,数 据要被链接到0X30000000这个地方,下面就是完成此功能的最简单的链接脚本: SECTIONS{ . = 0X10000000; .text : {*(.text)} . = 0X30000000; .data ALIGN(4) : { *(.data) } .bss ALIGN(4) : { *(.bss) } } 第1行我们先写了一个关键字“SECTIONS”,后面跟了一个大括号,这个大括号和第7行的大括号是一对,这是必须的。看起来就跟C语言里面的函数一样。 第2行对一个特殊符号“.”进行赋值,“.”在链接脚本里面叫做定位计数器,默认的定位计数器为0。我们要求代码链接到以0X10000000为起始地址的地方,因此这一行 给“.”赋值0X10000000,表示以0X10000000开始,后面的文件或者段都会以0X10000000为起始地址开始链接。 第3行的“.text”是段名,后面的冒号是语法要求,冒号后面的大括号里面可以填上要链接到“.text”这个段里面的所有文件,“*(.text)”中的“*”是通配符,表示所有输入文件的 .text段都放到“.text”中。 第4行,我们的要求是数据放到0X30000000开始的地方,所以我们需要重新设置定位计数器“.”,将其改为0X30000000。 第5行跟第3行一样,定义了一个名为“.data”的段,然后所有文件的“.data”段都放到这里面。但是这一行多了一个“ALIGN(4)”,这是什么意思呢?这是用来对“.data”这个段的 起始地址做字节对齐的,ALIGN(4)表示4字节对齐。也就是说段“.data”的起始地址要能被4整除,一般常见的都是ALIGN(4)或者ALIGN(8),也就是4字节或者8字节对齐。 第6行定义了一个“.bss”段,所有文件中的“.bss”数据都会被放到这个里面,“.bss”数据就是那些定义了但是没有被初始化的变量。 上面就是链接脚本最基本的语法格式,我们接下来就按照这个基本的语法格式来编写我们本试验的链接脚本,根据要求,在Makefile同目录下新建一个名为“imx6ul.lds”的文件, 然后在此文件里面输入如下所示代码: SECTIONS{ . = 0X87800000; .text : { start.o main.o *(.text) } .rodata ALIGN(4) : {*(.rodata*)} .data ALIGN(4) : { *(.data) } __bss_start = .; .bss ALIGN(4) : { *(.bss) *(COMMON) } __bss_end = .; } 第2行设置定位计数器为0X87800000,因为我们的链接地址就是0X87800000。第5行设置链接到开始位置的文件为start.o,因为start.o里面包含着第一个要执行的指令,所以一定要链接 到最开始的地方。第6行是main.o这个文件,其实可以不用写出来,因为main.o的位置就无所谓了,可以由编译器自行决定链接位置。 在第11、13行有“__bss_start”和“__bss_end”这两个东西?这个是什么呢?“__bss_start”和“__bss_end”是符号,第11、13这两行其实就是对这两个符号进行赋值,其值为定位符“.”,这两个 符号用来保存.bss段的起始地址和结束地址。前面说了.bss段是定义了但是没有被初始化的变量,我们需要手动对.bss段的变量清零的,因此我们需要知道.bss段的起始和结束地址,这样我们 直接对这段内存赋0即可完成清零。通过第11、13行代码,.bss段的起始地址和结束地址就保存在了“__bss_start”和“__bss_end”中,我们就可以直接在汇编或者C文件里面使用这两个符号。 3、修改 Makefile 在上一小节中我们已经编写好了链接脚本文件:imx6ul.lds,我们肯定是要使用这个链接脚本文件的,将Makefile中的如下一行代码: arm-linux-gnueabihf-ld -Ttext 0X87800000 -o ledc.elf $^ 改为: arm-linux-gnueabihf-ld -Timx6ul.lds -o ledc.elf $^ |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1777 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1621 浏览 1 评论
1080 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
728 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1678 浏览 2 评论
1938浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
731浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
570浏览 3评论
596浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
556浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-23 13:50 , Processed in 0.732167 second(s), Total 48, Slave 42 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号