|
從匯編編程的角度上看,STM32從復(fù)位到主程序的過程要比MCS稍顯復(fù)雜,一個(gè)比較主要的原因是CortexM3用很多偽指令來描述啟動(dòng)過程,會(huì)讓我這樣的菜鳥感到云遮霧罩。
說到底,CortexM3也好,MCS51也罷,啟動(dòng)過程都是大同小異的,無非是PC指針初始化,執(zhí)行完用戶的復(fù)位程序之后取出主程序地址,跳到主程序上。
對(duì)于MCS51來說,這個(gè)過程非常好理解:
ORG 0000H
LJMP Reset
ORG 0003H
;配置各個(gè)中斷服務(wù)程序的入口
………………
ORG 0100H
Reset:
………………
;初始化及主程序段
51單片機(jī)復(fù)位,PC=0000H指向首地址,取指令和操作數(shù),轉(zhuǎn)0100H執(zhí)行初始化及主程序,先定義堆棧指針SP,然后再干別的。
這個(gè)過程說明MCS51程序存儲(chǔ)器是這樣分區(qū)的:
復(fù)位入口 0000H-0002H。放置越過中斷向量程序段,直達(dá)主程序段的跳轉(zhuǎn)指令。
中斷向量 0003H-00BBH。放置中斷服務(wù)程序向量。有些增強(qiáng)型51單片機(jī)的中斷向量已經(jīng)用到了00BBH。
復(fù)位程序 從最后一個(gè)中斷向量操作數(shù)之后的地址開始。通常是先配置堆棧指針SP。
主程序段 主程序代碼區(qū)。
但對(duì)于CortexM3來說,它的復(fù)位過程有點(diǎn)兒特殊:如果BOOT0配置成從FLASH區(qū)啟動(dòng),那首先是PC=0x08000000,先把這個(gè)地址開始的4個(gè)字節(jié)數(shù)值賦值給主棧指針MSP,然后再把0x08000004開始的4個(gè)字節(jié)賦值給PC,轉(zhuǎn)到復(fù)位程序。
實(shí)際上,復(fù)位時(shí)PC=0x00000000,但BOOT0=0時(shí),0x00000000會(huì)被映射到0x08000000上,所以說PC是從0x08000000開始也是可以的。
CortexM3程序存儲(chǔ)器分區(qū)是如下:
MSP賦值 0x08000000—0x08000003。當(dāng)PC指向這個(gè)數(shù)據(jù)塊時(shí),4字節(jié)數(shù)自動(dòng)賦值給MSP。
復(fù)位入口 0x08000004—0x08000007。4個(gè)字節(jié),放置復(fù)位程序的入口地址。
中斷向量 在0x08000007之后,每個(gè)向量的入口地址可以定義。每個(gè)中斷向量地址可以浮動(dòng)。
復(fù)位程序 從最后一個(gè)中斷向量操作數(shù)之后的地址開始。
主程序段 主程序代碼區(qū)。
歸結(jié)起來,兩者跳轉(zhuǎn)的方式有所不同,MCS51是通過入口放置指令,而CortexM3則是通過入口處放置地址來實(shí)現(xiàn)跳轉(zhuǎn)。
為了深入探究一下CortexM3的復(fù)位機(jī)制,我編一個(gè)簡(jiǎn)單的程序,力圖通過HEX和LST文件理清脈絡(luò)。
環(huán)境如下:
IDE:Keil 4.12
硬件:STM32F103VCT6,BOOT0=0
程序:匯編語言
LST程序如下:
(1) 初始化程序Initialization.LST
00000000 20005000 MSP_Top EQU 0x20005000
00000000 THUMB
00000000 AREA Reset, Data, ReadOnly
00000000 EXPORT __Vectors
00000000 EXPORT __Vectors_End
00000000 EXPORT __Vectors_Size
00000000 __Vectors
00000000 20005000 DCD MSP_Top
00000004 00000000 DCD Reset_Handler
00000008 00 00 00
00 00 00
00 00 SPACE 0x8
00000010 __Vectors_End
00000010 00000010 __Vectors_Size EQU __Vectors_End - __Vectors
00000010 AREA |.text|, Code, Readonly
00000000 Reset_Handler PROC
00000000 EXPORT Reset_Handler [WEAK]
00000000 IMPORT Main
00000000 4800 LDR R0,=Main
00000002 4700 BX R0
00000004 ENDP
00000004 ALIGN
00000004 END
主棧配置到SRAM區(qū)0x20005000,暫時(shí)未定義主棧大小。暫時(shí)未定義其它中斷向量。
(2) 主程序Main.LST
INCLUDE Stm32F103REG.h
00000000 Main PROC
00000000 EXPORT Main
00000000 4801 LDR R0,=RCC_CR
00000002 6801 LDR R1,[R0]
00000004 E7FE B Main
00000006 ENDP
00000006 END
Stm32F103REG.h是自行編制的寄存器定義程序。
上述程序編譯之后打開工程HEX文件和兩個(gè)程序段的LST文件并整理一下,刪除一些不重要的數(shù)據(jù),結(jié)果如下:
(3)工程HEX文件
:020000040800F2
:100000000050002011000008000000000000000067
:10001000004800471900000801480168FCE700009B
:04002000001002408A
:0400000508000019D6
:00000001FF
看起來有些亂七八糟,整理一下。
數(shù)據(jù)長(zhǎng)度 地址偏移 數(shù)據(jù)類型 數(shù)據(jù)塊 校驗(yàn)和
1byte 2bytes 1byte n bytes 1byte
02 00 00 04 08 00 F2
10 00 00 00 00 50 00 20 11 00 00 08 00 00 00 00 00 00 00 00 67
10 00 10 00 00 48 00 47 19 00 00 08 01 48 01 68 FC E7 00 00 9B
04 00 20 00 00 10 02 40 8A
04 00 00 05 08 00 00 19 D6
00 00 00 01 FF
數(shù)據(jù)長(zhǎng)度好理解,是說本行中數(shù)據(jù)塊有幾個(gè)字節(jié)。
地址偏移是指當(dāng)前行第一個(gè)數(shù)據(jù)相對(duì)于基址的位置。
查了一下資料,數(shù)據(jù)類型可分為5種類型:
00=這一行的數(shù)據(jù)塊里全是數(shù)據(jù);
01=HEX文件結(jié)束符,所在行數(shù)據(jù)塊為空;
02=數(shù)據(jù)塊里的數(shù)據(jù)是擴(kuò)展段地址;
03=數(shù)據(jù)塊里的數(shù)據(jù)是段地址;
04=數(shù)據(jù)塊里的數(shù)據(jù)是線性地址高位;
05=數(shù)據(jù)塊里的數(shù)據(jù)是線性地址。
看起來,數(shù)據(jù)類型可分為三大類:數(shù)據(jù)類(00)、地址類(02~05)和編譯標(biāo)識(shí)類(01)
第一行數(shù)據(jù)類型為04,說明后面的0800和地址偏移0000構(gòu)成了一個(gè)基址0x08000000,這是STM32F103 FLASH區(qū)的首址,后序行的數(shù)據(jù)存儲(chǔ)地址都可以根據(jù)這個(gè)基址加偏移量計(jì)算出來。
第二行數(shù)據(jù)類型為00,偏移量為0000,也就是說,數(shù)據(jù)塊的第一個(gè)00被存放在地址0x08000000中,這就是單片機(jī)復(fù)位時(shí)從FLASH區(qū)啟動(dòng)的首址。
按照前面的說明,首址之后的4個(gè)字節(jié)應(yīng)當(dāng)是準(zhǔn)備賦給MSP的值,也就是程序指定的0x20005000,可現(xiàn)在卻是00 50 00 20,看了半天恍然大悟,突然想起CortexM3數(shù)據(jù)格式是所謂的小端模式Little-endian,所以這4個(gè)字節(jié)的32位數(shù)據(jù)得按字節(jié)倒過來讀成20 00 50 00。但當(dāng)數(shù)據(jù)類型為地址類時(shí),數(shù)據(jù)塊數(shù)據(jù)就可以正著讀了,哈哈。
00 50 00 20之后又是一個(gè)地址,倒過來是08 00 00 11,這就是復(fù)位程序入口地址。其后的數(shù)據(jù)是8個(gè)字節(jié)的00,這就是SPACE 0x8的功勞了。 按照流程,復(fù)位地址賦值給PC之后會(huì)從地址0x08000011開始運(yùn)行,0x08000011單元的數(shù)據(jù)是48,對(duì)照一下Initialization.LST,對(duì)應(yīng)的是它的第一條可執(zhí)行語句'LDR R0,=Main',只不過這條指令的代碼48 00也存儲(chǔ)成了小端模式。
以此類推,逐一閱讀后面的數(shù)據(jù),過程就出來了,Main的段地址是第三行的那個(gè)大紅圈里的數(shù)據(jù)0x08000019,從它以后就是Main的指令代碼了。但這里有一個(gè)問題,Main.LST表明‘B Main’指令的代碼是E7 FE,可HEX里卻是E7 FC,這又是怎么回事兒呢?不知道哪位前輩能告訴我。
還有一個(gè)問題:從0x08000020開始的數(shù)據(jù)明顯是RCC_CR的地址,它怎么會(huì)出現(xiàn)在這兒呢?
用匯編寫STM32程序是苦中有樂,有人說我這是自找麻煩,但我不這么認(rèn)為。與使用固件庫編程相比,無論是面向寄存器編程還是直接用匯編,編程難度都要大很多,但是,程序透明度也比前者大得多,絕對(duì)不會(huì)出現(xiàn)神龍見首不見尾的感覺,尤其是對(duì)于我這種不太會(huì)C語言的硬件工程師就更是如此。
|
評(píng)分
-
查看全部評(píng)分
|