欧美极品高清xxxxhd,国产日产欧美最新,无码AV国产东京热AV无码,国产精品人与动性XXX,国产传媒亚洲综合一区二区,四库影院永久国产精品,毛片免费免费高清视频,福利所导航夜趣136

 找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開(kāi)始

搜索
查看: 13906|回復(fù): 5
打印 上一主題 下一主題
收起左側(cè)

STM32在MAIN函數(shù)之前做了什么事

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:241309 發(fā)表于 2017-10-20 15:55 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
在學(xué)習(xí)STM32的時(shí)候了解了在MAIN函數(shù)之前程序干了什么,其中有一些內(nèi)容是參考別人的。

main函數(shù)之前執(zhí)行的啟動(dòng)程序:
在執(zhí)行main函數(shù)之前還有一些引導(dǎo)的程序,以開(kāi)發(fā)板的型號(hào)為例,啟動(dòng)程序?yàn)閟tartup_stm32f10x_cl.s:
開(kāi)始的部分是聲明的內(nèi)容
【1】MODULE 控制指令是用來(lái)標(biāo)記 modules 源碼的開(kāi)始,后邊的 ?cstartup 是模塊的名字,此文檔的最后的 END 表明模塊的結(jié)束,與前面的MODULE對(duì)應(yīng)。
【2】SECTION 指令是聲明段,一個(gè)段不能同時(shí)包含 public symbol 和 pubweak symbol ,模塊只有在相同的名字的模塊沒(méi)有被鏈接進(jìn)來(lái)的時(shí)候才會(huì)被鏈接進(jìn)來(lái)。
    語(yǔ)法格式:SECTION section:type [flag] [(align)]
        align,是用于指定地址對(duì)齊到 2^align,他的取值是 0 到 30
        flag,取值NOROOT、ROOT、REORDER、NOREORDER,默認(rèn)是ROOT,NOROOT表示如果這個(gè)段中的符號(hào)沒(méi)有被引用,將會(huì)被連接器舍棄,即可被優(yōu)化。ROOT表示不可被優(yōu)化。REORDER表示開(kāi)始一個(gè)新的名字是 section 的段(section),NOREORDER表示開(kāi)始一個(gè)新的名字為 section 的片段(fragment),多個(gè)片段組成一個(gè)段(section)。
        type,memory 的類型,取值是 CODE、CONST、DATA
        section,段的名字
【3】EXTERN 用導(dǎo)入其他模塊的 symbol(符號(hào))。
【4】PUBLIC 導(dǎo)出 symbol(符號(hào))。
【1】DATA 表示下邊中的標(biāo)簽是 32 位的標(biāo)簽,THUMB 表示下邊的標(biāo)簽是 16 位的標(biāo)簽,所謂的標(biāo)簽是 地址的別名,不占用代碼空間,給編譯器看的。
【2】 DCD 是數(shù)據(jù)定義或者 重定位指令,為的是定義一個(gè)值,或者保留 memory,DCD 別名是 DC32,用于聲明一個(gè) 32 位的常量,這部分是中斷向量表的內(nèi)容,需要注意的是,他們的順序不能改變,此部分會(huì)放到 flash 的最開(kāi)始部分,當(dāng)系統(tǒng)啟動(dòng)的時(shí)候會(huì)加載前一個(gè)地址,第一個(gè)地址是 C 程序的棧的棧頂?shù)刂罚诙䝼(gè)地址是向量表的開(kāi)始地址,中斷發(fā)生時(shí)會(huì)根據(jù)向量表的首地址和偏移量來(lái)找到程序的入口。
【3】sfe 指令作用是返回棧的結(jié)尾,因?yàn)闂5脑鲩L(zhǎng)方向是反方向的。
【1】THUMB 表明下邊是 thumb 指令。
【2】Reset_Handler 在開(kāi)機(jī)或者復(fù)位的時(shí)候執(zhí)行
    R0 = SystemInit
    跳轉(zhuǎn)到 SystemInit 函數(shù),并將處理器切換到 thumb 狀態(tài)
    R0 = __iar_program_start
    跳轉(zhuǎn)到 __iar_program_start 函數(shù),狀態(tài)也是切換到 thumb 狀態(tài)
【3】此處的 __iar_program_start 在程序中找不到是因?yàn)樗呀?jīng)被封裝到了 IAR 自帶的C庫(kù)啟動(dòng)代碼中了,當(dāng)我們編譯的時(shí)候,在項(xiàng)目屬性的 linker,library中勾選了 Automatic runtime library ,就告訴了編譯器用庫(kù)中的 __iar_program_start ,具體實(shí)現(xiàn)了什么,我們可以查看 IAR 工具為我們提供的源碼,具體路徑在IAR 安裝目錄下的 arm\src\lib\thumb ,我們可以看到有的文件分別的提供了 匯編代碼和 c 代碼。
其中的 cmain.s 文件中有
可以看到在執(zhí)行過(guò)__iar_program_start函數(shù)之后就進(jìn)入了main函數(shù)。
分析啟動(dòng)過(guò)程:
當(dāng)STM32復(fù)位后,首先從地址0x0000_0000處取出棧頂?shù)刂贩湃隡SP(主堆棧指針)寄存器中(即寄存器R13)。接著從地址0x0000_0004處取出復(fù)位向量地址放入PC寄存器中(即寄存器R15),再接著程序就從復(fù)位處理函數(shù)處(Reset_Handler)開(kāi)始執(zhí)行。
對(duì)比兩張圖片可以發(fā)現(xiàn),R13所指向的地址與0x0000_0000地址的內(nèi)容相等;R15所指向的地址與0x0000_0004地址的內(nèi)容相等。
接下來(lái)我們分析Reset_Handler做了哪些事:
我們可以看到,只是執(zhí)行了兩個(gè)函數(shù)SystemInit和__iar_program_start,通過(guò)單步跟蹤發(fā)現(xiàn)在SystemInit里進(jìn)行清理中斷,時(shí)鐘初始化,F(xiàn)lash讀取初始化等操作。
而在__iar_program_start里實(shí)際上只執(zhí)行了__low_level_init和__iar_data_init2兩個(gè)函數(shù),因?yàn)開(kāi)_low_level_init里只是將R0賦0x1,所以再將R0和0x0進(jìn)行比較結(jié)果不相等(CMP R0, #0x0),__iar_init$$done這個(gè)函數(shù)沒(méi)有得到執(zhí)行。
系統(tǒng)復(fù)位時(shí),Cortex-M3從代碼區(qū)偏移0x0000'0000處獲取棧頂?shù)刂罚脕?lái)初始化MSP(主堆棧指針)寄存器的值。接下來(lái)從代碼區(qū)偏移0x0000'0004獲取第一個(gè)指令的跳轉(zhuǎn)地址。這些地址,是CM3要求放置中斷向量表的地方。
這里是一個(gè)程序的啟動(dòng)區(qū)的反匯編:
__vector_table:
  08004000  2600      
  08004002  2000      
  08004004  7E1D      (注解:下面的文字老是說(shuō) IAP 很奇怪 不是 IAR么)
  08004006  0800      (轉(zhuǎn)發(fā)人注解:我知道 IAP 意思是“在應(yīng)用編程”。)
這個(gè)程序是由IAP程序來(lái)啟動(dòng)的,IAP程序獲取0x0800'4000處的MSP值(0x20002600),并設(shè)置為MSP的值,即主堆棧最大范圍是0x2000'0000~0x2000'25FF。接下來(lái)IAP程序獲取0x0800'4004處的Reset_Handler的地址(0x0800'7E1D),并跳轉(zhuǎn)到Reset_Handler()執(zhí)行。
IAP在這里完全是模仿了Cortex-M3的復(fù)位序列,也就是說(shuō),在沒(méi)有IAP的系統(tǒng)上,CM3只能從0x0800'0000獲取MSP,從0x0800'0004獲取第一條指令所處地址。而IAP就存在在0x0800'0000這個(gè)地址上,IAP的啟動(dòng),已經(jīng)消耗掉了這個(gè)復(fù)位序列,所以IAP要啟動(dòng)UserApp程序的時(shí)候,也是完全模仿Cortex-M3的復(fù)位序列的。
接下來(lái)我們看看復(fù)位后第一句指令——Reset_Handler()函數(shù)里有什么。
若我們使用的是ST公司標(biāo)準(zhǔn)外設(shè)庫(kù),那么已經(jīng)有了現(xiàn)成的Reset_Handler,不過(guò)他是弱定義——PUBWEAK,可以被我們重寫(xiě)的同名函數(shù)覆蓋。一般來(lái)說(shuō),我們使用的都是ST提供的Reset_Handler,在V3.4版本的庫(kù)中,可以在startup_stm32f10x_xx.s中找到這個(gè)函數(shù):
        PUBWEAK Reset_Handler
        SECTION .text:CODE:REORDER(2)
Reset_Handler
        LDR     R0, =SystemInit
        BLX     R0
        LDR     R0, =__iar_program_start
        BX      R0
看來(lái)ST沒(méi)有做太多的事,他只調(diào)用了自家?guī)焯峁┑腟ystemInit函數(shù)進(jìn)行系統(tǒng)時(shí)鐘、Flash讀取的初始化,并把大權(quán)交給了__iar_program_start這個(gè)IAR提供的“內(nèi)部函數(shù)”了,我們就跟緊這個(gè)__iar_program_start跳轉(zhuǎn),看看IAR做了什么,上面一段代碼的反匯編如下:
       Reset_Handler:
__iar_section$$root:
  08007E1C  4801      LDR          R0, [PC, #0x4]; LDR     R0, =SystemInit
  08007E1E  4780      BLX          R0;BLX     R0
  08007E20  4801      LDR          R0, [PC, #0x4];LDR R0, =__iar_program_start
  08007E22  4700      BX           R0;BX      R0
  08007E24  6C69      
  08007E26  0800      
  08007E28  7D8D      
  08007E2A  0800     
細(xì)心的觀眾會(huì)發(fā)現(xiàn)地址是0x0800'7E1C,比我們查到的0x0800'7E1D差了1,這是ARM家族的遺留問(wèn)題,因?yàn)锳RM處理器的指令至少是半字對(duì)齊的(16位THUMB指令集 or 32位ARM指令集),所以PC指針的LSB是常為0的,為了充分利用寄存器,ARM公司給PC的LSB了一個(gè)重要的使命,那就是在執(zhí)行分支跳轉(zhuǎn)時(shí),PC的LSB=1,表示使用THUMB模式,LSB=0,表示使用ARM模式,但在最新的Cortex-M3內(nèi)核上,只使用了THUMB-2指令集挑大梁,所以這一位要常保持1,所以我們查到的地址是0x0800'7E1D(C=1100,D=1101),放心,我們的CM3內(nèi)核會(huì)忽略掉LSB(除非為0,那么會(huì)引起一個(gè)fault),從而正確跳轉(zhuǎn)到0x0800'7E1C。
從0x0800'7E20處的加載指令,我們可以算出__iar_program_start所處的位置,就是當(dāng)前PC指針(0x0800'7E24),再加上4,即0x0800'7E28處的所指向的地址——0x0800'7D8D(0x0800'7D8C),我們跟緊著跳轉(zhuǎn),__iar_program_start果然在這里:
__iar_program_start:
  08007D8C  F000F88C  BL           __low_level_init
  08007D90  2800      CMP          R0, #0x0
  08007D92  D001      BEQ          __iar_init$$done
  08007D94  F7FFFFDE  BL           __iar_data_init2
  08007D98  2000      MOVS         R0, #0x0
  08007D9A  F7FDFC49  BL          main
我們看到IAR提供了__low_level_init這個(gè)函數(shù)進(jìn)行了“底層”的初始化,進(jìn)一步跟蹤,我們可以查到__low_level_init這個(gè)函數(shù)做了些什么,是不是我們想象中的不可告人。
__low_level_init:
  08007EA8  2001      MOVS         R0, #0x1
  08007EAA  4770      BX           LR
__low_level_init出乎想象的簡(jiǎn)單,只是往R0寄存器寫(xiě)入了1,就立即執(zhí)行"BX LR"回到調(diào)用處了,接下來(lái),__iar_program_start檢查了R0是否為0,為0,則執(zhí)行__iar_init$$done,若不是0,就執(zhí)行__iar_data_init2。__iar_init$$done這個(gè)函數(shù)很簡(jiǎn)單,只有2句話,第一句是把R0清零,第二句就直接"BL main",跳轉(zhuǎn)到main()函數(shù)了。不過(guò)既然__low_level_init已經(jīng)往R0寫(xiě)入了1,那么我們還是得走下遠(yuǎn)路——看看__iar_data_init2做了些什么,雖然距離main只有一步之遙,不過(guò)這中間隱藏了編譯器的思想,我們得耐心看下去。
__iar_data_init2:
  08007D54  B510      PUSH         {R4,LR}
  08007D56  4804      LDR          R0, [PC, #0x10]
  08007D58  4C04      LDR          R4, [PC, #0x10]
  08007D5A  E002      B            0x8007D62
  08007D5C  F8501B04  LDR          R1, [R0], #0x4
  08007D60  4788      BLX          R1
  08007D62  42A0      CMP          R0, R4
  08007D64  D1FA      BNE          0x8007D5C
  08007D66  BD10      POP          {R4,PC}
  08007D68  7C78      
  08007D6A  0800      
  08007D6C  7C9C     
  08007D6E  0800     
看來(lái)IAR遲遲不執(zhí)行main()函數(shù),就是為了執(zhí)行__iar_data_init2,我們來(lái)分析分析IAR都干了些什么壞事~
首先壓R4,LR入棧,然后加載0x0800'7C78至R0,0x0800'7C9C至R4,馬上跳轉(zhuǎn)到0x0800'7D62執(zhí)行R0,R4的比較,結(jié)果若是相等,則彈出R4,PC,然后立即進(jìn)入main()。不過(guò)IAR請(qǐng)君入甕是自不會(huì)那么快放我們出來(lái)的——結(jié)果不相等,跳轉(zhuǎn)到0x0800'7D5C執(zhí)行,在這里,把R0指向的地址——0x0800'7C78中的值——0x0800'7D71加載到R1,并且R0中的值自加4,更新為0x0800'7C7C,并跳轉(zhuǎn)到R1指向的地址處執(zhí)行,這里是另一個(gè)IAR函數(shù):__iar_zero_init2:
__iar_zero_init2:
  08007D70  2300      MOVS         R3, #0x0
  08007D72  E005      B            0x8007D80
  08007D74  F8501B04  LDR          R1, [R0], #0x4
  08007D78  F8413B04  STR          R3, [R1], #0x4
  08007D7C  1F12      SUBS         R2, R2, #0x4
  08007D7E  D1FB      BNE          0x8007D78
  08007D80  F8502B04  LDR          R2, [R0], #0x4
  08007D84  2A00      CMP          R2, #0x0
  08007D86  D1F5      BNE          0x8007D74
  08007D88  4770      BX           LR
  08007D8A  0000      MOVS         R0, R0
__iar_data_init2還沒(méi)執(zhí)行完畢,就跳轉(zhuǎn)到了這個(gè)__iar_zero_inti2,且看我們慢慢分析這個(gè)幫兇——__iar_zero_inti2做了什么。
__iar_zero_inti2將R3寄存器清零,立即跳轉(zhuǎn)到0x0800'7D80執(zhí)行'LDR          R2, [R0], #0x4',這句指令與剛才在__iar_data_init2見(jiàn)到的'LDR          R1, [R0], #0x4'很類似,都為“后索引”。這回,將R0指向的地址——0x0800'7C7C中的值——0x0000'02F4加載到R2寄存器,然后R0中的值自加4,更新為0x0800'7C80。接下來(lái)的指令檢查了R2是否為0,顯然這個(gè)函數(shù)沒(méi)那么簡(jiǎn)單想放我我們,R2的值為2F4,我們又被帶到了0x0800'7D74處,隨后4條指令做了如下的事情:
1、將R0指向的地址——0x0800'7C80中的值——0x2000'27D4加載到R1寄存器,然后R0中的值自加4,更新為0x0800'7C84。
2、將R1指向的地址——0x2000'27D4中的值——改寫(xiě)為R3寄存器的值——0,然后R1中的值自加4,更新為0x2000'27D8。
3、R2自減4
4、檢查R2是否為0,不為0,跳轉(zhuǎn)到第二條執(zhí)行。不為,則執(zhí)行下一條。
這簡(jiǎn)直就是一個(gè)循環(huán)!——C語(yǔ)言的循環(huán)for(r2=0x2F4;r2-=4;r!=0){...},我們看看循環(huán)中做了什么。
第一條指令把一個(gè)地址加載到了R1——0x2000'27D4 是一個(gè)RAM地址,以這個(gè)為起點(diǎn),在循環(huán)中,對(duì)長(zhǎng)度為2F4的RAM空間進(jìn)行了清零的操作。那為什么IAR要做這個(gè)事情呢?消除什么記錄么?用Jlink查看這片內(nèi)存區(qū)域,可以發(fā)現(xiàn)這片區(qū)域是我們定義的全局變量的所在地。也就是說(shuō),IAR在每次系統(tǒng)復(fù)位后,都會(huì)自動(dòng)將我們定義的全局變量清零0。
清零完畢后,接下來(lái)的指令"LDR          R2, [R0], #0x4"將R0指向的地址——0x0800'7C84中的值——0加載到R2寄存器,然后R0中的值自加4,更新為0x0800'7C88。隨后檢查R2是否為0,這里R2為0,執(zhí)行'BX LR'返回到__iar_data_init2函數(shù),若是不為0,我們可以發(fā)現(xiàn)又會(huì)跳轉(zhuǎn)至“4指令”處進(jìn)行一個(gè)循環(huán)清零的操作。
讀到這里,我們應(yīng)該可以猜到IAR的意圖了:__iar_data_init2一開(kāi)始加載了0x0800'7C78至R0,0x0800'7C9C至R4,[R0,R4]就是一段啟動(dòng)代碼區(qū),在這個(gè)區(qū)域內(nèi)保存了要“處理”的所有地址與信息——執(zhí)行的函數(shù)地址或者參數(shù),實(shí)際上,這片區(qū)域也有一個(gè)名字,叫做:Region$$Table$$Base。在這個(gè)區(qū)域內(nèi),程序以R0為索引,R4為上限,當(dāng)R0=R4,__iar_data_init2執(zhí)行完畢,跳轉(zhuǎn)至main()函數(shù)。
好了,保持我們這個(gè)猜想,繼續(xù)跟蹤我們的PC指針——我們回到了__iar_data_init2函數(shù)中,第一件事就是比較R0,R4的值,可惜的是,仍然不相等,我們又被帶到了0x0800'7D5C,至此,我們應(yīng)該能看出這是一個(gè)__iar_data_init2的“主循環(huán)”,這也驗(yàn)證了我們對(duì)IAR意圖的猜想~
  __iar_data_init2中的“主循環(huán)”:
  08007D5C  F8501B04  LDR          R1, [R0], #0x4
  08007D60  4788      BLX          R1
  08007D62  42A0      CMP          R0, R4
我們可以等價(jià)寫(xiě)為:for(r0=0x0800'7C78,r4=0x0800'7C9C;r0!=r4;r0+=4){...}
此時(shí),我們的R0為0x0800'7C88,經(jīng)過(guò)“指令1”,R0變?yōu)?x0800'7C8C,R1為0x0800'7C55。我們來(lái)看看,7C55處,IAR又要執(zhí)行何種操作。
__iar_copy_init2:
  08007C54  B418      PUSH         {R3,R4}
  08007C56  E009      B            0x8007C6C
  08007C58  F8501B04  LDR          R1, [R0], #0x4
  08007C5C  F8502B04  LDR          R2, [R0], #0x4
  08007C60  F8514B04  LDR          R4, [R1], #0x4
  08007C64  F8424B04  STR          R4, [R2], #0x4
  08007C68  1F1B      SUBS         R3, R3, #0x4
  08007C6A  D1F9      BNE          0x8007C60
  08007C6C  F8503B04  LDR          R3, [R0], #0x4
  08007C70  2B00      CMP          R3, #0x0
  08007C72  D1F1      BNE          0x8007C58
  08007C74  BC12      POP          {R1,R4}
  08007C76  4770      BX           LR
這是一個(gè)名為_(kāi)_iar_copy_init2的函數(shù),他執(zhí)行了什么"copy"操作呢?
首先壓R3,R4入棧,然后跳轉(zhuǎn)到0x0800'7C6C,從R0——Region$$Table$$Base中取出參數(shù)0x238放入R3,接下來(lái)的指令大家應(yīng)該都熟悉了,0x238不為0,所以我們被帶至7C58處,再次從Region$$Table$$Base中取出參數(shù)0x0800'7F14放入R1,從Region$$Table$$Base取出參數(shù)0x2000'2AC8放入R2處。細(xì)心的觀眾應(yīng)該能察覺(jué)這和__iar_zero_init2中取參數(shù)的幾乎一樣:先取出大小,隨后取出了地址——只不過(guò)這里多出了1個(gè)地址,沒(méi)錯(cuò)這就是"copy",隨后的指令
  08007C60  F8514B04  LDR          R4, [R1], #0x4
  08007C64  F8424B04  STR          R4, [R2], #0x4
  08007C68  1F1B      SUBS         R3, R3, #0x4
  08007C6A  D1F9      BNE          0x8007C60
則是另一個(gè)“4指令”,指令1將R1指向地址的數(shù)據(jù)讀到R4,指令2將R2指向地址的數(shù)據(jù)改寫(xiě)為R4的數(shù)據(jù),指令3、4是完成一個(gè)循環(huán)。
說(shuō)到這里大家都應(yīng)該明白了——這就是一個(gè)"copy"的操作,從Flash地址0x0800'7F14起,將長(zhǎng)度0x238的數(shù)據(jù)拷貝到RAM地址0x2000'2AC8中。
通過(guò)Jlink,我們可以看到這片區(qū)域是我們定義的并且已初始化的全局變量。也就是說(shuō),每次復(fù)位后,IAR在此處進(jìn)行全局變量的初始化。
在這“4指令”執(zhí)行完畢后,再次從Region$$Table$$Base中取出參數(shù),為0,比較之后條件符合,函數(shù)返回__iar_data_init2。
此時(shí)的R0已經(jīng)為0x0800'7C9C與R4相等,__iar_data_init2終于完成它的使命。
  08007D98  2000      MOVS         R0, #0x0
  08007D9A  F7FDFC49  BL           main
將R0清零以后,IAR放棄主動(dòng)權(quán),把PC指針交給了用戶程序的入口——main()。
但請(qǐng)注意,這里使用的是BL指令進(jìn)行main跳轉(zhuǎn),也就是說(shuō),main函數(shù)只是IAR手中的一個(gè)子程序,若是main函數(shù)執(zhí)行到了結(jié)尾,接下來(lái)則會(huì)執(zhí)行exit等IAR提供的“退出”函數(shù)。這些函數(shù),等待下回分解~
總之,IAR在啟動(dòng)main()函數(shù)以前,執(zhí)行了Reset_Handler,調(diào)用SystemInit()(ST庫(kù)提供)進(jìn)行時(shí)鐘,F(xiàn)lash讀取初始化,并轉(zhuǎn)入__iar_program_start中執(zhí)行__low_level_init與__iar_data_init2,并在__iar_data_init2中,先后調(diào)用__iar_zero_init2與__iar_copy_init2對(duì)全局變量、全局已初始化變量進(jìn)行相應(yīng)的初始化操作。最后,調(diào)用main()函數(shù)執(zhí)行。
這就是IAR在啟動(dòng)main()函數(shù)之前做的事情,它并沒(méi)有那么神秘,只要花些時(shí)間,就可以跟跟蹤分析出這個(gè)過(guò)程。
STM32的啟動(dòng)分析
一、STM32的復(fù)位序列
當(dāng)STM32產(chǎn)生復(fù)位后,做的第一件事就是讀取下列兩個(gè)32位整數(shù)的值:
1、從地址0x0000,0000處取出MSP(主堆棧指針)的初始值放入MSP寄存器中;
2、從地址0x0000,0004處取出復(fù)位向量放入PC寄存器中,然后從PC中存取的地
址出取指并開(kāi)始執(zhí)行。
圖1:復(fù)位序列
請(qǐng)注意,這與傳統(tǒng)的ARM架構(gòu)以及其他的單片機(jī)完全不同,他們復(fù)位后一般是從
0x0000,0000地址處取出第一條指令并執(zhí)行,而一般0x0000,0000都是一條跳轉(zhuǎn)指令。而在STM32中,在0地址處提供的是MSP的初始值,然后緊跟著就是向量表(上電復(fù)位時(shí)向量表是被默認(rèn)放在0x04地址處,但是通過(guò)修改向量表偏移量寄存器(VTOR)可以將其定義在其他位置)。另外,向量表中的數(shù)值是32位的地址,而不是跳轉(zhuǎn)指令,系統(tǒng)會(huì)自動(dòng)將該數(shù)值存入PC寄存器中后從該32為地址指向的地址出開(kāi)始執(zhí)行,這有點(diǎn)像指針的指針。
圖2:初始化MSP及PC的初始化的一個(gè)范例因?yàn)镾MT32使用的是向下生長(zhǎng)的滿棧,
所以MSP初始值必須是堆棧內(nèi)存的末地址加1。舉例來(lái)說(shuō),如果你的堆棧區(qū)域在
0x20007C00-0x20007FFF之間,那么MSP的初始值就必須是0x20008000。 向量表跟隨在MSP的初始化之后——也就是第2個(gè)標(biāo)目。要注意因?yàn)镾TM32是在Thumb態(tài)下執(zhí)行,所以向量表中每個(gè)數(shù)值必須把LSB(最低權(quán)重位)置1.正是因?yàn)檫@個(gè)原因,圖2中就是用0x101來(lái)表示0x100.當(dāng)0x100處的指令得到執(zhí)行后,就正是開(kāi)始了程序的執(zhí)行。在這之前MSP是必須的,因?yàn)榭赡艿?條指令還沒(méi)來(lái)得及執(zhí)行,就發(fā)生了NMI(不可屏蔽中斷)或者其他的Fault,MSP初始化好后就已經(jīng)為他們的服務(wù)例程準(zhǔn)備好了堆棧。
二、STM323種啟動(dòng)模式
在STM32中,可以通過(guò)BOOT[1:0]引腳選擇三種不同的啟動(dòng)模式,如表1:
表1:STM32的三種啟動(dòng)模式 根據(jù)選定的啟動(dòng)模式,主閃存存儲(chǔ)器、系統(tǒng)存儲(chǔ)器或SRAM可以按照以下方式訪問(wèn):
1、 從主閃存存儲(chǔ)器啟動(dòng):
主閃存存儲(chǔ)器被映射到啟動(dòng)空間(0x0000,0000),但仍然能夠在原有的地址(0x8000,0000)訪問(wèn)它,即閃存存儲(chǔ)器的內(nèi)容可以在兩個(gè)地
址區(qū)域訪問(wèn),0x0000,0000或者0x8000,0000.
2、從系統(tǒng)存儲(chǔ)器啟動(dòng):
系統(tǒng)存儲(chǔ)器被映射到啟動(dòng)空間(0x0000,0000),但仍然能夠在原有的地址(0x1FFF,F000)訪問(wèn)它。
3、 從內(nèi)置SRAM啟動(dòng):
只能在0x2000,0000開(kāi)始的地址區(qū)訪問(wèn)SARM。當(dāng)從內(nèi)置的SRAM中啟動(dòng),在應(yīng)用程序的初始化代碼中,必須使用NVIC的異常表和偏移寄存器,從新映射向量表到SRAM之中。
《stm32權(quán)威指南》中有這樣的說(shuō)法,待理解。

完整的Word格式文檔51黑下載地址(共12頁(yè)):
STM32在main函數(shù)之前執(zhí)行的啟動(dòng)程序.docx (392.57 KB, 下載次數(shù): 45)


評(píng)分

參與人數(shù) 1黑幣 +50 收起 理由
admin + 50 共享資料的黑幣獎(jiǎng)勵(lì)!

查看全部評(píng)分

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏3 分享淘帖 頂 踩
回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:1 發(fā)表于 2017-10-21 00:27 | 只看該作者
好資料,51黑有你更精彩!!!
回復(fù)

使用道具 舉報(bào)

板凳
ID:324843 發(fā)表于 2018-5-8 10:44 | 只看該作者
資料不錯(cuò), 可惜沒(méi)有積分下載個(gè)全的
回復(fù)

使用道具 舉報(bào)

地板
ID:222998 發(fā)表于 2018-10-22 10:52 | 只看該作者
樓主厲害了  分析的很詳細(xì)   學(xué)習(xí)了
回復(fù)

使用道具 舉報(bào)

5#
ID:372307 發(fā)表于 2018-10-22 20:32 | 只看該作者
學(xué)習(xí)了
回復(fù)

使用道具 舉報(bào)

6#
ID:474500 發(fā)表于 2019-2-1 10:08 | 只看該作者
感謝樓主,可惜沒(méi)有積分下載
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表