第2期ARM裸机篇:【6】 汇编LED驱动实验2_代码编译下载验证

简介

本章开始编写本教程第一个裸机例程——经典的点灯试验,这也是我们嵌入式 Linux 学习的第一步。万里长征第一步,祝愿大家学习愉快!

目标

了解如何使用汇编语言来初始化 I.MX6U 外设寄存器、了解 I.MX6UL 最基本的 IO 输出功能。

阅读基础

熟悉计算机。

环境说明

  • windows10

参考资料

  • 原子文档:I.MX6ULL开发指南 第八章
  • NXP官方文档:I.MX6ULL参考手册、数据手册

编译代码

如果你是在 Windows 下使用 Source Insight 编写的代码,就需要通过 FileZilla 将编写好的代码发送的 Ubuntu 中去编译,FileZilla 的使用参考 4.1 小节。因为我们现在是直接在 Ubuntu 下使用 VSCode 编译的代码,所以不需要使用 FileZilla 将代码发送到 Ubuntu 下,可以直接进行编译,在编译之前我们先了解几个编译工具。

参考文章:第2期ARM裸机篇:【1】开发环境搭建1_Ubuntu和Windows文件互传FileZilla_心飞的博客-CSDN博客

参考文章:第2期ARM裸机篇:【1】开发环境搭建1_Ubuntu和Windows文件互传FileZilla_心飞的博客-个人网站

arm-linux-gnueabihf-gcc 编译文件

我们是要编译出在 ARM 开发板上运行的可执行文件,所以要使用我们安装的交叉编译器arm-linux-gnueabihf-gcc来编译。

参考文章:第2期ARM裸机篇:【1】开发环境搭建3_Ubuntu交叉编译工具链安装_心飞的博客-CSDN博客

参考文章:第2期ARM裸机篇:【1】开发环境搭建3_Ubuntu交叉编译工具链安装_心飞的博客-个人网站

因为本试验就一个 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 文件,如图所示:

BlogImage-20210909200901

图中 led.o 文件并不是我们可以下载到开发板中运行的文件,一个工程中所有的 C文件和汇编文件都会编译生成一个对应的.o 文件,我们需要将这.o 文件链接起来组合成可执行文件

arm-linux-gnueabihf-ld 链接文件

arm-linux-gnueabihf-ld 用来将众多的.o 文件链接到一个指定的链接位置。

STM32的链接地址

我们在学习SMT32 的时候基本就没有听过“链接”这个词,我们一般用 MDK 编写好代码,然后点击“编译”,MDK 或者 IAR 就会自动帮我们编译好整个工程,最后再点击“下载”就可以将代码下载到开发板中。这是因为链接这个操作 MDK 或者 IAR 已经帮你做好了,后面我就以 MDK 为例给大家讲解。大家可以打开一个 STM32 的工程,然后编译一下,肯定能找到很多.o 文件,如图所示:

BlogImage-20210909201227

图中的这些.o 文件肯定会被 MDK 链接到某个地址去,如果使用 MDK 开发 STM32的话肯定对图所示界面很熟悉:

BlogImage-20210909201350

  • 左侧的 IROM1 我们都知道是设置 STM32 芯片的 ROM 起始地址和大小的,
  • 右边的 IRAM1是设置 STM32 芯片的 RAM 起始地址和大小的。

0X08000000就是STM32内部 ROM 的起始地址,编译出来的指令肯定是要从0X08000000这个地址开始存放的

对于STM32 来说 0X08000000就是它的链接地址,图中的这些.o 文件就是这个链接地址开始依次存放,最终生成一个可以下载的 hex 或者 bin 文件,我们可以打开.map 文件查看一下这些文件的链接地址,在 MDK 下打开一个工程的.map 文件方法如图所示:

BlogImage-20210909201748

图中的.map 文件就详细的描述了各个.o 文件都是链接到了什么地址,如图所示:

BlogImage-20210909201843

从图中就可以看出 STM32 的各个.o 文件所处的位置,起始位置是 0X08000000。由此可以得知,我们用 MDK 开发 STM32 的时候也是有链接的,只是这些工作 MDK 都帮我们全部做好了,我们不用关心而已。但是我们在 Linux 下用交叉编译器开发 ARM 的是时候就需要自己处理这些了

代码存储地址和运行地址

因此我们现在需要做的就是确定一下本试验最终的可执行文件其运行起始地址,也就是链接地址

这里我们要区分“存储地址”和“运行地址”这两个概念,

  • “存储地址”就是可执行文件存储在哪里,可执行文件的存储地址可以随意选择
  • “运行地址”就是代码运行的时候所处的地址,这个我们在链接的时候就已经确定好了

代码要运行,那就必须处于运行地址处,否则代码肯定运行出错。比如 I.MX6U 支持 SD 卡、EMMC、NAND 启动,因此代码可以存储到 SD 卡、EMMC 或者 NAND 中,但是要运行的话就必须将代码从 SD 卡、EMMC 或者NAND 中拷贝到其运行地址(链接地址)处,“存储地址”和“运行地址”可以一样,比如STM32 的存储起始地址和运行起始地址都是 0X08000000

I.MX6U的存储地址和运行地址

本教程所有的裸机例程都是烧写到 SD 卡中,上电以后 I.MX6U 的内部 boot rom 程序会将可执行文件拷贝到链接地址处,这个链接地址可以在 I.MX6U 的内部 128KB RAM 中(0X900000~0X91FFFF),也可以在外部的 DDR 中。

本教程所有裸机例程的链接地址都在 DDR中,链接起始地址为 0X87800000。之所以选择 0X87800000这个地址是因为后面要讲的 Uboot 其链接地址就是 0X87800000,这样我们统一使用 0X87800000这个链接地址,不容易记混。

I.MX6U-ALPHA 开发板的 DDR 容量有两种:512MB 和256MB,起始地址都为 0X80000000,只不过 512MB 的终止地址为 0X9FFFFFFF,而 256MB 容量的终止地址为 0X8FFFFFFF

链接led.o

确定了链接地址以后就可以使用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 文件,如图所示:

BlogImage-20210909203259

led.elf 文件也不是我们最终烧写到 SD 卡中的可执行文件,我们要烧写的.bin 文件,因此还需要将 led.elf 文件转换为.bin 文件,这里我们就需要用到 arm-linux-gnueabihf-objcopy 这个工具了。

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”表示不复制源文件中的调试信息。

上述命令执行完成以后,工程目录如图所示:

BlogImage-20210909203651

至此我们终于等到了想要的东西—led.bin 文件。

arm-linux-gnueabihf-objdump 反汇编

大多数情况下我们都是用 C 语言写试验例程的,有时候需要查看其汇编代码来调试代码,因此就需要进行反汇编,一般可以将 elf 文件反汇编,比如如下命令:

arm-linux-gnueabihf-objdump -D led.elf > led.dis

上述代码中的

  • “-D”选项表示反汇编所有的段

反汇编完成以后就会在当前目录下出现一个名为 led.dis 文件,如图所示:

BlogImage-20210909203939

可以打开 led.dis 文件看一下,看看是不是汇编代码,如图所示:

BlogImage-20210909204106

从图可以看出 led.dis 里面是汇编代码,而且还可以看到内存分配情况。在0X87800000处就是全局标号_start,也就是程序开始的地方。通过 led.dis 这个反汇编文件可以明显的看出我们的代码已经链接到了以 0X87800000为起始地址的区域。

创建 Makefile 文件

总结一下我们为了编译 ARM 开发板上运行的 led.o 这个文件使用了如下命令:

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.dis

如果我们修改了 led.s 文件,那么就需要在重复一次上面的这些命令,太麻烦了,这个时候我们就可以使用 Makefile 文件了。

是用“touch”命令在工程根目录下创建一个名为“Makefile”的文件,如图所示:

BlogImage-20210909204438

创建好 Makefile 文件以后就需要根据 Makefile 语法编写 Makefile 文件了,Makefile 基本语法我们已经在前面了。

参考文章:第1期Ubuntu入门篇:【16】make工具和Makefile的引入_心飞的博客-CSDN博客

参考文章:第1期Ubuntu入门篇:【17】Makefile基本语法_心飞的博客-CSDN博客

参考文章:第1期Ubuntu入门篇:【16】make工具和Makefile的引入_心飞的个人网站

参考文章:第1期Ubuntu入门篇:【17】Makefile基本语法_心飞的个人网站

在 Makefile 中输入如下内容:

ed.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.dis
clean:
        rm -rf *.o led.bin led.elf led.dis

编写完毕如下图:

BlogImage-20210909211711

执行make,如下图:

BlogImage-20210909211906

如果我们要清理工程的话执行“make clean”即可,如图所示:

BlogImage-20210909212009

至此,有关代码编译、arm-linux-gnueabihf 交叉编译器的使用就到这里了,我们接下来讲解如何将 led.bin 烧写到 SD 卡中。

代码烧写

我们学习 STM32 等其他的单片机的时候,编译完代码以后可以直接通过 MDK 或者 IAR下载到内部的 flash 中。

I.MX6U从外部存储介质中启动

但是 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 等这些存储介质中的。

将bin文件烧写到SD卡

那么,如何将我们前面编译出来的 led.bin 烧写到 SD 卡中呢?肯定有人会认为直接复制led.bin到 SD 卡中不就行了,错!而是将bin文件烧写到SD卡绝对地址上。

编译出来的可执行文件是怎么存放到 SD 中的,存放的位置是什么?这个 NXP 是有详细规定的!我们必须按照 NXP 的规定来将代码烧写到 SD 卡中,否则代码是绝对运行不起来的。比如先在bin文件前面添加头部。《IMX6UL 参考手册》的第 8 章“Chapter 8 System Boot”就是专门讲解 I.MX6U 启动的,我们之后会详细的讲解 I.MX6U 启动方式的。

对于I.MX而言,不能直接烧写bin文件。正点原子专门编写了一个软件来将编译出来的.bin 文件烧写到 SD 卡中,这个软件叫做“imxdownload”,imxdownlaod只能在 Ubuntu 下使用,使用步骤如下:

将 imxdownload 拷贝到工程根目录下

使用软件FileZila将imxdownload拷贝的工程根目录下。

参考文章:第2期ARM裸机篇:【1】开发环境搭建1_Ubuntu和Windows文件互传FileZilla_心飞的博客-CSDN博客

参考文章:第2期ARM裸机篇:【1】开发环境搭建1_Ubuntu和Windows文件互传FileZilla_心飞的博客-个人网站

如下图:

BlogImage-20210910091750

imxdownload拷贝成功。如下图:

BlogImage-20210910113612

给予 imxdownload 可执行权限

我们直接将软件 imxdownload从 Windows 下复制到 Ubuntu 中以后,imxdownload默认是
没有可执行权限的。如下图:

BlogImage-20210910113821

我们需要给予 imxdownload可执行权限,使用命令“chmod”,命令如下:

chmod 777 imxdownload

BlogImage-20210910113924

当给予 imxdownload 可执行权限以后其名字变成了绿色的,如果没有可执行权限的话其名字颜色是白色的。所以在 Ubuntu 中我们可以初步的从文件名字的颜色判断其是否具有可执行权限。

确定要烧写的 SD 卡

准备一张新的 SD(TF)卡和读卡器,确保 SD 卡里面没有数据,因为我们在烧写代码的时候可能会格式化 SD 卡!!!

Ubuntu 下所有的设备文件都在目录“/dev”里面,所以插上 SD 卡以后也会出现在“/dev”里面,其中存储设备都是以“/dev/sd”开头的。

我们要先看一下不插 SD 卡的时候电脑都有哪些存储设备,以防插入 SD 卡以后分不清谁是谁。输入如下所示命令:

ls /dev/sd*

BlogImage-20210910114335

从图中可以看到当前电脑有/dev/sda、/dev/sda1、/dev/sda2 和/dev/sda5 这 5 个存储设备。

格式化SD卡

使用读卡器将 SD 卡插到电脑,一定要确保 SD 卡是挂载到了 Ubuntu 系统中,而不是Windows下

插入带SD卡的读卡器到电脑的USB。如下图:

BlogImage-20210910115109

点击开始(如果有多个,就都格式化)。如下图:

BlogImage-20210910115507

SD卡挂载到虚拟机的Ubuntu

SD 卡挂载到电脑以后,VMware 右下角会出现如图所示图标:

BlogImage-20210910141001

这个图标就表示当前有存储设备插入,我们将鼠标放上去就会有提示当前设备名字,比如我这里提示“Genesys Logic USB3.0 Card Reader”,这是我的读卡器的名字。

BlogImage-20210910141151

如果 是灰色的话就表示 SD 卡挂载到了Windows 下,而不是 Ubuntu 上,所以我现在这个电脑的 SD 卡就是挂载到了 Windiows 下,我肯定要将其挂载到 Ubuntu 中,因为我要在 Ubuntu 下烧写代码。方法很简单,点击图标 ,点击以后如图所示:

BlogImage-20210910141347

提示你有个 USB 要从主机(Windows)拔出,插入虚拟机中,点击“确定”按钮即可。如下图:

BlogImage-20210910141423

SD 卡插入到 Ubuntu 以后,图标 就会变为绿色 不是灰色的了。如下图:

BlogImage-20210910141536

在输入命令“ls/dev/sd*”来查看当前 Ubuutu 下的存储设备,如图所示:

BlogImage-20210910141740

从图 中可以看到,我的电脑多出了/dev/sdb、/dev/sdc、/dev/sc1、/dev/sdc2、/dev/sdc3
这 5 个存储设备。我的读卡器是单一读卡器会出现一个该 SD 卡标号和 n 个分区标号。哪个才是 SD 卡呢?/dev/sdc、/dev/sc1、/dev/sdc2、/dev/sdc3是SD卡。

如果你用的是多合一读卡器可能会多出/dev/sdb、/dev/sdc、/dev/sdd、/dev/sdd1、/dev/sda和/dev/sdf 这 6 个存储设备。那这 6 个存储设备哪个才是 SD 卡呢?/dev/sdd 和/dev/sdd1 是 SD 卡,为什么呢?因为只有/dev/sdd 有个对应的/dev/sdd1,/dev/sdd1 是 SD 卡的第一个区。

SD 卡以后我们就可以使用软件 imxdownload 向 SD 卡烧写 led.bin 文件了。如果你的电脑没有找到 SD 卡的话,尝试重启一下 Ubuntu 操作

向 SD 卡烧写 bin 文件

使用 imxdownload 向 SD 卡烧写 led.bin 文件,命令格式如下:

./imxdownload <.bin file> <SD Card>

  • .bin 就是要烧写的.bin 文件
  • SD Card 就是你要烧写的 SD 卡,

比如我的电脑使用如下命令烧写 led.bin 到/dev/sdd 中:

./imxdownload led.bin /dev/sdd //不能烧写到/dev/sda 或 sda1 设备里面!那是系统磁盘

烧写的过程中可能会让你输入密码,输入你的 Ubuntu 密码即可完成烧写,烧写过程如图:

BlogImage-20210910142757

在图中,烧写的最后一行会显示烧写大小、用时和速度,比如 led.bin 烧写到 SD 卡中的大小是 3.2KB,用时 0.0460714s,烧写速度是 201KB/s。

注意这个烧写速度,如果这个烧写速度在几百 KB/s 以下那么就是正常烧写

如果这个烧写速度大于几十 MB/s、甚至几百 MB/s 那么肯定是烧写失败了

解决方法就是重新插拔 SD 卡,一般出现这种情况,重新插拔 SD 卡基本没啥用,只有重Ubuntu,至于原因,我也不清楚

烧写完成以后会在当前工程目录下生成一个 load.imx 的文件,如图所示:

BlogImage-20210910143131

load.imx 这个文件就是软件 imxdownload根据 NXP 官方启动方式介绍的内容,在 led.bin 文件前面添加了一些数据头以后生成的

最终烧写到 SD 卡里面的就是这个 load.imx 文件,而非led.bin。至于具体添加了些什么内容,我们会在之后讲解。

代码验证

代码已经烧写到了 SD 卡中了,接下来就是将 SD 卡插到开发板的 SD 卡槽中,然后设置拨码开关为 SD 卡启动,拨码开关设置如图所示:

BlogImage-20210910155122

设置好以后按一下开发板的复位键,等待几秒,如果代码运行正常的话 LED0 就会被点亮,如图 所示:

BlogImage-20210910155218

如图所示,LED0 被正常点亮,可能 LED0 之前会有一点微亮,那是因为 I.MX6U的 IO 默认电平可能让 LED0 导通了,但是 IO 的默认配置内部可能有很大的电阻,所以电流就很小,导致 LED0 微亮

但是我们自己编写代码、配置好 IO 以后就不会有这个问题,LED0 就很亮了。

我们详细的讲解了如何编译代码,并且如何将代码烧写进 SD 卡中进行测试后续我们的所有裸机实验和 Uboot 实验都使用的这种方法进行代码的烧写和测试

其他

相关资源下载

Ubuntu下裸机烧写软件imxdownload.rar-嵌入式文档类资源-CSDN下载

小有收获

有收获记得三连哦

最近更新

查看本文最近更新请点击

欢迎关注微信公众号

weixingognzhonghaoerweima

Guess you like

Origin blog.csdn.net/aa1319594154/article/details/120225241