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