新聞中心

EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > ARM處理器學(xué)習(xí)之GPIO操作篇(gnu link script)

ARM處理器學(xué)習(xí)之GPIO操作篇(gnu link script)

作者: 時(shí)間:2016-11-09 來(lái)源:網(wǎng)絡(luò) 收藏

1:主要內(nèi)容

本文主要介紹了VMA、LMA的相關(guān)概念,gnu link script的作用和使用方法。

2:引言

我們程序員剛開(kāi)始學(xué)習(xí)編寫(xiě)程序時(shí),都會(huì)接觸到一個(gè) " *.C " 文件要經(jīng)過(guò)編譯、鏈接等過(guò)程才能變成可以執(zhí)行的程序。至于這里的鏈接到底怎么回事,我們今天就來(lái)談?wù)勥@方面的內(nèi)容?,F(xiàn)在,我們有這樣一套ARM7的硬件開(kāi)發(fā)環(huán)境,0X80000000地址開(kāi)始BANK0 我們用的是NorFlash,0X40000000地址是芯片內(nèi)部的RAM。我編譯、鏈接的程序下載到0x80000000地址處。而真正運(yùn)行時(shí),一部分初始化代碼在0X80000000運(yùn)行,初始化完畢后,將主要工作的代碼copy到內(nèi)部RAM 0X40000000開(kāi)始的地方運(yùn)行。因?yàn)閮?nèi)部RAM運(yùn)行程序比較快,所以我想NorFlash充當(dāng)電腦的硬盤(pán)的作用,讓其主要程序在RAM里運(yùn)行。這是今天主要的內(nèi)容,當(dāng)然,程序的功能還是和上一節(jié)ARM處理器學(xué)習(xí)之--GPIO操作篇一樣:讓板子上的一個(gè)LED燈閃爍。

本文引用地址:http://m.butianyuan.cn/article/201611/317963.htm

3:主要思路

我把程序分成兩個(gè)主要部分,第一部分負(fù)責(zé)對(duì)芯片基本的操作和copy代碼(從Norflashcopy到Sdram中),然后跳轉(zhuǎn)到Sdram中去執(zhí)行程序。第二部分為程序的主要部分。那這樣的話(huà),我對(duì)這兩部分程序通過(guò)鏈接器鏈接時(shí)則需要設(shè)置不同的運(yùn)行地址。即,第一部分代碼鏈接的運(yùn)行地址是0X80000000,而第二部分代碼鏈接的運(yùn)行地址是0X40000000。但這兩部分組成的映像文件的下載地址都是0X80000000.我想這個(gè)應(yīng)該能通過(guò)鏈接器進(jìn)行設(shè)置,通過(guò)查詢(xún)資料得知,這個(gè)可以通過(guò)鏈接器的鏈接腳本來(lái)實(shí)現(xiàn),在gnu arm 的鏈接器上是通過(guò)書(shū)寫(xiě)一個(gè)*。lds的鏈接腳本來(lái)實(shí)現(xiàn)的。

4:相關(guān)知識(shí)點(diǎn)

經(jīng)過(guò)編譯,鏈接后生成的可執(zhí)行文件,其實(shí)有一定的結(jié)構(gòu)。主要分為code段,data段,zi段(在gnu linux 下為.text .data .bss段)。這個(gè)code段,就是我們使用匯編,c語(yǔ)言,c++寫(xiě)的程序指令,而data是程序中使用的變量,zi是程序中定義的未初始化的變量(由于這些內(nèi)容本來(lái)就沒(méi)有被初始化,所以這些zi段沒(méi)有必要存儲(chǔ)在生成的映像文件中,只是在程序真正運(yùn)行時(shí)在相應(yīng)的地址處預(yù)留出相應(yīng)的空間即可)。文件的鏈接簡(jiǎn)圖:

VMA(Virtual Memory Address)和LMA:(Load Memory Address)。這個(gè)LMA地址是程序裝載到存儲(chǔ)器時(shí)的地址,WMA可以理解成程序真正運(yùn)行時(shí)所在的地址。

而鏈接器指定的鏈接地址要和程序真正運(yùn)行時(shí)所在的地址一致。這個(gè)也好理解,鏈接器就是根據(jù)你指定的鏈接地址進(jìn)行整個(gè)映像的鏈接操作,一些絕對(duì)跳轉(zhuǎn)指令就是根據(jù)鏈接指定的地址進(jìn)行更改PC值的,這些在上一講有所解釋。當(dāng)然一般情況下,LMA和VMA的地址是一樣的,不過(guò),在有些嵌入式開(kāi)發(fā)的過(guò)程中,程序的裝載地址和運(yùn)行地址不一樣,那在訪(fǎng)問(wèn)鏈接地址和裝載地址不一樣的code、data、zi段的時(shí)候應(yīng)該在真正訪(fǎng)問(wèn)前將其copy到鏈接指定的地址上去。

gnu 鏈接腳本的格式。gnu 鏈接腳本是一個(gè)描述文本,用來(lái)描述怎么鏈接最終的映像文件。關(guān)于這個(gè)鏈接腳本文件,我們?cè)诰唧w案例中了解其用法。

5:實(shí)驗(yàn)源碼

initsystem.s

@****************************************************************************** @ 文件名 :initsystem.s @ 功 能:初始化系統(tǒng)并copy代碼 @ @ 作者 :張連聘 @ 創(chuàng)建時(shí)間:2014-06-22 @****************************************************************************** .text .global _start @聲明常量 .equ DATA_DST,0x40000000 @目的地址 .equ DATA_SRC,0x80000000 @源地址 @引入外部標(biāo)號(hào) .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 @復(fù)制的個(gè)數(shù)為128*8*4=4K CopyLoop: LDMIA R1!,{R2-R9} @從R1指定的內(nèi)存地址處裝載數(shù)據(jù)到R2--R9中 STMIA R0!,{R2-R9} @把R2--R9的數(shù)據(jù)復(fù)制到R0指定的內(nèi)存中 SUBS R10,R10,#1 BNE CopyLoop LDR PC,=MainLoop .end

control_led.s

@****************************************************************************** @ 文件名 :control_led.s @ 功 能:利用P2.28控制led燈閃爍 @ @ 作者 :張連聘 @ 創(chuàng)建時(shí)間: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] @設(shè)置P2.28為輸出 LDR R0,=IO2CLR LDR R1,=LEDCON STR R1,[R0] @P2.28為輸出0,熄滅led BL DELAYS @調(diào)用延時(shí)程序 LDR R0,=IO2SET LDR R1,=LEDCON STR R1,[R0] @P2.28為輸出1,點(diǎn)亮led BL DELAYS @調(diào)用延時(shí)程序 B MainLoop @****************************************************************************** @ 名 CopyData @ 功 能:復(fù)制代碼,從0x8000***---->0x40000000 size:4K @ 入口參數(shù):無(wú) @ 出口參數(shù):無(wú) @ 占用資源: @****************************************************************************** /* CopyData: LDR R0,=DATA_DST @RO 指向目的地址 MOV R10,#128 @復(fù)制的個(gè)數(shù)為128*8*4=4K CopyLoop: LDMIA R1!,{R2-R9} @從R1指定的內(nèi)存地址處裝載數(shù)據(jù)到R2--R9中 STMIA R0!,{R2-R9} @把R2--R9的數(shù)據(jù)復(fù)制到R0指定的內(nèi)存中 SUBS R10,R10,#1 BNE CopyLoop MOV PC,LR */ @****************************************************************************** @ 名 稱(chēng):DELAYS @ 功 能:軟件延時(shí) @ 入口參數(shù):無(wú) @ 出口參數(shù):無(wú) @ 占用資源:R7 @****************************************************************************** DELAYS: LDR R7,=0x00080000 @ 延時(shí)參數(shù) DELAYS_L1: SUBS R7,R7,#1 @ R7 = R7-1 BNE DELAYS_L1 @ 判斷R7-1結(jié)果是否為0,若不為0則跳轉(zhuǎn) 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:源碼重點(diǎn)解釋

關(guān)于上面兩個(gè).s的匯編文件,這里就不再贅述,請(qǐng)讀者自行分析。主要說(shuō)說(shuō)這個(gè)鏈接腳本的相關(guān)知識(shí)。gnu 鏈接腳本的詳細(xì)資料參見(jiàn),gnu_Linker.pdf這個(gè)官方資料。

鏈接文件的細(xì)節(jié)問(wèn)題這里也不再提及,只說(shuō)一下關(guān)鍵點(diǎn)。

1問(wèn):為什么我將初始化,copy的代碼放在一個(gè)單獨(dú)的文件里?

1答:我最開(kāi)始把所有代碼放在一個(gè)文件里,使用.section 偽指令定義新的段名,在鏈接腳本里使用不同的地址存放不同的段。但程序一直不能正常運(yùn)行,后反編譯得知,我這樣做,鏈接出來(lái)的映像文件和我在鏈接腳本里指定的不一樣。后查資料得知,gnu link 對(duì)每個(gè)源文件都有默認(rèn)的三個(gè)段名:.text .data .bss 。鏈接腳本里的輸入段只允許這些段名。因此我將啟動(dòng)代碼單獨(dú)放在一個(gè)文件里,且所有的代碼均在 .text 這個(gè)段里。

2問(wèn):我在鏈接腳本里能定義標(biāo)號(hào)嗎?定義的標(biāo)號(hào),怎么在匯編里引用吶?

2答:可以在鏈接腳本里定義標(biāo)號(hào),這里定義的標(biāo)號(hào)的意義等同于在編程語(yǔ)言里的地址。在上面給出的例子中,我們copy代碼并不是從第一條指令開(kāi)始copy的,而是從執(zhí)行完初始化和copy代碼這些功能后開(kāi)始指令。那我們?cè)趺粗纈nitsystem里面的指令到底占用多少空間,我們?cè)阪溄幽_本里定義了

start_copy_addr = . ;

其中
start_copy_addr 為標(biāo)號(hào)的名稱(chēng),它的值被賦成 . 其中這個(gè)dot代表當(dāng)前鏈接的地址,此時(shí)的地址是從0x80000000開(kāi)始加上initsystem.o 里所有代碼長(zhǎng)度后的值。那我們從這個(gè)地址開(kāi)始copy代碼是最合適的了,那這個(gè)地址在ARM 匯編里怎么使用哪?

.extern LDR R1,=start_copy_addr @R1指向源地址@先聲明這個(gè)標(biāo)號(hào)

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

在c語(yǔ)言里應(yīng)該這樣:

extern start_copy_addr ;

然后 使用&start_copy_addr 的方法來(lái)使用這個(gè)標(biāo)號(hào)的值。

7:相關(guān)資料

我下面列出的相關(guān)資料都上傳到我的csdn資源中。下載地址:http://download.csdn.net/detail/zhanglianpin/7546779

ARM開(kāi)發(fā)指南中文版系列文章。

gnu-assembler.pdf

gnu_Linker.pdf

linker&&loader.pdf

8:后記

其實(shí),程序鏈接這方面的知識(shí),對(duì)理解程序結(jié)構(gòu)是很有幫助的。尤其做嵌入式軟件開(kāi)發(fā)。我們閱讀uboot linux 內(nèi)核等開(kāi)源程序時(shí),若不理解鏈接過(guò)程是很難閱讀透徹的。
另外,我們寫(xiě)程序時(shí),其實(shí)有很多偽指令都是針對(duì)編譯、鏈接器的。了解了這方面的知識(shí),我們綜合利用程序、鏈接才能充分實(shí)現(xiàn)我們的想法。


關(guān)鍵詞: ARM處理器GPI

評(píng)論


技術(shù)專(zhuān)區(qū)

關(guān)閉