問題提出
大家不妨設(shè)想一下,cpu 的工作是什么,cpu 是沒有主觀意識(shí)的,它只會(huì)按照特定的指令執(zhí)行相應(yīng)的操作,用專業(yè)術(shù)語來說就是: 取指 -> 譯碼 -> 執(zhí)行 ,譯碼和執(zhí)行肯定是在 cpu 內(nèi)部進(jìn)行操作的,并且前提是已經(jīng)取到了指令。那現(xiàn)在問題來了,指令在哪?
cpu上電復(fù)位后執(zhí)行的第一步操作就是取指令
- 問題1:指令存儲(chǔ)在何處
我們?cè)?a href="http://ttokpm.com/v/tag/1247/" target="_blank">電腦上編寫的程序最終是要燒寫到芯片內(nèi)部的 FLASH中(此處特指STM32)。
- 問題2:如何將可執(zhí)行文件燒寫至 FLASH 上
STM32 的啟動(dòng)方式有很多種,從主存 FLASH 啟動(dòng),從 system memory 啟動(dòng),從 SRAM 中啟動(dòng)。
- 問題3:從 SRAM 中啟動(dòng),為什么需要重新設(shè)置中斷向量表
接下來,我們將圍繞這三個(gè)問題進(jìn)行解答
猜想
既然 cpu 上電復(fù)位后第一步操作就是取指令,那么這個(gè)指令肯定是存儲(chǔ)在掉電不丟失的存儲(chǔ)介質(zhì)上(rom、flash)。
- 猜想1:指令存儲(chǔ)在掉電不丟失的存儲(chǔ)介質(zhì)上
我們最終生成的、cpu可以執(zhí)行的可執(zhí)行文件肯定是要通過某種外設(shè)將用戶程序燒寫到 FLASH 上,這一點(diǎn)肯定是毋庸置疑的,因?yàn)?cpu 與外圍設(shè)備進(jìn)行數(shù)據(jù)交互的時(shí)候是通過外設(shè)控制器來進(jìn)行的。
- 猜想2:通過某種外設(shè)將可執(zhí)行文件燒寫至 FLASH 上
STM32 的 FLASH 基地址為 0X0800 0000 ,SRAM 基地址為 0X2000 0000。可不可能是因?yàn)檫@兩個(gè)存儲(chǔ)介質(zhì)的地址不同,所以才要重新設(shè)置中斷向量表。
因?yàn)槲覀兌贾溃?strong>中斷向量表的首地址就是程序的入口地址。
- 猜想3:可能與基地址有關(guān)
實(shí)驗(yàn)驗(yàn)證
實(shí)驗(yàn)前必備知識(shí)
1. XIP設(shè)備
eXecute In Place
,即芯片內(nèi)執(zhí)行,指應(yīng)用程序可以直接在 flash 閃存內(nèi)運(yùn)行,不必再把代碼讀到系統(tǒng) RAM中。在我們的印象里,應(yīng)用程序必須要從硬盤中加載到內(nèi)存當(dāng)中才可以被運(yùn)行,但實(shí)際上應(yīng)用程序是可以直接在flash 閃存運(yùn)行的,也就是說,cpu 可以直接從 flash 中取出指令。對(duì)于 STM32 而言,它是有 XIP 設(shè)備的。
STM32F1 內(nèi)存圖
如上圖所示,F(xiàn)LASH、SYSTEM MEMORY、OPTION BYTES 都是STM32內(nèi)部的XIP設(shè)備。
F1 內(nèi)存圖信息不是很全,再看下 F4 的內(nèi)存圖。
STM32F4內(nèi)存圖:
我們可以看到,不論是 F1 還是 F4,XIP
設(shè)備都屬于內(nèi)存圖中的 BLOCK0
區(qū)域內(nèi)。
這樣我們大概就知道了 STM32 內(nèi)部的 XIP
設(shè)備在 0x0000 0000 ~ 0x1FFF FFFF
內(nèi)。
上述的內(nèi)存圖是通過映射的方式將芯片的框圖進(jìn)行映射得到的,也就是說,上述這幅圖是為了開發(fā)人員更好地面向芯片編程而抽象出來的一幅圖。我們先來看下面這副圖
STM32F1框圖:
STM32F4框圖:
對(duì)比兩幅框圖可以看出,F(xiàn)4 比 F1 復(fù)雜很多,特別體現(xiàn)在外設(shè)上,架構(gòu)還是差不多的。
紅色箭頭所指向的就是譯碼電路。
如果你學(xué)過微機(jī)原理,那么你肯定知道,外設(shè)是通過譯碼電路連接到地址總線上,每一個(gè)外設(shè)都有其相對(duì)應(yīng)的內(nèi)存范圍,當(dāng) cpu 發(fā)出的地址信息處于某一個(gè)外設(shè)的地址范圍內(nèi),就選中了該外設(shè),cpu就可以與該外設(shè)進(jìn)行數(shù)據(jù)交互。
一個(gè)外設(shè)對(duì)應(yīng)一個(gè)內(nèi)存范圍,那所有的外設(shè)結(jié)合起來,是不是就是對(duì)應(yīng)一張圖了。
2. STM32 啟動(dòng)配置
在 STM32F10xxx
里,可以通過 BOOT[1:0]
引腳選擇三種不同啟動(dòng)模式。
在系統(tǒng)復(fù)位后,SYSCLK
的第 4 個(gè)上升沿, BOOT 引腳的值將被鎖存。用戶可以通過設(shè)置 BOOT1
和 BOOT0
引腳的狀態(tài),來選擇在復(fù)位后的啟動(dòng)模式。
在啟動(dòng)延遲之后, CPU
從地址 0x0000 0000
獲取堆棧頂?shù)牡刂?,并從啟?dòng)存儲(chǔ)器的 0x0000 0004
指示的地址開始執(zhí)行代碼。(這里先不驗(yàn)證,在之后的博客中會(huì)進(jìn)行驗(yàn)證,但你需要記住,后面用的上)
因?yàn)楣潭ǖ拇鎯?chǔ)器映像,代碼區(qū)始終從地址 0x0000 0000
開始(通過 ICode
和 DCode
總線訪問),而數(shù)據(jù)區(qū)(SRAM
)始終從地址 0x2000 0000
開始(通過系統(tǒng)總線訪問)。Cortex-M3
的 CPU 始終從 ICode
總線獲取復(fù)位向量,即啟動(dòng)僅適合于從代碼區(qū)開始(典型地從 Flash 啟動(dòng))。STM32F10xxx
微控制器實(shí)現(xiàn)了一個(gè)特殊的機(jī)制,系統(tǒng)可以不僅僅從 Flash 存儲(chǔ)器或系統(tǒng)存儲(chǔ)器啟動(dòng),還可以從內(nèi)置 SRAM 啟動(dòng)。
根據(jù)選定的啟動(dòng)模式,主閃存存儲(chǔ)器、系統(tǒng)存儲(chǔ)器或 SRAM 可以按照以下方式訪問:
- 從主閃存存儲(chǔ)器啟動(dòng) :主閃存存儲(chǔ)器被映射到啟動(dòng)空間(
0x00000000
),但仍然能夠在它原有的地址(0x08000000
)訪問它,即閃存存儲(chǔ)器的內(nèi)容可以在兩個(gè)地址區(qū)域訪問,0x00000000
或0x08000000
。 - 從系統(tǒng)存儲(chǔ)器啟動(dòng) :系統(tǒng)存儲(chǔ)器被映射到啟動(dòng)空間(
0x00000000
),但仍然能夠在它原有的地址(互聯(lián)型產(chǎn)品原有地址為0x1FFFB000
,其它產(chǎn)品原有地址為0x1FFFF000
)訪問它。 - 從內(nèi)置 SRAM 啟動(dòng) :只能在
0x20000000
開始的地址區(qū)訪問SRAM
(當(dāng)從內(nèi)置SRAM
啟動(dòng),在應(yīng)用程序的初始化代碼中,必須使用NVIC
的異常表和偏移寄存器,重新映射向量表到SRAM
中)。
一般情況下都是從主閃存模式啟動(dòng)的,也就是用戶代碼被燒寫到 0x08000000
地址處。
內(nèi)嵌的自舉程序 (Bootloader)
內(nèi)嵌的自舉程序存放在系統(tǒng)存儲(chǔ)區(qū),由 ST 在生產(chǎn)線上寫入,用于通過可用的串行接口對(duì)閃存存儲(chǔ)器進(jìn)行重新編程,也就是這個(gè)自舉程序在出廠的時(shí)候就已經(jīng)固化了。大家可以想一下內(nèi)嵌的自舉程序的作用是什么?想不出來也沒關(guān)系,后面會(huì)講到。
如果想要詳細(xì)了解這個(gè)自舉程序到底干了什么,可以看下官方文檔:
STM32 microcontroller system memory boot mode
3. 可執(zhí)行文件的形成過程
STM32 | hex文件、bin文件、axf文件的區(qū)別?
大家可以看下這篇博文,寫的還是挺不錯(cuò)的!描述了最終燒寫到STM32中的可執(zhí)行代碼的形成過程。
4. 三種復(fù)位
- 硬件復(fù)位
顧名思義通過硬件給系統(tǒng)一個(gè)復(fù)位,比如在電路板上設(shè)計(jì)一復(fù)位電路,通過按下按鍵就可以給系統(tǒng)實(shí)現(xiàn)一個(gè)復(fù)位,而無論系統(tǒng)在執(zhí)行什么樣的程序。復(fù)位后初始化一些配置芯片,硬件復(fù)位的作用區(qū)域一般是全局的。
- 軟件復(fù)位
是通過軟件給系統(tǒng)一個(gè)復(fù)位信號(hào),如低電平或許是高電平(具體看系統(tǒng)設(shè)置)來實(shí)現(xiàn)復(fù)位操作軟件復(fù)位一般是一些塊結(jié)構(gòu)復(fù)位。
- 上電復(fù)位
系統(tǒng)在上電的瞬間就執(zhí)行復(fù)位操作, 上電復(fù)位里面包括硬件復(fù)位和軟復(fù)位的操作,硬件復(fù)位和軟復(fù)位是從上電復(fù)位里面的某點(diǎn)開始的啟動(dòng)操作。
復(fù)位需要初始化CPU系統(tǒng),包括CPU和內(nèi)存等。
驗(yàn)證猜想
1. 驗(yàn)證猜想-1
對(duì)于猜想1,其實(shí)不需要驗(yàn)證。代碼肯定是要存儲(chǔ)在掉電不丟失的存儲(chǔ)介質(zhì)上,否則,每次重新上電都要重新燒寫程序,這是與事實(shí)相反的。而在實(shí)驗(yàn)前必備知識(shí)中,我們了解到 STM32
內(nèi)部的 XIP
設(shè)備,那不就是代碼存儲(chǔ)的地方嗎?并且也在 STM32
啟動(dòng)方式中詳細(xì)地描述了代碼存儲(chǔ)位置。
- 如果從主FLASH啟動(dòng),用戶代碼存儲(chǔ)在0X 0800 0000
- 如果從
SYSTEM MEMORY
啟動(dòng),里面存儲(chǔ)的是Bootloader
,是芯片出廠的時(shí)候就已經(jīng)固化好了的,可以從中讀數(shù)據(jù),但是不可以向其中寫數(shù)據(jù),它的作用就是:將用戶程序通過可用的外設(shè)燒寫到指定的地址處,然后啟動(dòng)STM32
。 - 如果從 SRAM 啟動(dòng),用戶代碼存儲(chǔ)在
0X2000 0000
2. 驗(yàn)證猜想-2
實(shí)驗(yàn)前的必備知識(shí)中已經(jīng)大概地描述了最終燒寫到 STM32
中的可執(zhí)行文件的形成過程,現(xiàn)在我們需要驗(yàn)證的就是如何將可執(zhí)行文件燒寫到指定的存儲(chǔ)設(shè)備中去(假設(shè)是 FLASH
,其實(shí)也可以是 SRAM
)
我第一次使用 Flymcu
(串口下載軟件的時(shí)候),我腦海里就有一個(gè)疑問,就是這個(gè)軟件到底是怎樣使得 STM32
將生成的代碼燒寫到內(nèi)部 FLASH
上的。這真的是很不可思議!因?yàn)?STM32
上電復(fù)位后肯定是要執(zhí)行代碼的,可是我還沒有給它代碼呢,它怎么會(huì)運(yùn)作呢?當(dāng)時(shí)我真的很迷惑。
其實(shí),STM32
出廠的時(shí)候 Bootloader
(用于將用戶程序下載到 STM32
內(nèi)部指定地址處的固件(程序))就已經(jīng)固化在了 System Memory
上了,可讀寫無效。
從 STM32
啟動(dòng)配置一節(jié)中我們知道,可以通過對(duì) BOOT1
和 BOOT0
引腳上高低電平的改變從而實(shí)現(xiàn) STM32
啟動(dòng)方式的不同。
如上所說,Bootloader
存儲(chǔ)在 Sytem Memory
上,如果想要讓 Bootloader
運(yùn)行(將用戶程序下載到指定內(nèi)存地址處),那啟動(dòng)模式肯定是要選擇以系統(tǒng)存儲(chǔ)器的方式啟動(dòng)。
BOOT1 = 0 BOOT0 = 1 -> 系統(tǒng)存儲(chǔ)器模式
因此, 外部電路的設(shè)計(jì)的目的就是要能夠達(dá)到能夠?qū)?BOOT1
和 BOOT0
引腳上高低電平改變的能力 。
接下來,我們就以正點(diǎn)原子的原理圖(探索者)為例,來看下 STM32
外部的電路到底是如何設(shè)計(jì)的,以及Flymcu
到底是怎樣控制 BOOT1
和 BOOT0
引腳上高低電平改變從而達(dá)到具有使得 STM32
從系統(tǒng)存儲(chǔ)器啟動(dòng)的的神奇能力。
如上圖所示,這就是正點(diǎn)原子探索者一鍵下載電路。
一鍵下載電路涉及到模電知識(shí),下面這篇文章寫的還不錯(cuò),并且還描述了CH340G芯片引腳的作用和功能。
stm32一鍵下載電路(下一篇文章)
從上圖我們可以知道,F(xiàn)lymcu 肯定是通過 usb 線將數(shù)據(jù)或指令寫入 CH340G
內(nèi)(CH340 D
+ CH340 D-
)然后CH340G
根據(jù)來自 usb 的指令進(jìn)行相應(yīng)的工作。
CH340G在此電路中的工作就兩個(gè):
- 與STM32進(jìn)行數(shù)據(jù)交互
- 控制BOOT0和RESET高低電平的變化
特別注意:正點(diǎn)原子探索者BOOT0和BOOT1引腳默認(rèn)都是接地
BOOT0、BOOT1是通過跳線帽和地進(jìn)行連接的
現(xiàn)在我們知道了,控制 BOOT1
和 BOOT0
引腳上高低電平改變是 CH340G
的作用,而 CH340G
是嚴(yán)格按照來自Flymcu 的指令進(jìn)行的,所以,控制 BOOT1
和 BOOT0
引腳上高低電平改變的幕后黑手就是 Flymcu
。
注意:向 FLASH 中燒寫程序不僅僅只有串口,由于硬件平臺(tái)的限制,因此分析串口下載。
問題來了,那 Flymcu 到底干了什么,它是如何將用戶程序燒寫的 STM32 內(nèi)部指定地址處?
以跑馬燈為例,看下Flymcu燒寫程序過程中輸出的信息。
DTR
電平置低:復(fù)位RTS
電平置高:進(jìn)入 Bootloader- 延時(shí)
100ms
:有誰能夠告訴我為什么 DTR
電平置高:釋放復(fù)位RTS
維持高 :此時(shí)開始運(yùn)行Bootloader
- 開始連接 :
Flymcu
要與STM32
連接肯定是要發(fā)送特定的指令,并且當(dāng)STM32
接收到預(yù)先約定好的指令時(shí),也會(huì)發(fā)送特定的回復(fù)。(和接頭的性質(zhì)差不多)
注意:STM32
從 Bootloader
啟動(dòng)到能夠與外部設(shè)備進(jìn)行數(shù)據(jù)交互需要一定的時(shí)間,因此連接需要一定的時(shí)間
- 讀出關(guān)于芯片相關(guān)的數(shù)據(jù)
- 讀出選項(xiàng)字節(jié)
- 進(jìn)行全片擦除,去除寫保護(hù),再次重啟
Bootloader
(有大佬能告訴我為什么) - 編寫程序,從
0x0800 0000
處開始運(yùn)行
上述的過程中,大家比較疑惑的地方就是,Flymcu
發(fā)送給 STM32
的指令到底是什么,這個(gè)指令肯定是事先就確定好的,在這個(gè)文檔中提及到了。
stm32 microcontroller system memory boot mode
這個(gè)手冊(cè)中的內(nèi)容大家可以自己詳細(xì)地去看下,內(nèi)容不多,我就粗略地說一下必要的知識(shí)點(diǎn)。
硬件連接需要(文檔中的第35頁)
通過串口與外部設(shè)備進(jìn)行數(shù)據(jù)交互時(shí)STM32外部電路設(shè)計(jì)。
通過 DFU
與外部設(shè)備進(jìn)行數(shù)據(jù)交互時(shí) STM32
外部電路設(shè)計(jì)
還有其他的連接方式,我想表述的意思就是:
Bootloader
與外部設(shè)備進(jìn)行數(shù)據(jù)交互的方式有很多種,不僅僅只有串口,只是由于硬件平臺(tái)有限(正點(diǎn)原子只有通過串口下載的接口(調(diào)試接口除外))而根據(jù)不同的交互方式,STM32
外部的電路設(shè)計(jì)又大不相同。
Bootloader啟動(dòng)流程
從啟動(dòng)流程中我們就可以得到Flymcu發(fā)送給STM32的特定連接指令為:0x7F。
STM32F40xxx/41xxx devices bootloader version
通過版本信息中紅色畫線部分可以得知,當(dāng) Bootloader
接收到相應(yīng)的命令之后,就會(huì)連續(xù)發(fā)送兩個(gè) response
。我們這個(gè)時(shí)候再看下 Flymcu 中的輸出信息,
通過紅色畫線部分可以看出,Flymcu
接收到兩個(gè)來自 Bootloader
的信息。
此時(shí),接頭成功?。。。?!
接頭成功后肯定就可以進(jìn)行數(shù)據(jù)交互了?。?!
因此,我們最初的猜想是正確的:即 STM32 通過某種外設(shè)將可執(zhí)行文件燒寫至 FLASH 上(也可以是 SRAM)
驗(yàn)證猜想-3
博客當(dāng)中已經(jīng)多次提及到,STM32
不僅可以從 FLASH
上啟動(dòng),還可以從 SRAM
上啟動(dòng)。并且在STM32啟動(dòng)配置中有一個(gè)小提示:從 SRAM
中啟動(dòng),需要重新設(shè)置中斷向量表。
中斷向量表的設(shè)置是用戶在用戶程序中自己實(shí)現(xiàn)的?。?!
要驗(yàn)證這個(gè)猜想,可以從 SRAM
中啟動(dòng),但是不設(shè)置中斷向量表,看一下會(huì)出現(xiàn)什么情況。
由于正點(diǎn)原子的電路設(shè)計(jì)(因?yàn)槲沂褂玫木褪钦c(diǎn)原子的探索者開發(fā)板),使得無法通過串口進(jìn)行 SRAM
啟動(dòng),只能通過調(diào)試接口下載程序。
注意:SRAM是掉電數(shù)據(jù)就會(huì)丟失的存儲(chǔ)器介質(zhì),因此使用時(shí)(前提是已經(jīng)掉電)要重新下載程序從
SRAM
中啟動(dòng)的最主要的目的是用來調(diào)試程序,產(chǎn)品中的用戶程序肯定都是存儲(chǔ)在 FLASH 上的,不然每次掉電后用戶程序都沒了?。。?/p>
如何通過調(diào)試接口將用戶程序下載到 SRAM
處,可以參考一下下面兩篇博文:
- STM32 內(nèi)部 SRAM 調(diào)試程序
- 在 SRAM 中調(diào)試代碼
假設(shè)你現(xiàn)在已經(jīng)實(shí)現(xiàn)了能夠通過調(diào)試接口將用戶程序下載到 SRAM
處,那么接下來,我們來驗(yàn)證一下。如果沒有重新設(shè)置中斷向量表會(huì)出現(xiàn)什么結(jié)果。
得出結(jié)論,總結(jié)歸納
對(duì)于最開始提出的三個(gè)猜想,現(xiàn)在可以得出結(jié)論:
- 指令存儲(chǔ)在掉電不丟失的存儲(chǔ)介質(zhì)上
- STM32 通過某種外設(shè)將可執(zhí)行文件燒寫至掉電不丟失的存儲(chǔ)介質(zhì)上
- 中斷向量表的首地址就是程序的入口地址
注意: 通過串口下載程序,實(shí)際上是 Flymcu(上位機(jī))與 STM32 內(nèi)置的 Bootloader 進(jìn)行數(shù)據(jù)交互,但兩者直接需要特定的硬件環(huán)境(CH340G(USB轉(zhuǎn)串口芯片))
注意: 內(nèi)存圖中的 reserved
有些是不使用,有些是不能用(有其他重要的作用:可讀寫忽略),不可以修改其值
看完這篇博客,你的腦海里必須得有一個(gè)流程的框架:
- 用戶面向單片機(jī)編寫用戶代碼(C,C++,ASM)
- 用戶代碼通過交叉編譯工具鏈生成單片機(jī)可以執(zhí)行的可執(zhí)行文件(HEX,BIN,AXF)
- 上位機(jī):各種燒寫工具(不局限于
Flymcu
(因?yàn)?Bootloader
與外部設(shè)備進(jìn)行數(shù)據(jù)交互不僅僅只是通過USART)))與單片機(jī)內(nèi)部的Bootloader
進(jìn)行數(shù)據(jù)交互(目的是將可執(zhí)行文件下載到指定的存儲(chǔ)地址處)
將可執(zhí)行文件下載到指定存儲(chǔ)地址處,然后還會(huì)繼續(xù)等待上位機(jī)的 command
,可以通過復(fù)位或上位機(jī)發(fā)送跳轉(zhuǎn)到用戶代碼入口地址的命令執(zhí)行用戶程序。
這個(gè)流程的框架總結(jié)一句話就是:
可執(zhí)行程序 -> cpu執(zhí)行第一條用戶代碼的流程
至于 cpu
執(zhí)行第一條用戶代碼之后的流程后面的博客會(huì)詳細(xì)說明,但毋庸置疑的是,這是一個(gè)重要轉(zhuǎn)折點(diǎn),在這個(gè)點(diǎn)之后執(zhí)行的是你自己編寫的代碼,你比較熟悉這個(gè)過程,但是在這個(gè)點(diǎn)之前,對(duì)大部分人來說都是都是比較陌生的,但是但你對(duì)這個(gè)過程了解之后,會(huì)對(duì)你的知識(shí)體系有非常大的提升。
-
STM32
+關(guān)注
關(guān)注
2264文章
10854瀏覽量
354299 -
代碼
+關(guān)注
關(guān)注
30文章
4722瀏覽量
68234
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論