完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
扫一扫,分享给好友
|
|
相关推荐
1个回答
|
|
1.汇编LED灯实验
1)I.MX6U GPIO详解 1、I.MX6U IO命名 参考手册中的形如“IOMUXC_SW_MUC_CTL_PAD_GPIO1_IO00”的就是 GPIO 命名,命 名形式就是“IOMUXC_SW_MUC_CTL_PAD_XX_XX”,后面的“XX_XX”就是 GPIO 命名, 比如:GPIO1_IO01、UART1_TX_DATA、JTAG_MOD、SNVS_TAMPER1 等等。 I.MX6U 的 GPIO并不像 STM32 一样以 PA0~15 这样命名,他是根据某个 IO 所拥有的功能来命名的。 比如我们一看到 GPIO1_IO01 就知道这个肯定能做 GPIO,看到 UART1_TX_DATA 肯定就知道这个 IO 肯定能做为 UART1 的发送引脚。 “Chapter 30: IOMUX Controller(IOMUXC)”这一章列出了I.MX6U 的 所 有 IO , 如果你找遍第30章的书 签 , 你会发现貌似GPIO只有GPIO1_IO00~GPIO1_IO09,难道 I.MX6U的GPIO 只有这10个?显然不是的, 我们知道STM32的很多 IO 是可以复用为其它功能的,那么 I.MX6U 的其它 IO 也是可以复用为 GPIO 功能。 同样的,GPIO1_IO00~GPIO_IO09也是可以复用为其它外设引脚的。 2、I.MX6U IO复用 以 IO“IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00”为例,打开参考手册的 1329 页,如图1所示: 图1 GPIO1_IO00 复用 从图 8.1.3.1 可以看到有个名为:IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00 的寄存器,寄存器地址为 0X020E005C, 这个寄存器是 32 位的,但是只用到了最低 5 位,其中bit0~bit3(MUX_MODE)就是设置 GPIO1_IO00 的复用功能的。 GPIO1_IO00 一共可以复用为9种功能 IO,分别对应 ALT0~ALT8,其中 ALT5 就是作为 GPIO1_IO00。GPIO1_IO00 还可以作 为 I2C2_SCL、GPT1_CAPTURE1、ANATOP_OTG1_ID 等。这个就是 I.MX6U 的 IO 复用。 再来看一个“IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATA”这个 IO,这个 IO 对应的复用如图2所示: 图2 UART1_TX_DATA IO 复用 同样的,从图 8.1.3.2 可以看出,UART1_TX_DATA 可以复用为 8 种不同功能的 IO,分为ALT0~ALT5 和 ALT8、ATL9, 其中 ALT5 表示 UART1_TX_DATA 可以复用为 GPIO1_IO16。 由此可见,I.MX6U 的 GPIO 不止 GPIO1_IO00~GPIO1_IO09 这 10 个,其它的 IO 都可以复用为 GPIO 来使用。 I.MX6U 的 GPIO 一共有 5 组:GPIO1、GPIO2、GPIO3、GPIO4 和 GPIO5,其中 GPIO1 有 32 个 IO,GPIO2 有 22 个 IO, GPIO3 有 29 个 IO、GPIO4 有 29 个 IO,GPIO5最少,只有 12 个 IO,这样一共有 124 个 GPIO。 3、I.MX6U IO 配置 细心的读者应该会发现在《I.MX6UL 参考手册》第 30 章“Chapter 30: IOMUXController(IOMUXC)”的书签中,每一个 IO 会出现两次, 它们的名字差别很小,不仔细看就看不出来,比如 GPIO1_IO00 有如下两个书签,如图3所示: 图3 2个不同的定义 上面两个都是跟 GPIO_IO00 有关的寄存器,名字上的区别就是红色部分,一个是“MUX”,一个是“PAD”。 IOMUX_SW_MUX_CTL_PAD_GPIO1_IO00 我们前面已经说了,是用来配置GPIO1_IO00 复用功能的, 那么 IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO00 是做什么的呢?找到这个书签对应的 1582 页,如图4 所示: 图4 IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO00 寄存器 从图4中可以看出,IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO00 也是个寄存器,寄存器地址为 0X020E02E8。 这也是个 32 位寄存器,但是只用到了其中的低 17 位,在看这写位的具体含义之前,先来看一下图5所示的 GPIO 功能图: 图5 GPIO 功能图 我们对照着图4来详细看一下寄存器 IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO00 的各个位的含义: HYS(bit16):对应图4中 HYS,用来使能迟滞比较器,当 IO 作为输入功能的时候有效,用于设置输入接收器的施密特触发器是否使能。 如果需要对输入波形进行整形的话可以使能此位。此位为0的时候禁止迟滞比较器,为1的时候使能迟滞比较器。 PUS(bit15:14) :对应图4中的PUS,用来设置上下拉电阻的,一共有四种选项可以选择,如图6所示: 图6 上下拉设置 PUE(bit13) :图4没有给出来,当 IO 作为输入的时候,这个位用来设置 IO 使用上下拉还是状态保持器。 当为0的时候使用状态保持器,当为1的时候使用上下拉。状态保持器在IO 作为输入的时候才有用,顾名思义, 就是当外部电路断电以后此 IO 口可以保持住以前的状态。 PKE(bit12) :对应图5中的 PKE,此为用来使能或者禁止上下拉/状态保持器功能,为0时禁止上下拉/状态保持器,为 1 时使能上下拉和状态保持器。 ODE(bit11):对应图5中的ODE,当 IO 作为输出的时候,此位用来禁止或者使能开路输出,此位为0的时候禁止开路输出,当此位为1的时候就使能开路输出功能。 SPEED(bit7:6) :对应图5中的SPEED,当 IO 用作输出的时候,此位用来设置 IO 速度,设置如图7所示: 图7 速度配置 DSE(bit5:3):对应图5中的 DSE,当 IO 用作输出的时候用来设置 IO 的驱动能力,总共有 8 个可选选项,如图8所示: 图8 驱动能力设置 SRE(bit0) :对应图5中的 SRE,设置压摆率,当此位为0的时候是低压摆率,当为1的时候是高压摆率。 这里的压摆率就是 IO 电平跳变所需要的时间,比如从 0 到 1 需要多少时间,时间越小波形就越陡, 说明压摆率越高;反之,时间越多波形就越缓,压摆率就越低。如果你的产品要过 EMC 的话那就可以使用小的压摆率, 因为波形缓和,如果你当前所使用的 IO做高速通信的话就可以使用高压摆率。 4、I.MX6U GPIO 配置 将其复用为 GPIO 以后还需要对其 GPIO 的功能进行配置,关于 I.MX6U 的 GPIO 请参考《IMX6UL 参考手册》 的第26章“Chapter 26 General Purpose Input/Ouput(GPIO)”,GPIO 结构如图9所示: 图9 GPIO 结构图 在图9的 左 下 角 的 IOMUXC 框 图 里 面 就 有 SW_MUX_CTL_PAD_* 和SW_PAD_CTL_PAD_*两种寄存器。 这两种寄存器前面说了用来设置 IO 的复用功能和 IO 属性配置。左上角部分的 GPIO 框图就是,当 IO 用作 GPIO 的时候需要设置的寄存器, 一共有八个:DR、GDIR、PSR、ICR1、ICR2、EDGE_SEL、IMR 和 ISR。 前面我们说了 I.MX6U 一共有GPIO1~GPIO5 共五组 GPIO,每组 GPIO 都有这 8 个寄存器。我们来看一下这 8 个寄存器都是什么含义。 1.DR寄存器 首先来看一下 DR 寄存器,此寄存器是数据寄存器,结构图如图10所示: 图10 DR 寄存器结构图 此寄存器是 32 位的,一个 GPIO组最大只有 32 个 IO,因此 DR 寄存器中的每个位都对应一个GPIO。 当 GPIO 被配置为输出功能以后,向指定的位写入数据那么相应的 IO 就会输出相应的高低电平, 比如要设置 GPIO1_IO00 输出高电平,那么就应该设置 GPIO1.DR=1。当 GPIO被配置为输入模式以后, 此寄存器就保存着对应IO的电平值,每个位对对应一个 GPIO,例如,当 GPIO1_IO00 这个引脚接地的话,那么 GPIO1.DR 的 bit0 就是 0。 2.GDIR 寄存器 看完 DR 寄存器,接着看 GDIR 寄存器,这是方向寄存器,用来设置某个 GPIO 的工作方向的,即输入/输出,GDIR 寄存器结构如图11所示: 图11 GDIR 寄存器 GDIR 寄存器也是 32 位的,此寄存器用来设置某个 IO 的工作方向,是输入还是输出。同样的,每个 IO 对应一个位, 如果要设置 GPIO 为输入的话就设置相应的位为 0,如果要设置为输出的话就设置为 1。比如要设置 GPIO1_IO00 为输入, 那么 GPIO1.GDIR=0; 3.PSR 寄存器 接下来看 PSR 寄存器,这是 GPIO 状态寄存器,如图12所示: 图12 PSR 状态寄存器 同样的 PSR 寄存器也是一个 GPIO 对应一个位,读取相应的位即可获取对应的 GPIO 的状态,也就是 GPIO 的高低电平值。 功能和输入状态下的 DR 寄存器一样。 4.ICR1和ICR2 寄存器 接下来看ICR1和ICR2这两个寄存器,都是中断控制寄存器,ICR1用于配置低16个GPIO,ICR2 用于配置高 16 个 GPIO,ICR1 寄存器如图13所示: 图13 ICR1 寄存器 ICR1 用于 IO0~15 的配置, ICR2 用于 IO16~31 的配置。ICR1 寄存器中一个 GPIO 用两个位,这两个位用来配置中断的触发方式, 和 STM32 的中断很类似,可配置的选项如图14所示: 图14 中断触发配置 以GPIO1_IO15为例,如果要设置GPIO1_IO15为上升沿触发中断,那么GPIO1.ICR1=2<<30,如果要设置 GPIO1 的 IO16~31 的话 就需要设置 ICR2 寄存器了。 5. IMR 寄存器 接下来看 IMR 寄存器,这是中断屏蔽寄存器,如图15所示: 图15 IMR 寄存器 IMR 寄存器也是一个 GPIO 对应一个位,IMR 寄存器用来控制 GPIO 的中断禁止和使能,如果使能某个 GPIO 的中断, 那么设置相应的位为 1 即可,反之,如果要禁止中断,那么就设置相应的位为 0 即可。例如,要使能 GPIO1_IO00 的中断, 那么就可以设置 GPIO1.MIR=1 即可。 6. ISR 寄存器 接下来看寄存器 ISR,ISR 是中断状态寄存器,寄存器如图16所示: 图16 ISR 寄存器 ISR 寄存器也是 32 位寄存器,一个 GPIO 对应一个位,只要某个 GPIO 的中断发生,那么ISR 中相应的位就会被置 1。 所以,我们可以通过读取 ISR 寄存器来判断 GPIO 中断是否发生,相当于 ISR 中的这些位就是中断标志位。 当我们处理完中断以后,必须清除中断标志位,清除方法就是向 ISR 中相应的位写 1,也就是写1清零。 7. EDGE_SEL 寄存器 最后来看一下 EDGE_SEL 寄存器,这是边沿选择寄存器,寄存器如图17所示: 图17 EDGE_SEL 寄存器 EDGE_SEL 寄存器用来设置边沿中断,这个寄存器会覆盖 ICR1 和 ICR2 的设置,同样是一个 GPIO 对应一个位。 如果相应的位被置 1,那么就相当于设置了对应的 GPIO 是上升沿和下降沿(双边沿)触发。例如,我们设置 GPIO1.EDGE_SEL=1, 那么就表示 GPIO1_IO01 是双边沿触发中断,无论 GFPIO1_CR1 的设置为多少,都是双边沿触发。 4、I.MX6U GPIO 时钟使能 STM32 的每个外设都有一个外设时钟,GPIO 也不例外,要使用某个外设,必须要先使能对应的时钟。I.MX6U 其实也 一样的,每个外设的时钟都可以独立的使能或禁止,这样可以关闭掉不使用的外设时钟,起到省电的目的。 如果要使用某个外设的话必须要先使能其时钟。I.MX6U 的系统时钟参考《I.MX6UL 参考手册》的第 18 章“Chapter 18: Clock Controller Module(CCM)”, 这一章主要讲解 I.MX6U 的时钟系统,很复杂。我们先不研究 I.MX6U 的时钟系统,我们只看一下 CCM 里面的外设时钟使能寄存器。 CMM 有 CCM_CCGR0~CCM_CCGR6 这 7 个寄存器,这 7 个寄存器控制着 I.MX6U 的所有外设时钟开关,我们以 CCM_CCGR0 为例来看一下如何 禁止或使能一个外设的时钟,CCM_CCGR0 结构体如图18所示: 图18 CCM_CCGR0 寄存器 CCM_CCGR0 是个 32 位寄存器,其中每 2 位控制一个外设的时钟,比如 bit31:30 控制着GPIO2 的外设时钟,两个位就有4种操作方式, 如图19所示: 图19 外设时钟控制 根据图18中的位设置,如果我们要打开 GPIO2 的外设时钟,那么只需要设置CCM_CCGR0 的 bit31 和 bit30 都为 1 即可, 也就是 CCM_CCGR0=3 << 30。反之,如果要关闭GPIO2 的 外 设 时 钟 , 那 就 设置CCM_CCGR0 的 bit31 和 bit30 都为0即 可 。 CCM_CCGR0~CCM_CCGR6 这 7 个寄存器操作都是类似的,只是不同的寄存器对应不同的外设时钟而已, 总结一下,要将 I.MX6U 的 IO 作为 GPIO 使用,我们需要一下几步: 1.使能 GPIO 对应的时钟。 2.设置寄存器 IOMUXC_SW_MUX_CTL_PAD_XX_XX,设置 IO 的复用功能,使其复用为 GPIO 功能。 3.设置寄存器 IOMUXC_SW_PAD_CTL_PAD_XX_XX,设置 IO 的上下拉、速度等等。 4.第2步已经将 IO 复用为了 GPIO 功能,所以需要配置 GPIO,设置输入/输出、是否使用中断、默认输出电平等。 2)硬件原理分析 I.MX6U-ALPHA 开发板上有一个 LED 灯,原理图如下图20所示: 图20 LED 原理图 从图20可以看出,LED0 接到了 GPIO_3 上,GPIO_3 就是 GPIO1_IO03,当 GPIO1_IO03输出低电平(0)的时候, 发光二极管 LED0 就会导通点亮,当 GPIO1_IO03 输出高电平(1)的时候发光二极管 LED0 不会导通,因此 LED0 也就不会点亮。 所以 LED0 的亮灭取决于 GPIO1_IO03的输出电平,输出 0 就亮,输出 1 就灭。 3)实验程序编写 1 、使能 GPIO1 时钟 GPIO1 的时钟由 CCM_CCGR1 的 bit27 和 bit26 这两个位控制,将这两个位都设置位 11 即可。 本教程所有例程已经将 I.MX6U 的所有外设时钟都已经打开了,因此这一步可以不用做。 2、设置 GPIO1_IO03 的复用功能 找到 GPIO1_IO03 的复用寄存器“IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03”的地址为0X020E0068, 然后设置此寄存器,将 GPIO1_IO03 这个 IO 复用为 GPIO 功能,也就是 ALT5。 3、配置 GPIO1_IO03 找到 GPIO1_IO03 的配置寄存器“IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03”的地址为0X020E02F4, 根据实际使用情况,配置此寄存器。 4 、设置 GPIO 我们已经将 GPIO1_IO03 复用为了 GPIO 功能,所以我们需要配置 GPIO。找到 GPIO3 对应的 GPIO 组寄存器地址, 在《IMX6ULL 参考手册》的 1357 页,如图21所示: 图21 GPIO1 对应的 GPIO 寄存器地址 本实验中 GPIO1_IO03 是作为输出功能的,因此 GPIO1_GDIR 的 bit3 要设置为 1,表示输出。 5、控制 GPIO 的输出电平 经过前面几步,GPIO1_IO03 已经配置好了,只需要向 GPIO1_DR 寄存器的 bit3 写入 0 即可控制 GPIO1_IO03 输出低电平,打开 LED,向 bit3 写入 1 可控制 GPIO1_IO03 输出高电平,关闭 LED。 led.s 文件的汇编代码如下所示: /**************************************************************Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.文件名 : mian.c作者 : 左忠凯版本 : V1.0描述 : 裸机实验1 汇编点灯 使用汇编来点亮开发板上的LED灯,学习和掌握如何用汇编语言来 完成对I.MX6U处理器的GPIO初始化和控制。其他 : 无论坛 : www.openedv.com在线教育 : www.yuanzige.com日志 : 初版V1.0 2019/1/3 左忠凯创建**************************************************************/.global _start /* 全局标号 *//* * 描述: _start函数,程序从此函数开始执行此函数完成时钟使能、 * GPIO初始化、最终控制GPIO输出低电平来点亮LED灯。 */_start: /* 例程代码 */ /* 1、使能所有时钟 */ ldr r0, =0X020C4068 /* CCGR0 */ ldr r1, =0XFFFFFFFF str r1, [r0] ldr r0, =0X020C406C /* CCGR1 */ str r1, [r0] ldr r0, =0X020C4070 /* CCGR2 */ str r1, [r0] ldr r0, =0X020C4074 /* CCGR3 */ str r1, [r0] ldr r0, =0X020C4078 /* CCGR4 */ str r1, [r0] ldr r0, =0X020C407C /* CCGR5 */ str r1, [r0] ldr r0, =0X020C4080 /* CCGR6 */ str r1, [r0] /* 2、设置GPIO1_IO03复用为GPIO1_IO03 */ ldr r0, =0X020E0068 /* 将寄存器SW_MUX_GPIO1_IO03_BASE加载到r0中 */ ldr r1, =0X5 /* 设置寄存器SW_MUX_GPIO1_IO03_BASE的MUX_MODE为5 */ str r1,[r0] /* 3、配置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 低转换率 */ ldr r0, =0X020E02F4 /*寄存器SW_PAD_GPIO1_IO03_BASE */ ldr r1, =0X10B0 str r1,[r0] /* 4、设置GPIO1_IO03为输出 */ ldr r0, =0X0209C004 /*寄存器GPIO1_GDIR */ ldr r1, =0X0000008 str r1,[r0] /* 5、打开LED0 * 设置GPIO1_IO03输出低电平 */ ldr r0, =0X0209C000 /*寄存器GPIO1_DR */ ldr r1, =0 str r1,[r0]/* * 描述: loop死循环 */loop: b loop 4)编译下载验证 1、编译代码 1.arm-linux-gnueabihf-gcc 编译文件 我们是要编译出在 ARM 开发板上运行的可执行文件,所以要使用之前安装的交叉编译器arm-linux-gnueabihf-gcc 来编译。因此本试验就一个 led.s 源文件,所以编译比较简单。先将 led.s 编译为对应的.o 文件,在终端中输入如下命令: arm-linux-gnueabihf-gcc -g -c led.s -o led.o 上述命令就是将 led.s 编译为 led.o,其中“-g”选项是产生调试信息,GDB 能够使用这些调试信息进行代码调试。 “-c”选项是编译源文件,但是不链接。“-o”选项是指定编译产生的文件名字,这里我们指定 led.s 编译完成以后的文件名字为 led.o。 执行上述命令以后就会编译生成一个 led.o 文件,如图22所示: 图22 编译生成 led.o 文件 图22中 led.o 文件并不是我们可以下载到开发板中运行的文件,一个工程中所有的 C文件和汇编文件都会编译生成一个对应的.o 文件, 我们需要将这.o 文件链接起来组合成可执行文件。 2.arm-linux-gnueabihf-ld 链接文件 arm-linux-gnueabihf-ld 用来将众多的.o 文件链接到一个指定的链接位置。 我们在学习SMT32 的时候基本就没有听过“链接”这个词,我们一般用 MDK 编写好代码,然后点击“编译”,MDK 或者 IAR 就会自动帮我们编译好整个工程, 最后再点击“下载”就可以将代码下载到开发板中。这是因为链接这个操作 MDK 或者 IAR 已经帮你做好了,后面我就以 MDK 为例给大家讲解。 大家可以打开一个 STM32 的工程,然后编译一下,肯定能找到很多.o 文件,如图23所示: 图23 STM32 编译生成的.o 文件 图23中的这些.o 文件肯定会被 MDK 链接到某个地址去,如果使用 MDK 开发 STM32的话肯定对图24所示界面很熟悉: 图24 STM32 配置界面 图24中左侧的 IROM1 我们都知道是设置 STM32 芯片的 ROM 起始地址和大小的,右边的 IRAM1 是设置 STM32 芯片的 RAM 起始地址和大小的。其中 0X08000000 就是 STM32 内部 ROM 的起始地址,编译出来的指令肯定是要从 0X08000000 这个地址 开始存放的。对于STM32 来说 0X08000000 就是它的链接地址,图23中的这些.o 文件就是这个链接地址开始依次存放, 最终生成一个可以下载的 hex 或者 bin 文件,我们可以打开.map 文件查看一下这些文件的链接地址,在 MDK 下打开一个 工程的.map 文件方法如图25所示: 图25 .map 文件打开方法 图25中的.map 文件就详细的描述了各个.o 文件都是链接到了什么地址,如图26所示: 图26 STM32 镜像映射文件 从图26中就可以看出 STM32 的各个.o 文件所处的位置,起始位置是 0X08000000。由此可以得知, 我们用 MDK 开发 STM32 的时候也是有链接的,只是这些工作 MDK 都帮我们全部做好了,我们不用关心而已。 但是我们在 Linux 下用交叉编译器开发 ARM 的是时候就需要自己处理这些了。 因此我们现在需要做的就是确定一下本试验最终的可执行文件其运行起始地址,也就是链接地址。这里我们要区分“存储地址” 和“运行地址”这两个概念,“存储地址”就是可执行文件存储在哪里,可执行文件的存储地址可以随意选择。“运行地址”就是代码 运行的时候所处的地址,这个我们在链接的时候就已经确定好了,代码要运行,那就必须处于运行地址处,否则代码肯定运行出错。 比如 I.MX6U 支持 SD 卡、EMMC、NAND 启动,因此代码可以存储到 SD 卡、EMMC 或者 NAND 中,但是要运行的话就必须将 代码从 SD 卡、EMMC 或者NAND 中拷贝到其运行地址(链接地址)处,“存储地址”和“运行地址”可以一样,比如STM32 的存储起始 地址和运行起始地址都是 0X08000000。 本教程所有的裸机例程都是烧写到 SD 卡中,上电以后 I.MX6U 的内部 boot rom 程序会将可执行文件拷贝到链接地址处,这个链接地址 可以在 I.MX6U 的内部 128KB RAM 中(0X900000~0X91FFFF),也可以在外部的 DDR 中。本教程所有裸机例程的链接地址都在 DDR 中,链接起始地址为 0X87800000。I.MX6U-ALPHA 开发板的 DDR 容量有两种:512MB 和256MB,起始地址都为 0X80000000, 只不过 512MB 的终止地址为 0X9FFFFFFF,而 256MB 容量的终止地址为 0X8FFFFFFF。之所以选择 0X87800000 这个地址是 因为后面要讲的 Uboot 其链接地址就是 0X87800000,这样我们统一使用 0X87800000 这个链接地址,不容易记混。 确定了链接地址以后就可以使用 arm-linux-gnueabihf-ld 来将前面编译出来的 led.o 文件链接到 0X87800000 这个地址,使用如下命令: arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf 上述命令中-Ttext 就是指定链接地址,“-o”选项指定链接生成的 elf 文件名,这里我们命名为 led.elf。上述命令执行完以后就会在工程目录 下多一个 led.elf 文件,如图27所示: 图27 链接生成 led.elf 文件 led.elf 文件也不是我们最终烧写到 SD 卡中的可执行文件,我们要烧写的.bin 文件,因此还需要将 led.elf 文件转换为.bin 文件,这里我们就需要 用到 arm-linux-gnueabihf-objcopy 这个工具了。 3.arm-linux-gnueabihf-objcopy 格式转换 arm-linux-gnueabihf-objcopy 更像一个格式转换工具,我们需要用它将 led.elf 文件转换为led.bin 文件,命令如下: arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin 上述命令中,“-O”选项指定以什么格式输出,后面的“binary”表示以二进制格式输出,选项“-S”表示不要复制源文件中 的重定位信息和符号信息,“-g”表示不复制源文件中的调试信息。上述命令执行完成以后,工程目录如图28所示: 图28 生成最终的 led.bin 文件 4.arm-linux-gnueabihf-objdump 反汇编 大多数情况下我们都是用 C 语言写试验例程的,有时候需要查看其汇编代码来调试代码,因此就需要进行反汇编,一般可以将 elf 文件反汇编,比如如下命令: arm-linux-gnueabihf-objdump -D led.elf > led.dis 上述代码中的“-D”选项表示反汇编所有的段,反汇编完成以后就会在当前目录下出现一个名为 led.dis 文件,如图29所示: 图29 反汇编生成 led.dis 可以打开 led.dis 文件看一下,看看是不是汇编代码,如图30所示: 图30 反汇编文件 从图30可以看出 led.dis 里面是汇编代码,而且还可以看到内存分配情况。在0X87800000 处就是全局标号_start, 也就是程序开始的地方。通过 led.dis 这个反汇编文件可以明显的看出到我们的代码已经链接到了以 0X87800000 为 起始地址的区域。 总结一下我们为了编译 ARM 开发板上运行的 led.o 这个文件使用了如下命令: arm-linux-gnueabihf-gcc -g -c led.s -o led.oarm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elfarm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.binarm-linux-gnueabihf-objdump -D led.elf > led.dis 如果我们修改了 led.s 文件,那么就需要再重复一次上面的这些命令,太麻烦了,这个时候我们就可以使用之前讲解的 Makefile 文件了。 2、创建 Makefile 文件 使用“touch”命令在工程根目录下创建一个名为“Makefile”的文件,如图31所示: 图31 创建 Makefile 文件 创建好 Makefile 文件以后就需要根据 Makefile 语法编写 Makefile 文件了,Makefile 基本语法我们已经在之前讲解了,在 Makefile 中输入如下内容: led.bin:led.s arm-linux-gnueabihf-gcc -g -c led.s -o led.o arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin arm-linux-gnueabihf-objdump -D led.elf > led.disclean: rm -rf *.o led.bin led.elf led.dis 创建好 Makefile 以后我们就只需要执行一次“make”命令即可完成编译,过程如图32所示: 图32 Makefile 执行过程 如果我们要清理工程的话执行“make clean”即可,如图33所示: 图33 make clean 清理工程 3、代码烧写 我们学习 STM32 等其他的单片机的时候,编译完代码以后可以直接通过 MDK 或者 IAR下载到内部的 flash 中。 但是 I.MX6U 虽然内部有 96K 的 ROM,但是这 96K 的 ROM 是 NXP自己用的,不向用户开放。 所以相当于说 I.MX6U 是没有内部 flash 的,但是我们的代码得有地方存放啊,为此,I.MX6U 支持从外置的 NOR Flash、NAND Flash、SD/EMMC、SPI NOR Flash和 QSPI Flash 这些存储介质中启动,所以我们可以将代码烧写到 这些存储介质中中。在这些存储介质中,除了 SD 卡以外,其他的一般都是焊接到了板子上的,我们没法直接烧写。但是 SD 卡是活动的,是可以从板子上插拔的,我们可以将 SD 卡插到电脑上,在电脑上使用软件将.bin文件烧写到 SD 卡中,然后再插到 板子上就可以了。其他的几种存储介质是我们量产的时候用到的,量产的时候代码就不可能放到 SD 卡里面了,毕竟 SD 是活动的, 不牢固,而其他的都是焊接到板子上的,很牢固。 因此,我们在调试裸机和 Uboot 的时候是将代码下载到 SD 中,因为方便嘛,当调试完成以后量产的时候要将裸机或者 Uboot 烧写到 SPI NOR Flash、EMMC、NAND 等这些存储介质中的。那么,如何将我们前面编译出来的 led.bin 烧写到 SD 卡中呢?肯定有人会认为 直接复制led.bin 到 SD 卡中不就行了,错!编译出来的可执行文件是怎么存放到 SD 中的,存放的位置是什么?这个 NXP 是有详细规定的! 我们必须按照 NXP 的规定来将代码烧写到 SD 卡中,否则代码是绝对运行不起来的。《IMX6UL 参考手册》的第 8 章“Chapter 8 System Boot” 就是专门讲解 I.MX6U 启动的,我们下一章会详细的讲解 I.MX6U 启动方式的。 正点原子专门编写了一个软件来将编译出来的.bin 文件烧写到 SD 卡中,这个软件叫做“imxdownload”,使用步骤如下: 1.将 imxdownload 拷贝到工程根目录下 我们要将 imxdownload 拷贝到工程根目录下,也就是和 led.bin 处于同一个文件夹下,要不然烧写会失败的,拷贝完成以后如图34所示: 图34 拷贝 imxdownload 软件 2.给予 imxdownload 可执行权限 我们直接将软件 imxdownload 从 Windows 下复制到 Ubuntu 中以后,imxdownload 默认是没有可执行权限的。我们需要给予 imxdownload 可执行权限, 使用命令“chmod”,命令如下图35所示: 图35 给予 imxdownload 可执行权限 通过对比图34和图35可以看到,当给予 imxdownload 可执行权限以后其名字变成了绿色的,如果没有可执行权限的话其名字颜色是白色的。所以在 Ubuntu 中 我们可以初步的从文件名字的颜色判断其是否具有可执行权限。 3.确定要烧写的SD卡 准备一张新的 SD(TF)卡,确保 SD 卡里面没有数据,因为我们在烧写代码的时候可能会格式化 SD 卡!!! Ubuntu 下所有的设备文件都在目录“/dev”里面,所以插上 SD 卡以后也会出现在“/dev”里面,其中存储设备都是以“/dev/sd”开头的。 我们要先看一下不插 SD 卡的时候电脑都有哪些存储设备,以防插入 SD 卡以后分不清谁是谁。输入如下所示命令: ls /dev/sd* 当前电脑的存储文件如图36所示: 图36 Ubuntu 当前存储文件 插入SD卡后输入命令“ls /dev/sd*”来查看当前 Ubuutu 下的存储设备,如图37所示: 图37 当前系统存储设备 /dev/sdd 和/dev/sdd1 是我的 SD 卡,为什么呢?因为只有/dev/sdd 有个对应的/dev/sdd1,/dev/sdd 是我的 SD 卡,/dev/sdd1 是 SD 卡 的第一个分区。如果你的 SD 卡有多个分区的话可能会出现/dev/sdd2、/dev/sdd3 等等。确定好SD 卡以后我们就可以使用软件 imxdownload 向 SD 卡烧写 led.bin 文件了。 4.向SD卡烧写bin文件 使用 imxdownload 向 SD 卡烧写 led.bin 文件,命令格式如下: ./imxdownload <.bin file> ./imxdownload led.bin /dev/sdd 烧写的过程中可能会让你输入密码,输入你的 Ubuntu 密码即可完成烧写,烧写过程如图38所示: 图38 imxdownload 烧写过程 在图38中,烧写的最后一行会显示烧写大小、用时和速度,比如 led.bin 烧写到 SD 卡中的大小是 3.2KB,用时 0.0160821s,烧写速度是 201KB/s。 注意这个烧写速度,如果这个烧写速度在几百 KB/s 以下那么就是正常烧写。 烧写完成以后会在当前工程目录下生成一个 load.imx 的文件,如图39所示: 图39 生成的 load.imx 文件 load.imx 这个文件就是软件 imxdownload 根据 NXP 官方启动方式介绍的内容,在 led.bin 文件前面添加了一些数据头以后生成的。最终烧写到 SD 卡里面 的就是这个 load.imx 文件,而非led.bin。 5.代码验证 代码已经烧写到了 SD 卡中了,接下来就是将 SD 卡插到开发板的 SD 卡槽中,然后设置拨码开关为 SD 卡启动,拨码开关设置如图40所示: 图40 拨码开关 SD 卡启动设置 设置好以后按一下开发板的复位键,如果代码运行正常的话 LED0 就会被点亮,如图41所示: |
|
|
|
只有小组成员才能发言,加入小组>>
调试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评论
595浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
554浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-23 07:48 , Processed in 0.785575 second(s), Total 76, Slave 60 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号