ARM處理器學(xué)習(xí)之GPIO操作篇(gnu link script)
1:主要內(nèi)容
2:引言
我們程序員剛開始學(xué)習(xí)編寫程序時,都會接觸到一個 " *.C " 文件要經(jīng)過編譯、鏈接等過程才能變成可以執(zhí)行的程序。至于這里的鏈接到底怎么回事,我們今天就來談?wù)勥@方面的內(nèi)容?,F(xiàn)在,我們有這樣一套ARM7的硬件開發(fā)環(huán)境,0X80000000地址開始BANK0 我們用的是NorFlash,0X40000000地址是芯片內(nèi)部的RAM。我編譯、鏈接的程序下載到0x80000000地址處。而真正運行時,一部分初始化代碼在0X80000000運行,初始化完畢后,將主要工作的代碼copy到內(nèi)部RAM 0X40000000開始的地方運行。因為內(nèi)部RAM運行程序比較快,所以我想NorFlash充當(dāng)電腦的硬盤的作用,讓其主要程序在RAM里運行。這是今天主要的內(nèi)容,當(dāng)然,程序的功能還是和上一節(jié)ARM處理器學(xué)習(xí)之--GPIO操作篇一樣:讓板子上的一個LED燈閃爍。
本文引用地址:http://butianyuan.cn/article/201611/317963.htm3:主要思路
4:相關(guān)知識點
經(jīng)過編譯,鏈接后生成的可執(zhí)行文件,其實有一定的結(jié)構(gòu)。主要分為code段,data段,zi段(在gnu linux 下為.text .data .bss段)。這個code段,就是我們使用匯編,c語言,c++寫的程序指令,而data是程序中使用的變量,zi是程序中定義的未初始化的變量(由于這些內(nèi)容本來就沒有被初始化,所以這些zi段沒有必要存儲在生成的映像文件中,只是在程序真正運行時在相應(yīng)的地址處預(yù)留出相應(yīng)的空間即可)。文件的鏈接簡圖:
VMA(Virtual Memory Address)和LMA:(Load Memory Address)。這個LMA地址是程序裝載到存儲器時的地址,WMA可以理解成程序真正運行時所在的地址。
而鏈接器指定的鏈接地址要和程序真正運行時所在的地址一致。這個也好理解,鏈接器就是根據(jù)你指定的鏈接地址進(jìn)行整個映像的鏈接操作,一些絕對跳轉(zhuǎn)指令就是根據(jù)鏈接指定的地址進(jìn)行更改PC值的,這些在上一講有所解釋。當(dāng)然一般情況下,LMA和VMA的地址是一樣的,不過,在有些嵌入式開發(fā)的過程中,程序的裝載地址和運行地址不一樣,那在訪問鏈接地址和裝載地址不一樣的code、data、zi段的時候應(yīng)該在真正訪問前將其copy到鏈接指定的地址上去。
gnu 鏈接腳本的格式。gnu 鏈接腳本是一個描述文本,用來描述怎么鏈接最終的映像文件。關(guān)于這個鏈接腳本文件,我們在具體案例中了解其用法。
5:實驗源碼
initsystem.s
@****************************************************************************** @ 文件名 :initsystem.s @ 功 能:初始化系統(tǒng)并copy代碼 @ @ 作者 :張連聘 @ 創(chuàng)建時間:2014-06-22 @****************************************************************************** .text .global _start @聲明常量 .equ DATA_DST,0x40000000 @目的地址 .equ DATA_SRC,0x80000000 @源地址 @引入外部標(biā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ù)制的個數(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)建時間: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)用延時程序 LDR R0,=IO2SET LDR R1,=LEDCON STR R1,[R0] @P2.28為輸出1,點亮led BL DELAYS @調(diào)用延時程序 B MainLoop @****************************************************************************** @ 名 CopyData @ 功 能:復(fù)制代碼,從0x8000***---->0x40000000 size:4K @ 入口參數(shù):無 @ 出口參數(shù):無 @ 占用資源: @****************************************************************************** /* CopyData: LDR R0,=DATA_DST @RO 指向目的地址 MOV R10,#128 @復(fù)制的個數(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 */ @****************************************************************************** @ 名 稱:DELAYS @ 功 能:軟件延時 @ 入口參數(shù):無 @ 出口參數(shù):無 @ 占用資源:R7 @****************************************************************************** DELAYS: LDR R7,=0x00080000 @ 延時參數(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:源碼重點解釋
關(guān)于上面兩個.s的匯編文件,這里就不再贅述,請讀者自行分析。主要說說這個鏈接腳本的相關(guān)知識。gnu 鏈接腳本的詳細(xì)資料參見,gnu_Linker.pdf這個官方資料。
鏈接文件的細(xì)節(jié)問題這里也不再提及,只說一下關(guān)鍵點。
1問:為什么我將初始化,copy的代碼放在一個單獨的文件里?
1答:我最開始把所有代碼放在一個文件里,使用.section 偽指令定義新的段名,在鏈接腳本里使用不同的地址存放不同的段。但程序一直不能正常運行,后反編譯得知,我這樣做,鏈接出來的映像文件和我在鏈接腳本里指定的不一樣。后查資料得知,gnu link 對每個源文件都有默認(rèn)的三個段名:.text .data .bss 。鏈接腳本里的輸入段只允許這些段名。因此我將啟動代碼單獨放在一個文件里,且所有的代碼均在 .text 這個段里。
2問:我在鏈接腳本里能定義標(biāo)號嗎?定義的標(biāo)號,怎么在匯編里引用吶?
2答:可以在鏈接腳本里定義標(biāo)號,這里定義的標(biāo)號的意義等同于在編程語言里的地址。在上面給出的例子中,我們copy代碼并不是從第一條指令開始copy的,而是從執(zhí)行完初始化和copy代碼這些功能后開始指令。那我們怎么知道initsystem里面的指令到底占用多少空間,我們在鏈接腳本里定義了
start_copy_addr = . ;
其中
start_copy_addr 為標(biāo)號的名稱,它的值被賦成 . 其中這個dot代表當(dāng)前鏈接的地址,此時的地址是從0x80000000開始加上initsystem.o 里所有代碼長度后的值。那我們從這個地址開始copy代碼是最合適的了,那這個地址在ARM 匯編里怎么使用哪?
.extern LDR R1,=start_copy_addr @R1指向源地址@先聲明這個標(biāo)號
LDR R1,=start_copy_addr @R1 指向源地址
在c語言里應(yīng)該這樣:
extern start_copy_addr ;
然后 使用&start_copy_addr 的方法來使用這個標(biāo)號的值。
7:相關(guān)資料
我下面列出的相關(guān)資料都上傳到我的csdn資源中。下載地址:http://download.csdn.net/detail/zhanglianpin/7546779
ARM開發(fā)指南中文版系列文章。
gnu-assembler.pdf
gnu_Linker.pdf
linker&&loader.pdf
評論