ARM處理器學(xué)習(xí)之GPIO操作篇(gnu link script)
1:主要內(nèi)容
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.htm3:主要思路
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
評(píng)論