chinese直男口爆体育生外卖, 99久久er热在这里只有精品99, 又色又爽又黄18禁美女裸身无遮挡, gogogo高清免费观看日本电视,私密按摩师高清版在线,人妻视频毛茸茸,91论坛 兴趣闲谈,欧美 亚洲 精品 8区,国产精品久久久久精品免费

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

在極海APM32系列MCU中如何把代碼重定位到SDRAM運(yùn)行

Geehy極海半導(dǎo)體 ? 來(lái)源:21ic論壇極海半導(dǎo)體專(zhuān)區(qū) ? 2025-11-04 09:14 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

《APM32芯得》系列內(nèi)容為用戶(hù)使用APM32系列產(chǎn)品的經(jīng)驗(yàn)總結(jié),均轉(zhuǎn)載自21ic論壇極海半導(dǎo)體專(zhuān)區(qū),全文未作任何修改,未經(jīng)原文作者授權(quán)禁止轉(zhuǎn)載。

在有些情況下,我們想要把代碼放到SDRAM運(yùn)行。下面介紹在APM32的MCU中,如何把代碼重定位到SDRAM運(yùn)行。對(duì)于不同APM32系列的MCU,方法都是一樣的。

1、APM32啟動(dòng)模式

熟悉MCU都知道,可以通過(guò)配置 BOOT0/1 兩個(gè)引腳的高低電平,選擇不同的啟動(dòng)方式。對(duì)于APM32來(lái)說(shuō)也是一樣的,可以配置 BOOT0/1 引腳電平選擇不同的啟動(dòng)模式,下表是我從APM32的用戶(hù)手冊(cè)截圖的啟動(dòng)模式配置表:

9cdd345e-b654-11f0-8c8f-92fbcf53809c.png

有內(nèi)置SRAM、Flash、系統(tǒng)存儲(chǔ)區(qū)啟動(dòng)3種模式。對(duì)于讓程序重定位到SDRAM運(yùn)行,我們選擇Flash啟動(dòng)即可。

不同的啟動(dòng)模式,本質(zhì)其實(shí)就是MCU上電時(shí),從哪個(gè)地址處讀取出數(shù)據(jù)然后賦值給PC寄存器,以及SP寄存器。比如選擇從Flash啟動(dòng),MCU上電時(shí),0x08000000處起始的4個(gè)字節(jié)的數(shù)據(jù),賦值給SP寄存器,08000004處起始的4個(gè)字節(jié)的數(shù)據(jù)賦值給PC寄存器。設(shè)置了SP和PC寄存器,程序就可以正常運(yùn)行了。

MCU上電,PC寄存器從哪里開(kāi)始取值,和后面要講的把代碼搬運(yùn)到SDRAM運(yùn)行,是有關(guān)聯(lián)的,所以這里提一下APM32的啟動(dòng)模式。

2、程序段概念的引入

一個(gè)程序的源碼被編譯之后,鏈接器會(huì)根據(jù)代碼中的不同屬性,把他們劃分為一個(gè)個(gè)不同的段,比如 .text段、.rodata段、.data段、.bss/.zi段等等,還有用戶(hù)也可以自定義一些段,比如把所有初始化的代碼,自定義一個(gè)初始化段。

.text段:代碼段或者文本段。我們編寫(xiě)的代碼,鏈接器都是歸類(lèi)在這個(gè)段的。對(duì)于MCU來(lái)說(shuō)就是存放在內(nèi)部的Flash中。

.rodata段:只讀數(shù)據(jù)段。比如我們使用const定義的變量,或者定義的字符串這些,都被鏈接到只讀數(shù)據(jù)段。只讀數(shù)據(jù)段和代碼段,都是只能讀不能寫(xiě),所以都是存放在Flash中的。

.data段:可讀可寫(xiě)的數(shù)據(jù)段。我們定義的初始化為非0的全局變量、非0的靜態(tài)局部變量,都是存放在這個(gè)段的。

.bss/.zi段:.bss段或者.zi段,都是同一個(gè)段,只是叫法不一樣。.bss段和.data段是一樣的,都是可讀可寫(xiě)的數(shù)據(jù)段的一種。.bss段存放的就是初始值為0的全局變量或者初始值為0的靜態(tài)局部變量。

既然.data段和.bss段存放的內(nèi)容基本一樣,為什么要把這兩個(gè)段分開(kāi)存放?這是因?yàn)?bss段的初值是0,不需要燒錄到Flash里面存放,在程序使用之前,我們把.bss段的對(duì)應(yīng)區(qū)域給清0就行了,這樣不需要浪費(fèi)Flash空間。

堆:一段空閑的內(nèi)存空間,可以給程序員自由使用??梢酝ㄟ^(guò)一些內(nèi)存管理接口函數(shù)進(jìn)行申請(qǐng)和釋放。

棧:也是一塊內(nèi)存空間,不過(guò)程序自行管理。C語(yǔ)言的運(yùn)行需要棧,MCU上電時(shí)就需要把SP(棧)寄存器指向一片正??捎玫腞AM作為棧來(lái)使用。

用戶(hù)如果有需要,也可以自定義自己的段,然后鏈接器會(huì)根據(jù)用戶(hù)的要求,把指定的代碼編譯到自定義的段。

我們要把代碼重定位到SDRAM運(yùn)行,本質(zhì)就是復(fù)制這些段的數(shù)據(jù),把這些數(shù)據(jù)復(fù)制到它們應(yīng)該位于的地方(代碼應(yīng)該要位于鏈接地址處運(yùn)行)。

3. keil散列文件語(yǔ)法分析

前面提到,代碼的重定位,本質(zhì)就是數(shù)據(jù)的復(fù)制。數(shù)據(jù)的復(fù)制有3個(gè)要點(diǎn)要知道:從那里復(fù)制(源地址)、復(fù)制到哪里去(目的地址)、復(fù)制多長(zhǎng)(長(zhǎng)度)。

源地址:我們要從哪里開(kāi)始復(fù)制數(shù)據(jù)呢?其實(shí)就是加載地址,我們程序燒錄到哪個(gè)位置,那就是加載地址。比如程序燒寫(xiě)到內(nèi)部的Flash中,那么加載地址就是0x08000000 。程序存放到外部的SPI Flash中,加載地址就是存放在外部SPI Flash的地址。

目的地址:要把程序復(fù)制到這里的地址,就是程序的鏈接地址,程序的運(yùn)行就是要位于它的鏈接地址處運(yùn)行(當(dāng)然如果寫(xiě)的所有代碼都是位置無(wú)關(guān)碼,那么可以不在鏈接地址處運(yùn)行)。

長(zhǎng)度:復(fù)制多長(zhǎng)。

上面提到的這些復(fù)制數(shù)據(jù)所需的信息,所有的這一切都可以從鏈接腳本中獲取到,它是用來(lái)指導(dǎo)鏈接器如何進(jìn)行鏈接的一種規(guī)則文件。對(duì)于Keil來(lái)說(shuō),鏈接腳本指的就是散列文件。

3.1 Keil默認(rèn)的散列文件示例

下面的代碼是keil自動(dòng)生成的APM32F407ZG型號(hào)的散列文件:

; *************************************************************

; *** Scatter-Loading Description File generated by uVision ***

; *************************************************************

LR_IROM1 0x08000000 0x00100000 { ; load region size_region

ER_IROM1 0x08000000 0x00100000 { ; load address = execution address

*.o (RESET, +First)

*(InRoot$Sections)

.ANY (+RO)

.ANY (+XO)

}

RW_IRAM1 0x20000000 0x00020000 { ; RW data

.ANY (+RW +ZI)

}

}

一個(gè)散列文件,是由多個(gè)加載域、執(zhí)行域和輸入段所組成??梢酝ㄟ^(guò)Keil幫助文檔獲得這些內(nèi)容的講解。

3.2 散列文件語(yǔ)法

上面的示例,每個(gè)數(shù)據(jù)、符號(hào)代表什么意義?這些內(nèi)容我們可以通過(guò)Keil的幫助文檔學(xué)習(xí)到,安裝了Keil軟件,就可以獲取到幫助文檔信息了。

9d427f08-b654-11f0-8c8f-92fbcf53809c.png

打開(kāi)鏈接相關(guān)的文檔,找到散列文件的語(yǔ)法介紹。

下圖就是幫助文檔關(guān)于散列文件的組成結(jié)構(gòu):

9dab79e0-b654-11f0-8c8f-92fbcf53809c.png

根據(jù)上圖:一個(gè)散列文件可以一個(gè)或多個(gè)加載域,每個(gè)加載域又可以包含多個(gè)可執(zhí)行域,而可執(zhí)行域是由各個(gè)段(如代碼段、數(shù)據(jù)段等)組成的。

3.2.1加載域語(yǔ)法

load_region_description ::=

load_region_name ( base_address | ("+" offset )) [ attribute_list ] [ max_size ]

"{"

execution_region_description +

"}"

load_region_name就是加載域的名稱(chēng),base_address加載域的基地址,加載域的長(zhǎng)度(就是整個(gè)程序燒錄的大?。H缓蠹虞d域里面包含一個(gè)或多個(gè)可執(zhí)行域。

以上面的示例為例:

LR_IROM1 0x08000000 0x00100000 { ; load region size_region

}

加載域名稱(chēng)就是 LR_IROM1,起始地址0x08000000,長(zhǎng)度0x00100000。

3.2.2 可執(zhí)行域語(yǔ)法

execution_region_description ::=exec_region_name ( base_address | "+" offset ) [ attribute_list ] [ max_size | length ]"{"input_section_description *"}"

和加載域的描述也是類(lèi)似的。然后可執(zhí)行域里面就是由各個(gè)段組成的。

3.2.3 輸入段

9e0ddab8-b654-11f0-8c8f-92fbcf53809c.png

前面是選擇代碼的哪些區(qū)域空間鏈接進(jìn)這個(gè)段,后面是段的名字。

比如:main.o(+RO) 就是說(shuō)main.o文件鏈接到RO段。

常見(jiàn)段類(lèi)型的解析:

*.o (RESET, +First) :指的是所有的.o文件的RESET段,+First就是要求RESET段要鏈接到程序最開(kāi)始的地方。

*(InRoot$$Sections):鏈接器去鏈接Keil自帶的一部分代碼。這部分代碼的作用主要是數(shù)據(jù)段的重定位和清除bss段

.ANY (+RO):.ANY作用和 * 一樣,指的是所有的意思,但是優(yōu)先級(jí)比 * 低。這里說(shuō)是把所有文件的RO段(Read Only段)放到這里。

.ANY (+XO):所有的 execute-only 段。

3.3如何通過(guò)散列文件獲取源、目的、長(zhǎng)度

我們學(xué)習(xí)散列文件的目的就是為了得到代碼重定位的源(加載地址)、目的(鏈接地址)、和長(zhǎng)度。那么我們?nèi)绾瓮ㄟ^(guò)散列文件獲取這些信息?

Keil的鏈接器定義了各種符號(hào),通過(guò)這些符號(hào)我們可以獲取到這些信息。

3.3.1 可執(zhí)行域區(qū)域信息

9e72fe84-b654-11f0-8c8f-92fbcf53809c.png

通過(guò)可執(zhí)行域的這些符號(hào),我們就知道把代碼復(fù)制到哪里去了。

3.3.2 加載域區(qū)域信息

9ed891c2-b654-11f0-8c8f-92fbcf53809c.png

通過(guò)加載域的這些信息,可以獲取到各個(gè)段(代碼段、數(shù)據(jù)段、bss段)的起始地址,和長(zhǎng)度信息。

4. 代碼重定位到SDRAM的方法

我們?yōu)槭裁匆汛a重定位到SDRAM運(yùn)行?

一般有兩種情況:

內(nèi)部Flash空間不足,不能存放下所有的代碼。這個(gè)時(shí)候我們就只能把編譯得到的bin文件燒錄到外部的存儲(chǔ)設(shè)備了,比如SPI Flash等。但是SPI Flash根本就不能運(yùn)行代碼,所以MCU上電后就需要把SPI Flash的bin文件,搬運(yùn)到SDRAM或者其他可運(yùn)行代碼的存儲(chǔ)設(shè)備。

為了得到更快的執(zhí)行速率,這個(gè)時(shí)候我們可以把代碼搬運(yùn)到SRAM或者SDRAM執(zhí)行。(但是對(duì)于MCU來(lái)說(shuō),我不確定是內(nèi)部的Flash執(zhí)行代碼更快還是SDRAM更快)

對(duì)于第一種情況,是很常用的。比如嵌入式Linux的設(shè)備,就是這種啟動(dòng)方式,內(nèi)核鏡像存儲(chǔ)在外部EMMC、SD卡等這種大容量設(shè)備中,但是他們都無(wú)法執(zhí)行代碼,所以上電后會(huì)把內(nèi)核鏡像搬運(yùn)到內(nèi)存中運(yùn)行。

根據(jù)這兩種情況,我們可以有兩種方法把代碼搬運(yùn)到SDRAM運(yùn)行。

應(yīng)用程序自己復(fù)制自己

通過(guò)Bootloader程序,復(fù)制應(yīng)用程序

4.1 程序自己復(fù)制自己

當(dāng)整個(gè)應(yīng)用程序都燒寫(xiě)在MCU的內(nèi)部Flash時(shí),這個(gè)時(shí)候我們可以使用這種方法,讓?xiě)?yīng)用程序自己復(fù)制自己到內(nèi)存中,比如SDRAM運(yùn)行。

當(dāng)然在這種情況中,我好像想不到把程序復(fù)制到SDRAM運(yùn)行的意義。是為了獲得更快的代碼運(yùn)行速度?但是我也不確定程序在內(nèi)部Flash運(yùn)行更快還是SDRAM運(yùn)行更快?

可能唯一的意義可以就是可以學(xué)習(xí)到代碼重定位相關(guān)的知識(shí)了吧......

廢話少說(shuō),程序它為什么可以自己復(fù)制自己?

我們前面介紹過(guò),程序運(yùn)行應(yīng)該位于它的鏈接地址上,但是當(dāng)這個(gè)程序的所有代碼都是位置無(wú)關(guān)碼的時(shí)候,它就可以不在鏈接地址上運(yùn)行。位置無(wú)關(guān)碼就是這段代碼可以在任何地址上正常運(yùn)行,它與執(zhí)行的位置無(wú)關(guān)。

位置無(wú)關(guān)碼編寫(xiě)要求:

匯編指令,不能使用絕對(duì)跳轉(zhuǎn)。比如對(duì)PC指針賦值跳轉(zhuǎn),或者使用跳轉(zhuǎn)指令,跳轉(zhuǎn)到某個(gè)地址值。比如下面這些就是絕對(duì)跳轉(zhuǎn)

; 下面這種跳轉(zhuǎn)方式就是絕對(duì)跳轉(zhuǎn)

ldr PC, =main

LDR R0, =SystemInit

BLX R0

; BL指令是相對(duì)跳轉(zhuǎn)指令

BL main

C言語(yǔ),不能使用函數(shù)指針調(diào)用函數(shù)。因?yàn)楹瘮?shù)指針調(diào)用方式就是指向一個(gè)確定的地址值,而這個(gè)地址是鏈接時(shí)分配的地址,代碼沒(méi)有在鏈接地址處運(yùn)行的時(shí)候,跳轉(zhuǎn)過(guò)去程序只能崩潰

對(duì)于訪問(wèn)數(shù)據(jù)的話,不要去訪問(wèn)全局變量、靜態(tài)局部變量、字符串。

我們把應(yīng)用程序燒寫(xiě)到MCU內(nèi)部的Flash時(shí),可以在應(yīng)用程序的最前面一部分代碼, 放置重定位相關(guān)的代碼,這重定位相關(guān)的代碼編寫(xiě)要求就是必須使用位置無(wú)關(guān)碼編寫(xiě)。

因?yàn)楫?dāng)我們要把程序放到SDRAM運(yùn)行時(shí),就需要修改程序的鏈接地址指向SDRAM的內(nèi)存空間,而MCU剛上電,是從Flash的空間開(kāi)始取指令運(yùn)行的,所以Flash最開(kāi)始的那一部分代碼必須是位置無(wú)關(guān)碼,否則就無(wú)法正常運(yùn)行。

4.2 Bootloader復(fù)制應(yīng)用程序

當(dāng)應(yīng)用程序太大,無(wú)法燒錄到內(nèi)部Flash時(shí),就只能燒寫(xiě)到外部Flash。

這個(gè)時(shí)候,就可以編寫(xiě)一個(gè)簡(jiǎn)單的程序(當(dāng)然你也可以做得很復(fù)雜,做到適配各種外部存儲(chǔ)設(shè)備,各種協(xié)議什么的),它的主要作用就是復(fù)制應(yīng)用程序到鏈接地址處運(yùn)行。

然后把這個(gè)程序燒寫(xiě)到內(nèi)部的Flash上,MCU上電,可以先運(yùn)行這段代碼,接著把外部的應(yīng)用程序復(fù)制到內(nèi)存(SDRAM)運(yùn)行,復(fù)制完成之后再跳轉(zhuǎn)到內(nèi)存運(yùn)行即可。這個(gè)程序通常被叫做Bootloader。

嵌入式Linux設(shè)備就是使用這種方式啟動(dòng)的,當(dāng)然啟動(dòng)過(guò)程比這里說(shuō)的還要復(fù)雜,但是總體啟動(dòng)過(guò)程類(lèi)似。

5. 代碼重定位到SDRAM運(yùn)行的過(guò)程

前面講了很多內(nèi)容,大家可以不用看,只看最后這章,看看代碼怎么寫(xiě)就行了。重定位的代碼其實(shí)也很簡(jiǎn)單,本質(zhì)就是復(fù)制數(shù)據(jù),而復(fù)制數(shù)據(jù)調(diào)用一個(gè)memcpy函數(shù)就足夠了。

上面介紹了兩種代碼重定位到SDRAM運(yùn)行的方法,下面我只講第一種方法。其實(shí)大家也可以把前面這部分重定位的代碼看作是Bootloader程序,只不過(guò)它比較簡(jiǎn)單,和應(yīng)用程序鏈接在一起了。

重定位的這部分代碼編寫(xiě)流程:

初始化系統(tǒng)時(shí)鐘

初始化SDRAM,因?yàn)樵L問(wèn)SDRAM需要設(shè)置SDRAM的時(shí)序參數(shù)

.text段重定位

.data段重定位

.bss/zi段清零

重新設(shè)置中斷向量表寄存器的基地址

絕對(duì)跳轉(zhuǎn)到SDRAM運(yùn)行

然后我的開(kāi)發(fā)板使用的是APM32F407ZG型號(hào),下面的代碼是適配這個(gè)型號(hào)的。當(dāng)然APM32系列的其他型號(hào),重定位的思路方法都是一樣的,參考著來(lái)就行。

5.1 修改散列文件

我們的目的是要把程序放到SDRAM運(yùn)行,所以我們必須修改鏈接地址在SDRAM的內(nèi)存空間,讓鏈接器根據(jù)這段內(nèi)存空間分配地址。而修改鏈接地址,對(duì)于Keil來(lái)說(shuō)就要修改散列文件。

我使用的芯片型號(hào)是APM32F407ZG,而且外面接的SDRAM的起始地址是0x60000000,大小一共是2MB。所以修改出來(lái)的散列文件如下:

LR_IROM1 0x08000000 0x00100000 { ; load region size_region

ER_IROM1 0x60000000 0x00200000 { ; load address = execution address

*.o (RESET, +First)

;*(InRoot$Sections)

.ANY (+RO)

.ANY (+XO)

}

RW_IRAM1 0x20000000 0x00020000 { ; RW data

.ANY (+RW +ZI)

}

ARM_LIB_HEAP +0 EMPTY 0x0200 { ; Heap region growing up

}

ARM_LIB_STACK +0 EMPTY 0x0400 { ; Stack region growing down

}

}

加載域的地址不變,因?yàn)槲覀冞€是要下載到Flash中存儲(chǔ)程序的。但是執(zhí)行域修改為了SDRAM的起始地址和大小。然后可讀可寫(xiě)的數(shù)據(jù)域地址和大小沒(méi)改,依舊是使用MCU內(nèi)部的SRAM作為數(shù)據(jù)存儲(chǔ)區(qū)。

但是下面我新增了兩個(gè)執(zhí)行域,那就是堆和棧,這兩個(gè)域的地址是緊接著 RW_IRAM1域進(jìn)行編排地址的,大小分別是 0x200 和 0x400。為什么要加上這兩個(gè)域下面講解。

5.2 初始化系統(tǒng)時(shí)鐘和SDRAM

初始化時(shí)鐘相關(guān)的代碼,直接調(diào)用APM32 SDK提供的SystemInit函數(shù)即可,但是要更改一下調(diào)用方式為相對(duì)跳轉(zhuǎn)。

我們是要把代碼從MCU內(nèi)部的Flash復(fù)制到SDRAM運(yùn)行,那么復(fù)制之前,必須能正確的讀寫(xiě)SDRAM才能正常復(fù)制,而SDRAM的讀寫(xiě)需要先初始化它的時(shí)序參數(shù)才行。

我的板子使用的SDRAM型號(hào)是:EM638165TS-7IG,然后關(guān)于這個(gè)SDRAM型號(hào)的初始化代碼,可以從官方的 APM32F4xx_EVAL_SDK_V1.0 這個(gè)SDK中,稍微修改下就可以拿過(guò)來(lái)使用了。

然后我們?cè)趨R編代碼的 Reset_Handler 函數(shù)(標(biāo)號(hào))中調(diào)用這幾個(gè)函數(shù):

BL SystemInit

BL SDRAM_GPIOConfig

BL SDRAM_Init

5.3 .text段重定位

各個(gè)段的重定位,就是數(shù)據(jù)的復(fù)制。我們必須要知道,源、目的、長(zhǎng)度。而這些信息,都在散列文件中獲取到。前面花了很大篇幅講了散列文件作用就在這里。

代碼如下:

IMPORT |Image$ER_IROM1$Base|

IMPORT |Image$ER_IROM1$Length|

IMPORT |Load$ER_IROM1$Base|

; relocate text section

LDR R0, = |Image$ER_IROM1$Base| ; destination

LDR R1, = |Load$ER_IROM1$Base| ; source

LDR R2, = |Image$ER_IROM1$Length| ; lenth

BL mymemcpy

ER_IROM1 就段名,這個(gè)段就是 .text 代碼段的意思。

|Load$$ER_IROM1$$Base| :.text段加載地址,就是復(fù)制數(shù)據(jù)的源地址

|Image$$ER_IROM1$$Base| :.text段執(zhí)行域地址,也就是鏈接地址,就是復(fù)制數(shù)據(jù)的目的地址

|Image$$ER_IROM1$$Length| :.text段的長(zhǎng)度

我們通過(guò)上面那幾個(gè)奇奇怪怪的符號(hào),就可以獲得.text段的加載地址,鏈接地址,和長(zhǎng)度了。知道這些信息,然后復(fù)制數(shù)據(jù)調(diào)用一個(gè)memcpy就可以把Flash的.text段復(fù)制到SDRAM了。

5.4 .data段重定位

.data段,存放的就是各種初值不是0的全局變量、靜態(tài)局部變量,我們也需要把這部分?jǐn)?shù)據(jù)的初始值,從Flash空間中復(fù)制到對(duì)應(yīng)的內(nèi)存中去。

我們?cè)谏⒘形募?,?shù)據(jù)段的鏈接地址是設(shè)置在0x20000000地址處的。

重定位代碼如下:

IMPORT |Image$RW_IRAM1$Base|

IMPORT |Image$RW_IRAM1$Length|

IMPORT |Load$RW_IRAM1$Base|

; relocate data section

LDR R0, = |Image$RW_IRAM1$Base| ; destination

LDR R1, = |Load$RW_IRAM1$Base| ; source

LDR R2, = |Image$RW_IRAM1$Length| ; lenth

BL mymemcpy

代碼基本和.text段重定位差不多的,就是那幾個(gè)符號(hào)不一樣,是獲取.data段的加載地址、鏈接地址和長(zhǎng)度的符號(hào)。

5.5 .bss/zi段清零

.bss/zi段 是初始值為0的全局變量和靜態(tài)局部變量存放的地址,但是這個(gè)段的內(nèi)容不會(huì)存放在Flash中,因?yàn)槌踔禐?,我們?cè)谑褂弥鞍堰@個(gè)段的內(nèi)存空間清0即可,無(wú)需浪費(fèi)空間把編譯進(jìn)bin文件里面。

IMPORT |Image$RW_IRAM1$ZI$Base|

IMPORT |Image$RW_IRAM1$ZI$Length|

; clear bss/zi

LDR R0, = |Image$RW_IRAM1$ZI$Base| ; destination

MOV R1, #0 ; Value

LDR R2, = |Image$RW_IRAM1$ZI$Length| ; lenth

BL bss_section_clear

|Image$$RW_IRAM1$$ZI$$Base| : 是bss/zi段的鏈接地址

|Image$$RW_IRAM1$$ZI$$Length| : bss/zi段的長(zhǎng)度

我們調(diào)用一個(gè)memset函數(shù)就可以把對(duì)應(yīng)的內(nèi)存段清0了。

5.6 重新設(shè)置中斷向量表寄存器基地址

中斷向量表的基地址默認(rèn)是0x08000000處的,當(dāng)中斷發(fā)生時(shí),MCU會(huì)自動(dòng)到這個(gè)地址處找到對(duì)應(yīng)的中斷處理函數(shù),然后跳轉(zhuǎn)過(guò)去。

但是我們需要把代碼搬運(yùn)到了SDRAM運(yùn)行,如果還想要正常使用中斷處理函數(shù)的話,就必須修改中斷向量表的基地址到鏈接的起始地址,也就是0x60000000。

修改方法也很簡(jiǎn)單,SCB->VTOR 修改這個(gè)寄存器的值就行了,這個(gè)寄存器是內(nèi)核相關(guān)的寄存器,它的地址是0xE000ED08.

代碼如下:

; set interrupt vector base address to 0x60000000

ldr r0, =__Vectors

ldr r1, =0xE000ED08 ; SCB->VTOR register address is 0xE000ED08

str r0, [r1]

ldr指令,把__Vectors的鏈接地址(其實(shí)就是0x60000000)加載到r0寄存器中,然后再使用str指令,把這個(gè)值寫(xiě)到0xE000ED08地址處。

5.7 一些問(wèn)題

做完前面的過(guò)程,基本就完成了把代碼重定位到SDRAM了,這個(gè)時(shí)候就可以使用絕對(duì)跳轉(zhuǎn)指令,跳轉(zhuǎn)到用戶(hù)應(yīng)用程序運(yùn)行代碼了。

LDR R0, =mymain

BX R0

當(dāng)執(zhí)行了上面兩條指令,那么就已經(jīng)是跳轉(zhuǎn)到SDRAM運(yùn)行代碼了,因?yàn)閙ymain的函數(shù)鏈接地址,就是位于SDRAM那邊的。

但是我在這個(gè)過(guò)程中遇到了一些問(wèn)題。

5.7.1 Reset_Handler 中斷問(wèn)題

9f38434c-b654-11f0-8c8f-92fbcf53809c.png

file://E:/%E5%8D%9A%E5%AE%A2%E6%96%87%E7%AB%A0/picture/image-20230808233404427.png?lastModify=1691512138

在中斷向量表的第二個(gè)位置,會(huì)放這個(gè) Reset_Handler 的函數(shù)在那里。這個(gè)是MCU上電的時(shí)候,就會(huì)從這個(gè)位置,然后把這個(gè) Reset_Handler 的函數(shù)地址賦值給PC指針,然后讓MCU從 Reset_Handler 開(kāi)始不斷的執(zhí)行代碼。

程序被燒錄到內(nèi)部的Flash中,然后0x08000004地址開(kāi)始存放的,就是 Reset_Handler 的地址了。

但是問(wèn)題是 Reset_Handler 這個(gè)函數(shù)名,鏈接器是使用鏈接地址給它分配地址值的,也就是它的地址是 0x60000000 開(kāi)始的某個(gè)地址。然后MCU上電,就把這個(gè) 0x60000000 開(kāi)始的某個(gè)地址,賦值給了PC指針,這個(gè)時(shí)候會(huì)怎么樣?

因?yàn)檫@時(shí)還沒(méi)有把代碼復(fù)制到SDRAM,那MCU從那里取不到正確的指令,只能是無(wú)法執(zhí)行下去。

所以我們不能使用 Reset_Handler 函數(shù)名放在那個(gè)位置,而是要把 0x08000000 開(kāi)始的某個(gè)地址放在那里,因?yàn)槲覀儼殉绦驘浀搅?0x08000000 處。

然后這個(gè)數(shù)值怎么得到?通過(guò)反匯編文件。

9f991708-b654-11f0-8c8f-92fbcf53809c.png

file://E:/%E5%8D%9A%E5%AE%A2%E6%96%87%E7%AB%A0/picture/image-20230808234433096.png?lastModify=1691512138

這里的 +1 是因?yàn)锳RM公司有兩種指令集,ARM 指令集和 Thumb 指令集,其中指令的 bit0 位為1,那就代表是Thumb指令。而Cortex-M3/M4 內(nèi)核使用的就是Thumb指令集,所以會(huì) +1.

所以才會(huì)看到中斷向量表的第二個(gè)位置放了個(gè)奇怪的數(shù)據(jù): 0x080003f5,就是這么來(lái)的。

5.7.2 清除bss/zi段時(shí)把堆棧也給清除了

在運(yùn)行bss段清0的代碼時(shí),我發(fā)現(xiàn)代碼就死掉了。通過(guò) .map 文件分析,發(fā)現(xiàn)Keil把堆和棧的大小,也歸類(lèi)為了bss里面了,這可能是 .s 文件定義堆棧的方式導(dǎo)致的,我沒(méi)有去深究。

如果把堆棧都?xì)w類(lèi)了bss段了,那么清除bss段時(shí),就把堆棧給干掉了,堆可能關(guān)系還不大,因?yàn)檫€沒(méi)有用到堆內(nèi)存。但是把棧清0了,肯定不行,因我們前面的代碼有C語(yǔ)言寫(xiě)的代碼,C函數(shù)的調(diào)用,局部變量都用到了棧。

解決方式其實(shí)很簡(jiǎn)單,既然把堆棧都?xì)w類(lèi)為bss段,那么我們想辦法不讓Keil把堆棧歸類(lèi)到bss段即可。

我是直接在散列文件那里定義堆棧的大小了,然后通過(guò)鏈接器的符號(hào):|Image$$ARM_LIB_STACK$$ZI$$Limit|獲取到棧頂指針的地址值,然后在中斷向量表的第一個(gè)位置填這個(gè)符號(hào)即可。

a000f29c-b654-11f0-8c8f-92fbcf53809c.png

file://E:/%E5%8D%9A%E5%AE%A2%E6%96%87%E7%AB%A0/picture/image-20230809000650638.png?lastModify=1691512138

當(dāng)然,不使用散列文件的話,也有很多其他取巧的解決辦法,比如:

調(diào)用 bss 段清0函數(shù)之前,重新設(shè)置 SP 的值

bss 段清0函數(shù)內(nèi)部,長(zhǎng)度信息減去堆棧的空間長(zhǎng)度

以上就是程序自己復(fù)制自己,把自己重定位到SDRAM運(yùn)行的介紹。

整個(gè)demo程序也上傳這里了,以供大家學(xué)習(xí)參考。

注:文章作者在原帖中提供了例程文件,有需要請(qǐng)至原文21ic論壇下載

原文地址:https://bbs.21ic.com/icview-3319862-1-1.html

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • mcu
    mcu
    +關(guān)注

    關(guān)注

    147

    文章

    18669

    瀏覽量

    388884
  • SDRAM
    +關(guān)注

    關(guān)注

    7

    文章

    449

    瀏覽量

    57316
  • 寄存器
    +關(guān)注

    關(guān)注

    31

    文章

    5590

    瀏覽量

    129248
  • FlaSh
    +關(guān)注

    關(guān)注

    10

    文章

    1720

    瀏覽量

    154828
  • 引腳
    +關(guān)注

    關(guān)注

    16

    文章

    2099

    瀏覽量

    55246

原文標(biāo)題:APM32芯得 EP.64 | APM32代碼重定位--如何讓整個(gè)程序在SDRAM運(yùn)行

文章出處:【微信號(hào):geehysemi,微信公眾號(hào):Geehy極海半導(dǎo)體】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    新品發(fā)布丨半導(dǎo)體推出工業(yè)級(jí)標(biāo)準(zhǔn)型APM32S103系列MCU

    半導(dǎo)體最新宣布推出,基于32位Arm Cortex-M3內(nèi)核的 工業(yè)級(jí)標(biāo)準(zhǔn)型 APM32S103系列MCU 。該
    的頭像 發(fā)表于 12-16 18:24 ?2848次閱讀

    推出APM32A系列車(chē)規(guī)級(jí)MCU芯片

    中量產(chǎn)驗(yàn)證。下一代符合ISO 26262功能安全標(biāo)準(zhǔn)的G32A系列車(chē)規(guī)級(jí)MCU規(guī)劃。審
    發(fā)表于 02-21 14:21

    APM32工業(yè)級(jí)硬核抗寒體質(zhì),無(wú)懼-40℃寒潮!

    APM32系列MCU,工作溫度覆蓋-40℃~+105℃,ESD等級(jí)高達(dá)8KV,具有低功耗、高性能、安全可靠、可移植性好、客戶(hù)接受程度高
    發(fā)表于 01-14 17:13 ?3044次閱讀

    半導(dǎo)體新品上市—工業(yè)增強(qiáng)型APM32F091xC系列MCU

    半導(dǎo)體工業(yè)增強(qiáng)型APM32F091xC系列新品MCU,其
    發(fā)表于 07-26 14:27 ?7087次閱讀
    <b class='flag-5'>極</b><b class='flag-5'>海</b>半導(dǎo)體新品上市—工業(yè)增強(qiáng)型<b class='flag-5'>APM</b>32F091xC<b class='flag-5'>系列</b><b class='flag-5'>MCU</b>

    基于APM32 MCU的電動(dòng)車(chē)BMS及電機(jī)控制應(yīng)用方案

    APM32系列工業(yè)級(jí)通用MCU,低功耗、高性能、高集成、易于移植、支持96位唯一設(shè)備ID(UID),ESD高達(dá)8KV,符合工業(yè)級(jí)可靠性標(biāo)
    發(fā)表于 02-08 17:01 ?17次下載
    基于<b class='flag-5'>APM32</b> <b class='flag-5'>MCU</b>的電動(dòng)車(chē)BMS及電機(jī)控制應(yīng)用方案

    嵌入式開(kāi)發(fā)工具服務(wù)商IAR Systems工具鏈全面支持半導(dǎo)體APM32系列MCU

    嵌入式開(kāi)發(fā)工具服務(wù)商IAR Systems工具鏈全面支持半導(dǎo)體APM32系列MCU. IAR Embedded Workbench fo
    發(fā)表于 07-13 17:08 ?2273次閱讀
    嵌入式開(kāi)發(fā)工具服務(wù)商IAR Systems工具鏈全面支持<b class='flag-5'>極</b><b class='flag-5'>海</b>半導(dǎo)體<b class='flag-5'>APM32</b><b class='flag-5'>系列</b><b class='flag-5'>MCU</b>

    推出基于Arm Cortex-M4內(nèi)核的高性能、高安全APM32F405/415工業(yè)級(jí)MCU新品

    半導(dǎo)體注重并持續(xù)在產(chǎn)品需求、產(chǎn)品定義、迭代升級(jí)、應(yīng)用方面與各相關(guān)廠商加深合作,目前APM32系列工業(yè)級(jí)MCU
    的頭像 發(fā)表于 08-11 10:39 ?2269次閱讀

    Flasher在線燒錄器全面支持APM32系列MCU

    半導(dǎo)體常務(wù)副總經(jīng)理王遠(yuǎn)學(xué)表示:“非常有幸能與SEGGER達(dá)成合作,目前APM32系列
    的頭像 發(fā)表于 09-08 11:10 ?3027次閱讀

    APM32系列工業(yè)級(jí)MCU GW88系列低功耗藍(lán)牙芯片選型

    APM32系列工業(yè)級(jí)MCU GW88系列低功耗藍(lán)牙芯片選型表免費(fèi)下載。需要樣品可以留言下載,15994789587
    發(fā)表于 12-13 11:51 ?12次下載

    半導(dǎo)體32位APM32工業(yè)級(jí)MCU工控領(lǐng)域的出色表現(xiàn)

    工業(yè)級(jí)MCU應(yīng)用場(chǎng)景范圍十分廣泛,并對(duì)使用壽命、溫度、濕度、電磁輻射等有著嚴(yán)格的品質(zhì)要求。半導(dǎo)體長(zhǎng)期深耕中高端工控市場(chǎng),本期就以絕對(duì)值編碼器、高性能伺服驅(qū)動(dòng)器及變頻器方案為例,詳細(xì)介紹
    發(fā)表于 11-03 17:14 ?3984次閱讀

    喜報(bào)頻傳!APM32工業(yè)級(jí)/車(chē)規(guī)級(jí)MCU產(chǎn)品接連榮獲三項(xiàng)大獎(jiǎng)

    半導(dǎo)體喜報(bào)頻傳接連榮獲三項(xiàng)大獎(jiǎng)。半導(dǎo)體“工業(yè)級(jí)高安全MCU APM32F415”、 “工
    發(fā)表于 11-21 15:17 ?1000次閱讀

    PEmicro全面支持APM32MCU

    高性能、高集成度、低功耗APM32工業(yè)級(jí)/車(chē)規(guī)級(jí)MCU,基于Arm Cortex-M0+/M3/M4內(nèi)核,擁有強(qiáng)大運(yùn)算性能和增強(qiáng)型存儲(chǔ)空間,具有豐富的協(xié)處理功能和廣泛的外設(shè)資源。
    發(fā)表于 01-13 11:37 ?803次閱讀

    推出首款電機(jī)控制專(zhuān)用芯片APM32F035系列MCU

    宣布正式推出首款高性能、高可靠性、高性?xún)r(jià)比的電機(jī)控制專(zhuān)用芯片—APM32F035系列MCU,覆蓋多種電機(jī)應(yīng)用。
    的頭像 發(fā)表于 07-28 17:13 ?2511次閱讀
    <b class='flag-5'>極</b><b class='flag-5'>海</b>推出首款電機(jī)控制專(zhuān)用芯片<b class='flag-5'>APM</b>32F035<b class='flag-5'>系列</b><b class='flag-5'>MCU</b>

    APM32 MCU助力推動(dòng)新型工業(yè)化發(fā)展

    國(guó)產(chǎn)APM32 MCU助力推動(dòng)新型工業(yè)化發(fā)展
    的頭像 發(fā)表于 09-28 17:38 ?1482次閱讀
    <b class='flag-5'>APM32</b> <b class='flag-5'>MCU</b>助力推動(dòng)新型工業(yè)化發(fā)展

    基于半導(dǎo)體APM32F407系列MCU的伺服控制器應(yīng)用方案

    基于半導(dǎo)體APM32F407系列MCU的伺服控制器應(yīng)用方案
    的頭像 發(fā)表于 09-19 16:48 ?2059次閱讀
    基于<b class='flag-5'>極</b><b class='flag-5'>海</b>半導(dǎo)體<b class='flag-5'>APM</b>32F407<b class='flag-5'>系列</b><b class='flag-5'>MCU</b>的伺服控制器應(yīng)用方案