打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
ARM处理器学习之GPIO操作

http://blog.csdn.net/zhanglianpin/article/details/34106049

2014

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


  1. @******************************************************************************  
  2. @ 文件名  :initsystem.s  
  3. @ 功    能:初始化系统并copy代码  
  4. @   
  5. @ 作者    :张连聘  
  6. @ 创建时间:2014-06-22  
  7. @******************************************************************************  
  8.   
  9. .text  
  10. .global _start  
  11.   
  12.               
  13.             @声明常量  
  14.             .equ   DATA_DST,0x40000000  @目的地址  
  15.             .equ   DATA_SRC,0x80000000  @源地址  
  16.       
  17. @引入外部标号  
  18.             .extern  MainLoop  
  19.             .extern  start_copy_addr  
  20.               
  21. _start:  
  22.   
  23.    
  24.                   
  25.                     LDR PC,  ResetAddr  
  26.            
  27. ResetAddr: .word  ResetInit  
  28.   
  29. ResetInit:  
  30.   
  31.                     LDR R0,=DATA_DST            @RO 指向目的地址  
  32.                     LDR R1,=start_copy_addr     @R1 指向源地址  
  33.                     MOV R10,#128                @复制的个数为128*8*4=4K  
  34. CopyLoop:           LDMIA R1!,{R2-R9}           @从R1指定的内存地址处装载数据到R2--R9中  
  35.                     STMIA R0!,{R2-R9}           @把R2--R9的数据复制到R0指定的内存中  
  36.                     SUBS  R10,R10,#1  
  37.                     BNE   CopyLoop  
  38.                       
  39.                       
  40.                     LDR PC,=MainLoop  
  41. .end  

control_led.s

  1. @******************************************************************************  
  2. @ 文件名  :control_led.s  
  3. @ 功    能:利用P2.28控制led灯闪烁  
  4. @   
  5. @ 作者    :张连聘  
  6. @ 创建时间:2014-06-08  
  7. @******************************************************************************  
  8.   
  9.  .text  
  10. .global MainLoop  
  11.   
  12.   
  13. StartMain:            
  14.               
  15.               
  16.             @定义程序中使用到的常量          
  17.             .equ   IO2DIR  ,0xE0028028  @ 控制IO0的输入、输出属性寄存器  
  18.             .equ   IO2SET  ,0xE0028024  @IO2输出1控制寄存器  
  19.             .equ   IO2CLR  ,0xE002802C  @IO2输出0控制寄存器  
  20.             .equ   LEDCON  ,(1<<28)     @0x10000000  
  21.               
  22.               
  23.   
  24. MainLoop:             
  25.                       
  26.                     LDR R0,=IO2DIR      @IO2DIR                
  27.                     LDR R1,=LEDCON    
  28.                     STR R1,[R0]         @设置P2.28为输出  
  29.   
  30.                     LDR R0,=IO2CLR    
  31.                     LDR R1,=LEDCON  
  32.                     STR R1,[R0]        @P2.28为输出0,熄灭led  
  33.                     BL DELAYS          @调用延时程序  
  34.               
  35.                     LDR R0,=IO2SET  
  36.                     LDR R1,=LEDCON            
  37.                     STR R1,[R0]        @P2.28为输出1,点亮led  
  38.                     BL DELAYS          @调用延时程序  
  39.           
  40.               
  41.                     B MainLoop  
  42.               
  43. @******************************************************************************  
  44. @ 名    CopyData  
  45. @ 功    能:复制代码,从0x8000***---->0x40000000 size:4K  
  46. @ 入口参数:无  
  47. @ 出口参数:无  
  48. @ 占用资源:  
  49. @******************************************************************************  
  50. /*  
  51.    
  52. CopyData:           LDR R0,=DATA_DST            @RO 指向目的地址  
  53.                     MOV R10,#128                @复制的个数为128*8*4=4K  
  54. CopyLoop:           LDMIA R1!,{R2-R9}           @从R1指定的内存地址处装载数据到R2--R9中  
  55.                     STMIA R0!,{R2-R9}           @把R2--R9的数据复制到R0指定的内存中  
  56.                     SUBS  R10,R10,#1  
  57.                     BNE   CopyLoop  
  58.                     MOV   PC,LR  
  59. */            
  60. @******************************************************************************  
  61. @ 名    称:DELAYS  
  62. @ 功    能:软件延时  
  63. @ 入口参数:无  
  64. @ 出口参数:无  
  65. @ 占用资源:R7  
  66. @******************************************************************************  
  67.   
  68. DELAYS:   
  69.             LDR     R7,=0x00080000      @ 延时参数  
  70. DELAYS_L1:  SUBS    R7,R7,#1            @ R7 = R7-1  
  71.             BNE     DELAYS_L1           @ 判断R7-1结果是否为0,若不为0则跳转  
  72.             MOV     PC,LR               @ 返回              
  73.               
  74. .end  

led_control.lds

  1. /*  
  2. * led_control 的链接脚本。  
  3. *  
  4. *  
  5. */  
  6.   
  7.  MEMORY  
  8. {  
  9.    rom (rx)  : ORIGIN = 0x80000000, LENGTH = 2M  
  10.    ram (!rx) : ORIGIN = 0x40000000, LENGTH = 2M  
  11. }  
  12. ENTRY(_start)  
  13. SECTIONS  
  14. {  
  15.   
  16.     . = 0x80000000 ;  
  17.     .init :   
  18.     {     
  19.         initsystem.o(.text)  
  20.         start_copy_addr = . ;  
  21.     } >rom  
  22.     . = 0x40000000 ;  
  23.     .main :  
  24.     AT (ADDR(.init)+SIZEOF(.init))  
  25.     {     
  26.         control_led.o(.text)  
  27.     } >ram  
  28.   
  29.   
  30.       
  31.       
  32. }  
Makefile

  1. control_led.bin:control_led.s initsystem.s  
  2.     arm-linux-gcc -g -c -o control_led.o control_led.s  
  3.     arm-linux-gcc -g -c -o initsystem.o initsystem.s  
  4.     arm-linux-ld -Tled_control.lds -nostdlib -g  control_led.o initsystem.o -o control_led_elf  
  5.     arm-linux-objcopy -O binary -S control_led_elf  control_led.bin  
  6. clean:  
  7.     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 内核等开源程序时,若不理解链接过程是很难阅读透彻的。
另外,我们写程序时,其实有很多伪指令都是针对编译、链接器的。了解了这方面的知识,我们综合利用程序、链接才能充分实现我们的想法。感兴趣的同志可以仔细
翻阅我上面列出的参考文档。愿此文对您有帮助。
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
【Linux笔记】LED驱动程序
S7-1200 1500 指令说明示例RD_ADDR:根据硬件标识符确定 IO 地址
LDQM UDQM
LPC启动过程和重映射概念
LPC2000系列ARM处理器重映射原理的分析与实现
教你如何找到导致程序跑飞的指令
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服