一.LED灯的点亮——2编译程序

我们在上一章已经把代码放出来了,下面就要把代码编译成开发板能运行的程序。因为开发板用的是arm的架构,用树莓派可以直接编译。完整版的树莓派自带arm的gcc编译器,就不用额外配置了,并且我们是通过vscode第ssh插件直接在树莓派里写的代码,也不需要复制等操作,直接编译就行了。

程序编译

程序编译分下面几个步骤

  1. 将.c/.s(C代码或汇编代码)文件编译程.o文件(只编译不链接形成.o文件。里面包含了对各个函数的入口标记)
  2. 将所有的.o文件链接为elf格式的可执行文件(包含调试信息的可执行文件)
  3. 将elf文件转为bin文件
  4. 将elf文件转为汇编、反汇编(C语言编写时,非必要流程)

上面几个步骤所用到的工具是不同的

1.编译.o文件

这里所用到工具是arm-linux-gnueabihf-gcc,通过下面的命令可以讲led.s文件编译为led.o

arm-linux-gnueabihf-gcc -c -g led.s -o led.o

-g  产生调试信息,GDB可以使用这些调试信息进行代码调试、

 -c 编译源文件,但是不链接

-o  指定编译有产生文件的文件名,这里我们指定的文件名为led.o如下面的图所示

里面的Makefile和imxdownload先不用管。主要就是我们写的led.s文件和编译出来的led.o文件。

但是这个.o文件是不能被我们的开发板使用的,一个工程中所有的C文件和汇编文件都会被编译成一个相应的.o文件。下面就需要将所有的.o文件链接起来组成一个可执行文件。

2.链接文件

这里要用到的工具是arm-linux-gnueabihf-ld,这个工具用来将众多的.o文件链接到一个指定的链接位置。这里的链接是指将所有的.o文件链接在一起,并且指向一个代码运行的起始内存地址。要注意点是这里和代码的保存地址有可能是不同的,存储地址是指定执行文件存储在哪里,这个地址可以随意选择。而运行地址是代码运行时候所处的地址,这个地址在我们链接的时候就已经指定好了,代码要运行,那就必须处于运行地址处。比如I.MX6ULL支持SD卡、EMMC、NAND启动,那么代码可以被存储在SD卡、EMMC或者NAND里,但是要运行的话必须将代码从存储的地址拷贝到其运行地址处(链接地址)。对于STM32来说,如果我们使用MDK开发,这个地址已经在IDE里设置好了,在设置界面上可以看出来

这个地址就是0x08000000。对于6ULL来说,链接起始地址应该指向RAM地址,RAM分为内部RAM和外部RAM,也就是DDR。内部RAM地址范围是0x900000。我们在这次的学习中是把程序烧写到SD卡中的,上电后处理器内部boot rom会把执行文件拷贝到链接地址处,这个地址就可以是处理器内部128KB的RAM中(0x900000~0x91FFFF),但是要注意代码的大小;当然也可以选为外部的DDR中。我们在学习的时候都把链接地址放在DDR中,起始地址为0x80000000,而依照教程上说的,核心板板的DDR容量分别为512M何256M,二者起始地址都是0x80000000,512MB终止地址为0x9FFFFFFF,,256MB的终止地址为0x8FFFFFFF。这里我们统一使用0x87800000,是因为后面我们学习UBoot的时候就是这个地址,我们统一使用,不易记混。

确定了链接地址后我们用下面的命令来创建链接

arm-linux-gnueabihf-ld -Ttext 0x87800000 led.o -o led.elf

其中的选项-Ttext就是指定链接地址。

3.转换bin文件

elf文件生成后,需要将其转换为bin文件,命令如下:

arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin

上面命令中的选项注意一下:

-O表示指定以什么格式输出,后面跟的binary表示以二进制格式输出

-S表示不要复制原文中的重定位信息和符号信息

-g表示不复制源文件中的调试信息

4.反汇编(非必要)

由于我们在这个试验中只写了一个汇编文件,而后期目标是通过大量C代码实现的,有些时候就需要反汇编来看下调试的信息

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

生成的dis文件就是反汇编文件。

二.烧写bin文件

前面说过了,IMX6ULL分别支持SD卡,EMMC,NAND,nor,SPIFlash等启动模式,而裸机开发的时候用SD卡启动是比较方便的。直接把程序烧到SD卡内即可。对于I.MX来说,bin文件也不能直接运行,需要添加一个头部,这个头部信息包含了DDR的初始化参数。I.MX系列Soc内部boot rom会从EMMC,SD卡等外置存储中读取头部信息,然后初始化DDR,并且将bin文件拷贝到指定的地方(链接地址),所以Bin的运行地址一定要和链接起始地址一致。

所以,我们编译生成的bin文件是不能直接拷贝到SD卡中,需要通过开发板的官方发布的烧录工具imxdownload。把这个文件放到项目中,先查一下设备,插上卡后再查下设备

这里由于以前的SD卡分了两个区,, sd卡对应的实际路径应该是/dev/sda。下来使用烧录工具烧录一下,注意要修改权限

sudo chmod 777 imxdownload

 运行一下,会发现报错

没有错误信息,但是同个文件在PC端是能用到。我估计是因为这个文件是在x86架构下的编译的,但是在arm架构上用不了。还好教程里提供了源代码,复制到树莓派下重新编译一下

gcc imxdownload.c -o imxdownload

重新编译的imxdownload就可以直接使用了,重新复制到项目下,注意查下权限,重新烧录一遍

./imxdownload led.bin /dev/sda

 看下写入速度,图里的速度就是正常的,如果是几十甚至上百兆就是有问题的。注意打印出来的信息,有个load.imx文件,这个文件是由imxdownload向led.bin添加的一个头部信息以后新生成的文件。这个load.imx文件就是最终烧入到SD卡的文件。

将卡插入卡槽,修改启动跳线

 上电,可以发现灯已经被点亮

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

有了makefile,在每次修改源文件的时候就可以直接通过make命令直接编译文件了。一定要注意arm前面要用tab键,不要用空格!

额外的小知识

我们这里是把程序放在外部SD里,那么能不能通过JTAG将bin文件写在内部RAM里呢?理论上是可以的,但是存在另一个问题:6ULL的JTAG口和SAI(串行音频接口)接口复用,在板子上SAI即可和WM8960音频DAC链接,如果使用JLINK的话需要把这个DAC拆掉。并且对于普通嵌入式Linux开发的时候是基本用不上JLINK的,调试代码的时候通过点亮灯、串口打印直接调试程序就可以了。就是每次要插拔SD卡,烧录程序,比较麻烦。

原文地址:https://www.cnblogs.com/yinsedeyinse/p/15730539.html