日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當(dāng)前位置:首頁 > 單片機 > 單片機
[導(dǎo)讀]一些工控產(chǎn)品,當(dāng)系統(tǒng)復(fù)位后(非上電復(fù)位),可能要求保持住復(fù)位前RAM中的數(shù)據(jù),用來快速恢復(fù)現(xiàn)場,或者不至于因瞬間復(fù)位而重啟現(xiàn)場設(shè)備。而keil mdk在默認情況下,任何形式的復(fù)位都會將RAM區(qū)的非初始化變量數(shù)據(jù)清零

一些工控產(chǎn)品,當(dāng)系統(tǒng)復(fù)位后(非上電復(fù)位),可能要求保持住復(fù)位前RAM中的數(shù)據(jù),用來快速恢復(fù)現(xiàn)場,或者不至于因瞬間復(fù)位而重啟現(xiàn)場設(shè)備。而keil mdk在默認情況下,任何形式的復(fù)位都會將RAM區(qū)的非初始化變量數(shù)據(jù)清零。如何設(shè)置非初始化數(shù)據(jù)變量不被零初始化,這是本篇文章所要探討的。


在給出方法之前,先來了解一下代碼和數(shù)據(jù)的存放規(guī)則、屬性,以及復(fù)位后為何默認非初始化變量所在RAM都被初始化為零了呢。


什么是初始化數(shù)據(jù)變量,什么又是非初始化數(shù)據(jù)變量?(因為我的文字描述不一定準確,所以喜歡舉一些例子來輔助理解文字。)


定義一個變量:int nTimerCount=20;變量nTimerCount就是初始化變量,也就是已經(jīng)有初值;


如果定義變量:int nTimerCount;變量nTimerCount就是一個非賦值的變量,Keil MDK默認將它放到屬性為ZI的輸入節(jié)。


那么,什么是“ZI”,什么又是“輸入節(jié)”呢?這要了解一下ARM映像文件(image)的組成了,這部分內(nèi)容略顯無聊,但我認為這是非常有必要掌握的。


ARM映像文件的組成:


一個映像文件由一個或多個域(region,也有譯為“區(qū)”)組成

每個域包含一個或多個輸出段(section,也有譯為“節(jié)”)

每個輸出段包含一個或多個輸入段

各個輸入段包含了目標(biāo)文件中的代碼和數(shù)據(jù)


輸入段中包含了四類內(nèi)容:代碼、已經(jīng)初始化的數(shù)據(jù)、未經(jīng)過初始化的存儲區(qū)域、內(nèi)容初始化為零的存儲區(qū)域。每個輸入段有相應(yīng)的屬性:只讀的(RO)、可讀寫的(RW)以及初始化成零的(ZI)。


一個輸出段中包含了一些列具有相同的RO、RW和ZI屬性的輸入段。輸出段屬性與其中包含的輸入段屬性相同。


一個域包含一到三個輸出段,各個輸出段的屬性各不相同:RO屬性、RW屬性和ZI屬性


到這里我們就可以知道,一般情況下,代碼會被放到RO屬性的輸入節(jié),已經(jīng)初始化的變量會被分配到RW屬性輸入?yún)^(qū),而“ZI”屬性輸入節(jié)可以理解為是初始化成零變量的集合。


已經(jīng)初始化變量的初值,會被放到硬件的哪里呢?(比如定義int nTimerCount=20;那么初始值20被放到哪里呢?),我覺得這是個有趣的問題,比如keil在編譯完成后,會給出編譯文件大小的信息,如下所示:


Total RO Size (Code + RO Data) 54520 ( 53.24kB) Total RW Size (RW Data + ZI Data) 6088 ( 5.95kB) Total ROM Size (Code + RO Data + RW Data) 54696 ( 53.41kB)


很多人不知道這是怎么計算的,也不知道究竟放入ROM/Flash中的代碼有多少。其實,那些已經(jīng)初始化的變量,是被放入RW屬性的輸入節(jié)中,而這些變量的初值,是被放入ROM/Flash中的。有時候這些初值的量比較大,Keil還會將這些初值壓縮后再放入ROM/Flash以節(jié)省存儲空間。那這些初值是誰在何時將它們恢復(fù)到RAM中的?ZI屬性輸入節(jié)中的變量所在RAM又是誰在何時給用零初始化的呢?要了解這些東西,就要看默認設(shè)置下,從系統(tǒng)復(fù)位,到執(zhí)行C代碼中你編寫的main函數(shù),Keil幫你做了些什么。


硬件復(fù)位后,第一步是執(zhí)行復(fù)位處理程序,這個程序的入口在啟動代碼里(默認),摘錄一段cortex-m3的復(fù)位處理入口代碼:


1: Reset_Handler PROC ;PROC等同于FUNCTION,表示一個函數(shù)的開始,與ENDP相對?

2:

3: EXPORT Reset_Handler [WEAK]

4: IMPORT SystemInit

5: IMPORT __main

6: LDR R0, =SystemInit

7: BLX R0

8: LDR R0, =__main

9: BX R0

10: ENDP

初始化堆棧指針、執(zhí)行完用戶定義的底層初始化代碼(SystemInit函數(shù))后,接下來的代碼調(diào)用了__main函數(shù),這里__main函數(shù)會調(diào)用一些列的C庫函數(shù),完成代碼和數(shù)據(jù)的復(fù)制、解壓縮以及ZI數(shù)據(jù)的零初始化。數(shù)據(jù)的解壓縮和復(fù)制,其中就包括將儲存在ROM/Flash中的已初始化變量的初值復(fù)制到相應(yīng)的RAM中去。對于一個變量,它可能有三種屬性,用const修飾符修飾的變量最可能放在RO屬性區(qū),已經(jīng)初始化的變量會放在RW屬性區(qū),那么剩下的變量就要放到ZI屬性區(qū)了。默認情況下,ZI數(shù)據(jù)的零初始化會將所有ZI數(shù)據(jù)區(qū)初始化為零,這是每次復(fù)位后程序執(zhí)行C代碼的main函數(shù)之前,由編譯器“自作主張”完成的。所以我們要在C代碼中設(shè)置一些變量在復(fù)位后不被零初始化,那一定不能任由編譯器“胡作非為”,我們要用一些規(guī)則,約束一下編譯器。

分散加載文件對于連接器來說至關(guān)重要,在分散加載文件中,使用UNINIT來修飾一個執(zhí)行節(jié),可以避免__main對該區(qū)節(jié)的ZI數(shù)據(jù)進行零初始化。這是要解決非零初始化變量的關(guān)鍵。因此我們可以定義一個UNINIT修飾的數(shù)據(jù)節(jié),然后將希望非零初始化的變量放入這個區(qū)域中。于是,就有了第一種方法:


1. 修改分散加載文件,增加一個名為MYRAM的執(zhí)行節(jié),該執(zhí)行節(jié)起始地址為0x1000A000,長度為0x2000字節(jié)(8KB),由UNINIT修飾:


1: LR_IROM1 0x00000000 0x00080000 { ; load region size_region

2: ER_IROM1 0x00000000 0x00080000 { ; load address = execution address

3: *.o (RESET, +First)

4: *(InRoot$$Sections)

5: .ANY (+RO)

6: }

7: RW_IRAM1 0x10000000 0x0000A000 { ; RW data

8: .ANY (+RW +ZI)

9: }

10: MYRAM 0x1000A000 UNINIT 0x00002000 {

11: .ANY (NO_INIT)

12: }

13: }

那么,如果在程序中有一個數(shù)組,你不想讓它復(fù)位后零初始化,就可以這樣來定義變量:


unsigned char plc_eu_backup[PLC_EU_BACKUP_BUF/8] __attribute__((at(0x1000A000)));

變量屬性修飾符__attribute__((at(adder)))用來將變量強制定位到adder所在地址處。由于地址0x1000A000開始的8KB區(qū)域ZI變量不會被零初始化,所以處在這一區(qū)域的數(shù)組plc_eu_backup也就不會被零初始化了。


這種方法的缺點是顯而易見的:要自己分配變量的地址,如果非零初始化數(shù)據(jù)比較多,這將是件難以想象的大工程(以后的維護、增加、修改代碼等等)。所以要找到一種辦法,讓編譯器去自動分配這一區(qū)域的變量。


2. 分散加載文家同方法1,如果還是定義一個數(shù)組,可以用下面方法:


unsigned char plc_eu_backup[PLC_EU_BACKUP_BUF/8] __attribute__((section("NO_INIT"),zero_init));

變量屬性修飾符__attribute__((section(“name”),zero_init))用于將變量強制定義到name屬性數(shù)據(jù)節(jié)中,zero_init表示將未初始化的變量放到ZI數(shù)據(jù)節(jié)中。因為“NO_INIT”這顯性命名的自定義節(jié),具有UNINIT屬性。


3. 如何將一個模塊內(nèi)的非初始化變量都非零初始化?


假如該模塊名字為test.c,修改分散加載文件如下所示:


1: LR_IROM1 0x00000000 0x00080000 { ; load region size_region

2: ER_IROM1 0x00000000 0x00080000 { ; load address = execution address

3: *.o (RESET, +First)

4: *(InRoot$$Sections)

5: .ANY (+RO)

6: }

7: RW_IRAM1 0x10000000 0x0000A000 { ; RW data

8: .ANY (+RW +ZI)

9: }

10: RW_IRAM2 0x1000A000 UNINIT 0x00002000 {

11: test.o (+ZI)

12: }

13: }

定義時使用如下方法:


int uTimerCount __attribute__((zero_init));

這里,變量屬性修飾符__attribute__((zero_init))用于將未初始化的變量放到ZI數(shù)據(jù)節(jié)中變量,其實keil默認情況下,未初始化的變量就是放在ZI數(shù)據(jù)區(qū)的。


4.將整個程序的非初始化變量都非零初始化 看了上面的,這個已經(jīng)沒有必要說了。


本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

嵌入式開發(fā)作為一種專業(yè)且技術(shù)密集型的領(lǐng)域,涵蓋了從硬件底層驅(qū)動、中間件到應(yīng)用層軟件開發(fā)等多個層面的工作,其所需的工具種類繁多,各有針對性,旨在提升開發(fā)效率、保證代碼質(zhì)量以及簡化調(diào)試過程。

關(guān)鍵字: 嵌入式開發(fā) keil

之后新建新的工程,添加.a文件就可以使用了,當(dāng)然也可以使用keil來添加,但是keil默認的是用.lab,需要自己配置一下文件屬性,改為lib文件即可。一半release sdk的時候用這種方式很關(guān)鍵的,畢竟自己的核心代...

關(guān)鍵字: keil 文件屬性 lib文件

Keil公司是一家業(yè)界領(lǐng)先的微控制器(MCU)軟件開發(fā)工具的獨立供應(yīng)商。Keil公司由兩家私人公司聯(lián)合運營,分別是德國慕尼黑的Keil Elektronik GmbH和美國德克薩斯的Keil Software Inc。K...

關(guān)鍵字: mdk 安裝

mdk是什么意思_STM32是什么?應(yīng)用在哪里?都需要學(xué)什么?

關(guān)鍵字: mdk 編程

Keil、uVision、MDK、Keil C51之間的區(qū)別比較

關(guān)鍵字: keil c51 mdk

KEIL是公司的名稱,有時候也指KEIL公司的所有軟件開發(fā)工具,目前2005年Keil由ARM公司收購,成為ARM的公司之一。

關(guān)鍵字: keil5 mdk

DSP28335與AD7606通過SPI的串行數(shù)據(jù)交互

關(guān)鍵字: keil C

為幫助大家更好使用單片機編程軟件,本文將對keil單片機編程軟件仿真、調(diào)劑技巧予以講解,并對該單片機編程軟件的常見錯誤提示予以闡述。

關(guān)鍵字: 單片機編程軟件 指數(shù) keil

本文對于單片機編程軟件的介紹將基于如何采用keil單片機編程軟件調(diào)試stm32,如果你對單片機編程軟件抑或本文內(nèi)容具有興趣,不妨繼續(xù)往下閱讀哦。

關(guān)鍵字: 單片機編程軟件 指數(shù) keil
關(guān)閉