您的位置 首页 软件

ARM处理器学习之GPIO操作篇(gnu link script)

1:主要内容本文主要介绍了VMA、LMA的相关概念,gnulinkscript的作用和使用方法。2:引言我们程序员刚开始学习编写程序时,都会接触到…

1:首要内容

本文首要介绍了VMA、LMA的相关概念,gnu link script的效果和运用方法。

2:导言

咱们程序员刚开端学习编写程序时,都会接触到一个 ” *.C ” 文件要通过编译、链接等进程才干变成能够履行的程序。至于这儿的链接究竟怎样回事,咱们今日就来谈谈这方面的内容。现在,咱们有这样一套ARM7的硬件开发环境,0X80000000地址开端BANK0 咱们用的是NorFlash,0X40000000地址是芯片内部的RAM。我编译、链接的程序下载到0x80000000地址处。而真实运转时,一部分初始化代码在0X80000000运转,初始化结束后,将首要作业的代码copy到内部RAM 0X40000000开端的当地运转。因为内部RAM运转程序比较快,所以我想NorFlash充任电脑的硬盘的效果,让其首要程序在RAM里运转。这是今日首要的内容,当然,程序的功用仍是和上一节ARM处理器学习之–GPIO操作篇相同:让板子上的一个LED灯闪耀。

3:首要思路

我把程序分红两个首要部分,榜首部分担任对芯片根本的操作和copy代码(从Norflashcopy到Sdram中),然后跳转到Sdram中去履行程序。第二部分为程序的首要部分。那这样的话,我对这两部分程序通过链接器链接时则需求设置不同的运转地址。即,榜首部分代码链接的运转地址是0X80000000,而第二部分代码链接的运转地址是0X40000000。但这两部分组成的映像文件的下载地址都是0X80000000.我想这个应该能通过链接器进行设置,通过查询材料得知,这个能够通过链接器的链接脚原本完成,在gnu arm 的链接器上是通过书写一个*。lds的链接脚原本完成的。

4:相关常识点

通过编译,链接后生成的可履行文件,其实有必定的结构。首要分为code段,data段,zi段(在gnu linux 下为.text .data .bss段)。这个code段,便是咱们运用汇编,c言语,c++写的程序指令,而data是程序中运用的变量,zi是程序中界说的未初始化的变量(因为这些内容原本就没有被初始化,所以这些zi段没有必要存储在生成的映像文件中,只是在程序真实运转时在相应的地址处预留出相应的空间即可)。文件的链接简图:

VMA(Virtual Memory Address)和LMA:(Load Memory Address)。这个LMA地址是程序装载到存储器时的地址,WMA能够了解成程序真实运转时地点的地址。

而链接器指定的链接地址要和程序真实运转时地点的地址共同。这个也好了解,链接器便是依据你指定的链接地址进行整个映像的链接操作,一些肯定跳转指令便是依据链接指定的地址进行更改PC值的,这些在上一讲有所解说。当然一般情况下,LMA和VMA的地址是相同的,不过,在有些嵌入式开发的进程中,程序的装载地址和运转地址不相同,那在拜访链接地址和装载地址不相同的code、data、zi段的时分应该在真实拜访前将其copy到链接指定的地址上去。

gnu 链接脚本的格局。gnu 链接脚本是一个描绘文本,用来描绘怎样链接终究的映像文件。关于这个链接脚本文件,咱们在详细事例中了解其用法。

5:试验源码

initsystem.s

@****************************************************************************** @ 文件名 :initsystem.s @ 功 能:初始化体系并copy代码 @ @ 作者 :张连聘 @ 创立时刻:2014-06-22 @****************************************************************************** .text .global _start @声明常量 .equ DATA_DST,0x40000000 @意图地址 .equ DATA_SRC,0x80000000 @源地址 @引进外部标号 .extern MainLoop .extern start_copy_addr _start: LDR PC, ResetAddr ResetAddr: .word ResetInit ResetInit: LDR R0,=DATA_DST @RO 指向意图地址 LDR R1,=start_copy_addr @R1 指向源地址 MOV R10,#128 @仿制的个数为128*8*4=4K CopyLoop: LDMIA R1!,{R2-R9} @从R1指定的内存地址处装载数据到R2–R9中 STMIA R0!,{R2-R9} @把R2–R9的数据仿制到R0指定的内存中 SUBS R10,R10,#1 BNE CopyLoop LDR PC,=MainLoop .end

control_led.s

@****************************************************************************** @ 文件名 :control_led.s @ 功 能:运用P2.28操控led灯闪耀 @ @ 作者 :张连聘 @ 创立时刻:2014-06-08 @****************************************************************************** .text .global MainLoop StartMain: @界说程序中运用到的常量 .equ IO2DIR ,0xE0028028 @ 操控IO0的输入、输出特点寄存器 .equ IO2SET ,0xE0028024 @IO2输出1操控寄存器 .equ IO2CLR ,0xE002802C @IO2输出0操控寄存器 .equ LEDCON ,(1<<28) @0x10000000 MainLoop: LDR R0,=IO2DIR @IO2DIR LDR R1,=LEDCON STR R1,[R0] @设置P2.28为输出 LDR R0,=IO2CLR LDR R1,=LEDCON STR R1,[R0] @P2.28为输出0,平息led BL DELAYS @调用延时程序 LDR R0,=IO2SET LDR R1,=LEDCON STR R1,[R0] @P2.28为输出1,点亮led BL DELAYS @调用延时程序 B MainLoop @****************************************************************************** @ 名 CopyData @ 功 能:仿制代码,从0x8000***---->0x40000000 size:4K @ 进口参数:无 @ 出口参数:无 @ 占用资源: @****************************************************************************** /* CopyData: LDR R0,=DATA_DST @RO 指向意图地址 MOV R10,#128 @仿制的个数为128*8*4=4K CopyLoop: LDMIA R1!,{R2-R9} @从R1指定的内存地址处装载数据到R2–R9中 STMIA R0!,{R2-R9} @把R2–R9的数据仿制到R0指定的内存中 SUBS R10,R10,#1 BNE CopyLoop MOV PC,LR */ @****************************************************************************** @ 名 称:DELAYS @ 功 能:软件延时 @ 进口参数:无 @ 出口参数:无 @ 占用资源:R7 @****************************************************************************** DELAYS: LDR R7,=0x00080000 @ 延时参数 DELAYS_L1: SUBS R7,R7,#1 @ R7 = R7-1 BNE DELAYS_L1 @ 判别R7-1成果是否为0,若不为0则跳转 MOV PC,LR @ 回来 .end

led_control.lds

/* * led_control 的链接脚本。 * * */ MEMORY { rom (rx) : ORIGIN = 0x80000000, LENGTH = 2M ram (!rx) : ORIGIN = 0x40000000, LENGTH = 2M } ENTRY(_start) SECTIONS { . = 0x80000000 ; .init : { initsystem.o(.text) start_copy_addr = . ; } >rom . = 0x40000000 ; .main : AT (ADDR(.init)+SIZEOF(.init)) { control_led.o(.text) } >ram }

Makefile

control_led.bin:control_led.s initsystem.s arm-linux-gcc -g -c -o control_led.o control_led.s arm-linux-gcc -g -c -o initsystem.o initsystem.s arm-linux-ld -Tled_control.lds -nostdlib -g control_led.o initsystem.o -o control_led_elf arm-linux-objcopy -O binary -S control_led_elf control_led.bin clean: rm -f control_led.bin control_led_elf *.o

6:源码要点解说

关于上面两个.s的汇编文件,这儿就不再赘述,请读者自行剖析。首要说说这个链接脚本的相关常识。gnu 链接脚本的详细材料拜见,gnu_Linker.pdf这个官方材料。

链接文件的细节问题这儿也不再提及,只说一下要害点。

1问:为什么我将初始化,copy的代码放在一个独自的文件里?

1答:我最开端把一切代码放在一个文件里,运用.section 伪指令界说新的段名,在链接脚本里运用不同的地址寄存不同的段。但程序一向不能正常运转,后反编译得知,我这样做,链接出来的映像文件和我在链接脚本里指定的不相同。后查材料得知,gnu link 对每个源文件都有默许的三个段名:.text .data .bss 。链接脚本里的输入段只允许这些段名。因而我将发动代码独自放在一个文件里,且一切的代码均在 .text 这个段里。

2问:我在链接脚本里能界说标号吗?界说的标号,怎样在汇编里引证呐?

2答:能够在链接脚本里界说标号,这儿界说的标号的含义等同于在编程言语里的地址。在上面给出的比如中,咱们copy代码并不是从榜首条指令开端copy的,而是从履行完初始化和copy代码这些功用后开端指令。那咱们怎样知道initsystem里边的指令究竟占用多少空间,咱们在链接脚本里界说了

start_copy_addr = . ;

其间
start_copy_addr 为标号的称号,它的值被赋成 . 其间这个dot代表当时链接的地址,此刻的地址是从0x80000000开端加上initsystem.o 里一切代码长度后的值。那咱们从这个地址开端copy代码是最合适的了,那这个地址在ARM 汇编里怎样运用哪?

.extern LDR R1,=start_copy_addr @R1指向源地址@先声明这个标号

LDR R1,=start_copy_addr @R1 指向源地址

在c言语里应该这样:

extern start_copy_addr ;

然后 运用&start_copy_addr 的方法来运用这个标号的值。

7:相关材料

我下面列出的相关材料都上传到我的csdn资源中。下载地址:http://download.csdn.net/detail/zhanglianpin/7546779

ARM开发攻略中文版系列文章。

gnu-assembler.pdf

gnu_Linker.pdf

linker&&loader.pdf

8:跋文

其实,程序链接这方面的常识,对了解程序结构是很有协助的。特别做嵌入式软件开发。咱们阅览uboot linux 内核等开源程序时,若不了解链接进程是很难阅览透彻的。
别的,咱们写程序时,其实有许多伪指令都是针对编译、链接器的。了解了这方面的常识,咱们综合运用程序、链接才干充沛完成咱们的主意。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/qianrushi/ruanjian/277271.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部