arm-linux-gcc 裸机程序开发(一)

2020-07-04来源: eefocus关键字:arm-linux-gcc  裸机程序  mini2440

以前开发arm裸机程序都是在ADS1.2开发环境下编译和调试的。刚开始时初学嵌入式好多东西不懂,选择这个开发环境的理由,一是资料多的,mini2440开发板上提供了很多例程可以参考,网上几乎所有arm裸机程序都是基于ADS1.2开发的。二是开发环境友善,虽然后来感觉ADS1.2有点难用,但毕竟是IDE的环境,对初学者来说总比命令行的方式更加直观与方便。随着学习的深入,感觉它就像傻瓜相机一样,虽然好用但屏蔽了很多内容,影响了我们深入理解代码编译以及链接的细节。而且ADS对于程序的开发没有GNU工具链灵活。这段时间因为需要,又要编写一些arm裸机程序。自己已经用Linux习惯了,不想再切回windows下工作了。所以,最近对linux下arm的裸机程序开发进行了学习。要让程序在arm裸机下运行,主要解决如下问题:


(1) 编译器问题,这个不用说肯定是GNU的大名鼎鼎的GCC了,与此相关的什么连接器,汇编器也都包含在内了。针对arm的GCC,当然就是arm-linux-gcc了,我所用的版本就是友善之臂光盘自带arm-linux-gcc 4.4.3。也有资料说也可以用arm-elf-gcc,这个与arm-linux-gcc带的c库不同,是uclibc,更精简更适合嵌入式。但是,相对于arm-elf-gcc而言,我对arm-linux-gcc更熟悉一点,毕竟编译u-boot的时候用过,所以选择了arm-linux-gcc作为编译器。


(2) 启动代码问题:C程序运行是需要一定条件的,首先板子必须初始化好,然后堆栈必须初始化好,这样C代码才能够运行起来。mini2440的启动代码对于ADS1.2就是2440init.S。本来打算移植这个,因为ARM汇编与GNU汇编虽说在一般的汇编指令之间差别不大,但是在伪指令和语法结构上还是有很大的区别。所以移植起来会很困难,我果断放弃了这个想法。然后我想起了u-boot的start.S,这个是现成基于GNU汇编的启动代码。但是start.S毕竟是基于u-boot工程的,所以还是需要移植的。


(3) 直接下载到内存中运行:其实这也是启动代码的问题。在ADS开发环境下,我一般编译程序都先是用NorFlash里的supervivi将程序下载到内存中运行看结果,默认的下载地址是0x30000000。所以用arm-linux-gcc编译也是一样,要首先实现在内存中下载运行。其实在supervivi这个环境下,不需要启动代码也是可以运行C程序的,因为这种情况下supervivi已经在运行了,它已经做过了启动代码应该做的事。不过这里还是要加上启动代码,这是未雨绸缪,因为我们的程序以后得自食其力。


(4) NandFlash启动问题,S3C2440提供了两种启动方式,NorFlash启动以及NandFlash启动,第三个问题其实也是为了NorFlash启动,因为运行的环境是norflash启动后的。对于NandFlash启动,u-boot的启动代码与ADS的启动代码都提供了解决的办法。u-boot读写Nandflash的程序就是nand_read.c,而ADS用的是nand.c。其实这两个文件都做了同一件事,就是将NandFlash里的程序拷贝到内存中。具体选那种方式,还是要看移植时的修改量。看看nand_read.c前两个头文件#include #include 顿时失去了修改他的想法,函数比较复杂。那么只剩下nand.c了,随便看了一下,函数很简单基本上不用修改什么。所以就选这个文件作为读写NandFlash的程序了。


(5) 移植2440lib.c问题,这个文件包含一些操作板子资源的库函数。是mini2440自带的,因为是C语言编写的,所以可以轻松的移植到linux编译环境下。


(6) 中断的问题。中断对嵌入式编程非常重要。所以编写稳定高效的中断处理程序很重要,也很必要。在ADS中我们只需要在自己的中断服务程序中加入一行字:_irq,编译器就会给我们保存中断现场,多智能呀。可是arm-linux-gcc目前我还没有发现这个功能,所以中断现成的保护与恢复就得我们自己来完成。

     

一. 编译器问题

按照友善之臂说明书,安装好arm-linux-gcc4.4.3,我的编译器目录是/home/sun/study/crosstools/4.4.3/ ,我所用的开发环境为linux发行版 ubuntu10.10。


二. 启动代码问题

首先修改start.S以及消除编译错误,这个最容易解决,只要去掉u-boot有关的一些宏定义,以及注释掉与nandflash启动相关的部分就可以了。最难的是能在开发板上正确的运行。在此之前,我们得先弄清楚一个问题,就是嵌入式程序的加载域与运行域的关系与区别。就拿u-boot来说明,我们知道友善之臂mini2440的u-boot,在u-boot.lds里有这一行代码

. = 0x33f80000;在编译u-boot的输出信息中,我们会发现的最后的链接选项有一个-Ttext 0x33f80000。那么这个0x33f80000究竟是什么意思呢,他都会影响什么呢?弄清楚这个问题之前,我们先清楚一些概念。我们知道一个链接好的可运行的二进制代码,无非是一条一条的机器指令罗列而成。u-boot.bin也是这样,每条指令相对于代码的开始都是有一个偏移的地址,第一条指令偏移为0,第二条为1,以此类推直到程序的结束,最后一条指令偏移量就是程序的长度。


我们先抛开这个0x33f800000不说,先说一下CPU运行u-boot的过程。假如我们把u-boot下载到内存0x30000000处运行,那么u-boot第一条指令的地址就是0x30000000, CPU要去运行u-boot,那么首先必须执行类似这样的指令adr pc,#0x30000000,这样CPU就会取到0x30000000里的第一条u-boot指令。假如我们把u-boot下载到内存的0x0地址处运行,那么CPU是去0x0地址取指令。这个过程与你链接的时候指定什么地址无关,程序该到哪里运行就到哪里运行,一切地址都是相对与第一条指令在内存中的位置。这时程序就是运行在加载域,也就是刚刚下载进去的内存地址范围运行。


回到问题的初衷,链接时我们指定了一个地址0x33f80000,这个究竟是做什么的。这个就涉及到了程序的绝对地址与运行域。也就是说二进制代码的指令有两种地址,一个就是相对地址,相对于你加载到内存中的位置,一个就是绝对地址,无论你下载到内存中的哪里,这个地址都是不变的。比如u-boot的这个0x33f80000,那u-boot的第一条指令的绝对地址就是0x33f80000了,以后的指令的绝对地址类推。说完了这个地址是什么,接下来说说他的作用,很明显他是程序运行域的开始地址。具体运行域的概念,我的理解就是,程序正常运行时所处的地址空间。这里的正常运行是指的代码自拷贝之后的状态,在u-boot有一段自拷贝程序,如果程序的加载地址与链接是指定的地址不相符的话,程序会把自己拷贝到链接指定的地址处,u-boot就是0x33f80000。所以0x33f80000这个地址的作用就是,提供程序运行域的开始地址。那么这个地址影响的代码,就是调用ldr伪指令的代码。ldr伪指令就是取标号的绝对地址的。如ldr sp,=UndefStack,就是将UndefStack的绝对地址赋值给sp,这个地址一经链接无论程序下载到哪里都是不变的。如果写成adr sp,UndefStack 就是取UndefStack的相对地址,他会随程序下载的地址不同而不同。


现在总结一下,程序刚开始下载的内存或者norFlash的位置就是下载域,当程序把自己拷贝到链接是指定的位置后就会运行在运行域。程序的代码有两种地址:绝对地址与相对地址。如果你把程序恰好下载到了链接时指定的地址,那么加载域就是运行域,绝对地址就是相对地址。如果不是,那么绝对地址与相对地址是不同的。当程序运行在运行域的时候,绝对地址等于相对地址。


注意: ldr 这个指令有两种身份,一种是普通数据装载指令,用法ldr r0,[r1]或者#define pWTCON 0x53000000 ldr r0,=pWTCON,这条指令就是把0x53000000赋值给r0,相对的就是str。一种就是伪指令,加载标号处的绝对地址,用法ldr r0, = label1。 相对应的就是adr,加载相对地址


弄清楚了加载域与运行域,相对地址与绝对地址的关系。我们就要修改start.S使之正确的运行在内存中,下面就该解决第三个问题了。


三. 直接下载到内存中运行的问题

要使得裸机程序能在内存中运行,实现先解决u-boot直接下载到内存中的运行的问题。我以前移植的u-boot是不能下载到内存中运行的,问题原因以前也没有注意,现在要用到了,一起解决吧。但是,找了很久一直不知道是什么原因,后来在开发板光盘中的u-boot的移植手册中发现了端倪,就是因为绝对地址与相对地址的关系的问题。问题出在了lowlevel_init.S上,这是一段初始化内存的代码。关键代码如下: 


_TEXT_BASE:

.word TEXT_BASE

.globl lowlevel_init

lowlevel_init:

/* memory control configuration */

/* make r0 relative the current location so that it */

/* reads SMRDATA out of FLASH rather than memory ! */

ldr     r0, =SMRDATA    @取SMADATA的绝对地址

ldr r1, _TEXT_BASE  @TEXT_BASE = 0x33f80000 所以r1 = 0x33f80000

sub r0, r0, r1      

ldr r1, =BWSCON /* Bus Width Status Controller */

add     r2, r0, #13*4

0:

ldr     r3, [r0], #4

str     r3, [r1], #4

cmp     r2, r0

bne     0b

 

/* everything is fine now */

mov pc, lr

 

.ltorg

/* the literal pools origin */

 

SMRDATA:

.word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSC

[1] [2]
关键字:arm-linux-gcc  裸机程序  mini2440 编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/ic502124.html 本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:三 ARM9(S3C2440)的串口UART——程序实例讲解
下一篇:mini2440裸机试炼之——看门狗中断和复位操作

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

arm学习笔记011之arm-linux-gcc的命令参数介绍
我们需要编译出运行在ARM平台上的代码,所使用的交叉编译器为 arm-linux-gcc。下面将arm-linux-gcc编译工具的一些常用命令参数介绍给大家。在此之前首先介绍下编译器的工作过程,在使用GCC编译程序时,编译过程分为四个阶段:1. 预处理(Pre-Processing)2. 编译(Compiling)3. 汇编(Assembling)4. 链接(Linking)Linux程序员可以根据自己的需要让 GCC在编译的任何阶段结束,以便检查或使用编译器在该阶段的输出信息,或者对最后生成的二进制文件进行控制,以便通过加入不同数量和种类的调试代码来为 今后的调试做好准备。和其它常用的编译器一样,GCC也提供了灵活而强大的代码
发表于 2020-06-21
ARM学习笔记001之arm-linux-gcc 4.3.2下载与安装
下载arm-linux-gcc-4.3.2.tgz(84MB)安装交叉编译工具链:1、首先以root用户登入2、复制arm-linux-gcc-4.3.2.tgz到根目录下tmp文件夹里3、解压命令    [root@localhost ~]tar xvzf arm-linux-gcc-4.3.2.tgz -C /    注意tgz和-C之间有空格,-C是大写,-C和/之间有空格注意:在进行解压命令之前一定要先进入路径”/tmp“([root@localhost ~]cd /tmp),不然会出现如下情况4、配置编译环境的路径    ① 在小红帽的终端里输入
发表于 2020-06-20
<font color='red'>ARM</font>学习笔记001之<font color='red'>arm</font>-<font color='red'>linux</font>-<font color='red'>gcc</font> 4.3.2下载与安装
编译通过的U-boot和使用的arm-linux-gcc编译器
说实话编译U-boot挺累人的,要做的修改不是很多,但是在编译器上花的功夫却很多,经常遇到各种奇怪的问题。下面是编译通过的U-boot和对应的gcc编译器GCC下载地址:http://download.csdn.net/detail/king_mcu/9002001U-Boot下载地址:http://download.csdn.net/detail/king_mcu/9002011说明:1、arm-linux-gcc解压到linux电脑上后,添加环境变量,这点不用多说,下面是我电脑上的安装路径:2、解压U-boot到工作目录,修改Makefile中的编译器路径,改为你电脑上arm-linux-gcc的路径:3、make
发表于 2020-04-14
编译通过的U-boot和使用的<font color='red'>arm</font>-<font color='red'>linux</font>-<font color='red'>gcc</font>编译器
arm-linux-gcc和简单的makefile
gcc常用选项gcc 的使用方法: gcc [选项] 文件名-v:查看gcc编译器的版本,显示gcc执行时的详细过程-o :指定输出文件名为file,不用与编译文件同名-E: preprocess only; do not compile, assemble or link(只预处理,不会编译、汇编、链接)-S:Compile only; do not assemble or link(只编译需要有已经预处理完成的输出文件,不会汇编和链接)-c:Compile and assemble, but do not link(预处理 编译和汇编,不会链接)gcc编译文件gcc hello.c :直接默认生成一个a.out文件gcc -o
发表于 2020-04-14
第七篇:gccarm-linux-gcc常用选项
一、gcc和arm-linux-gcc的常用选项常用选型-v 查看gcc编译器的版本,显示gcc执行时的详细过程-o Place the output into 指定输出文件名为file,这个名称不能跟源文件名同名-E Preprocess only; do not compile, assemble or link只预处理,不会编译、汇编、链接-S Compile only; do not assemble or link只编译,不会汇编、链接-c Compile and assemble, but do not link编译和汇编,不会链接举例//举例一:gcc hell.c
发表于 2020-04-14
第七篇:<font color='red'>gcc</font>和<font color='red'>arm</font>-<font color='red'>linux</font>-<font color='red'>gcc</font>常用选项
S3C2440运行裸机程序需烧录到NAND Flash
对于韦东山的S3C2440开发板,当运行LED等简单的小程序时,必须烧录到NAND Flash,原因如下:(1)NOR Flash虽然可以向内存一样进行读操作,但不可以像内存一样进行写操作,所以假如要从NOR Flash启动,一般先在代码的开始部分使用汇编指令初始化外接的内存器件(外部RAM),然后将代码复制到外存中,最后跳转到外存中继续执行。(这段初始化代码比较复杂,需要后面再学习)。(2)S3C2440中有称为“Steppingstone”的4KB内存RAM,当选择从NAND Flash启动CPU时,CPU会通过内部的硬件将NAND Flash开始的4KB字节数据复制到这4KB的内部RAM中(此时内部RAM的起始地址
发表于 2020-06-30
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2020 EEWORLD.com.cn, Inc. All rights reserved