嵌入式開發人員觀看固件在微控制器調試器上運行幾乎是一種神奇的體驗。按下 IDE 中的那個綠色小按鈕,進度條在顯示屏上快速完成,直到最后屏幕閃爍,一行代碼用 main 一詞突出顯示。幕后發生了如此多的事情,幾乎令人難以置信!讓我們檢查在到達程序 main 之前發生了什么。
一旦程序被加載到微控制器上,微處理器首先讀取復位向量開始執行。復位向量保存程序的第一條指令所在的內存地址。然而,這些第一條指令并不是 main 中存在的應用程序的開始,而是通常隱藏在啟動代碼中的初始化例程。
啟動代碼執行許多關鍵功能,這些功能是準備微控制器以開始執行開發人員的應用程序所必需的。啟動代碼所遵循的確切步驟因微控制器而異,但通常第一步是初始化系統時鐘。系統時鐘穩定后,必須通過將初始化變量復制到 RAM 來設置內存環境。此過程通常稱為“C 向下復制”。這個想法是微控制器的內存被設置為運行應用程序代碼將在其下運行的 C 環境。
啟動代碼還將初始化堆棧指針。堆棧是存儲自動變量、函數參數、中斷幀和函數返回地址的內存區域。裸機系統(沒有 RTOS 的系統)將只有一個堆棧區域,默認情況下,該區域的大小通常為 0x400 深度。嵌入式開發人員啟動代碼通常會有一些旨在設置堆棧指針頂部的匯編語言指令。
一旦時鐘被初始化,C 向下復制并且棧頂已經被識別,最后一步是跳轉到或調用 main 。一些啟動代碼將執行跳轉到 main 而不是對 main 的函數調用,因為這可以節省一些寶貴的堆棧空間字節。但是,無論使用哪種方法,開發人員都會發現自己處于 main 的開頭,調試器暫停并等待執行應用程序的權限。
然而,開發人員通常完全不知道在到達 main 開頭之前已執行的所有代碼。但是,如果開發人員取消選中“run to main”選項,應用程序不會在 main 的第一行中斷,而是在啟動代碼的第一條指令處中斷。然后,開發人員可以在到達 main 之前單步執行用于設置處理器的每一行代碼。正如人們所預料的那樣,啟動代碼通常因開發環境而異。在項目開始時,單步執行啟動代碼通常很有用,因為在到達 main 之前,嵌入式開發人員永遠不知道會設置什么有趣的東西。
如果混合中有引導加載程序,微控制器的啟動代碼會變得更加復雜。當應用程序啟動時,可能會有一段代碼執行快速檢查以確定是否應執行主應用程序或是否應執行固件更新應用程序。如果執行引導加載程序以更新固件或簡單地以已知狀態啟動系統并執行系統完整性檢查,則啟動代碼可能需要重置堆棧指針,而不是簡單地對其進行初始化。
嵌入式軟件開發人員過去了解他們軟件的每一個角落。由于微控制器已經采用 32 位,因此開發固件的方式發生了迅速變化。處理器如何啟動和代碼執行的細節只是開始隱藏在幕后的第一段代碼。有一天,嵌入式開發人員可能只知道微控制器級別發生的事情,就像應用程序開發人員了解服務器微處理器級別發生的事情一樣多。盡管似乎失去了洞察力,但抽象和隱藏代碼的級別無疑將幫助開發人員更快地以更低的成本編寫代碼,而明顯的魔術在幕后起作用。