STM32系列微控制器廣泛應(yīng)用于嵌入式系統(tǒng)開發(fā),其啟動過程對于理解系統(tǒng)如何從上電復(fù)位到執(zhí)行用戶代碼至關(guān)重要。本文將詳細(xì)介紹如何使用C++編寫STM32的啟動腳本,并以STM32F103為例進行說明。
一、啟動過程概述
STM32微控制器的啟動過程始于復(fù)位操作。當(dāng)處理器復(fù)位后,它會從存儲器的特定位置讀取啟動向量,這些向量決定了系統(tǒng)的初始堆棧指針和復(fù)位處理程序的地址。對于Cortex-M3和Cortex-M4內(nèi)核的STM32微控制器,這一機制尤其關(guān)鍵。
復(fù)位向量表:復(fù)位后,Cortex-M處理器從存儲器地址0x00000000開始讀取前兩個32位字。第一個字是主堆棧指針(MSP)的初始值,第二個字是復(fù)位向量,即復(fù)位處理程序的起始地址。
堆棧指針設(shè)置:堆棧指針通常指向SRAM的末尾,因為Cortex-M3和Cortex-M4的堆棧操作是基于完全降序堆棧(即堆棧指針在存儲之前遞減)。
二、啟動腳本的C++實現(xiàn)
以下是如何用C++編寫STM32F103的啟動腳本的詳細(xì)步驟和代碼示例。
定義SRAM和堆棧地址
cpp
#define SRAM_START 0x20000000U
#define SRAM_SIZE (20U * 1024U) // 20KB
#define SRAM_END ((SRAM_START) + (SRAM_SIZE))
#define STACK_START SRAM_END
初始化中斷向量表
啟動代碼的第一個任務(wù)是在地址0x00000000處初始化中斷向量表。這個表包含了各種中斷處理程序的地址。
cpp
uint32_t vectors[] __attribute__((section(".isr_vector"))) = {
STACK_START,
(uint32_t)Reset_Handler,
(uint32_t)NMI_Handler,
(uint32_t)HardFault_Handler,
(uint32_t)MemManage_Handler,
(uint32_t)BusFault_Handler,
(uint32_t)UsageFault_Handler,
0, // 保留
// 其他中斷處理程序地址...
};
這段代碼指示編譯器將vectors數(shù)組放置在.isr_vector節(jié)中,并通過鏈接器腳本將其放置在Flash存儲器的開始處。
鏈接器腳本
鏈接器腳本用于定義各個內(nèi)存區(qū)域的布局。以下是一個簡單的鏈接器腳本示例,用于STM32F103。
ld
ENTRY(Reset_Handler)
MEMORY {
FLASH(rx) : ORIGIN = 0x08000000, LENGTH = 64K
SRAM(rwx) : ORIGIN = 0x20000000, LENGTH = 20K
}
SECTIONS {
.isr_vector : {
*(.isr_vector)
. = ALIGN(4);
} > FLASH
.text : {
*(.text)
*(.text.*)
*(.init)
*(.fini)
// 其他代碼段...
} > FLASH
// 其他內(nèi)存段...
}
復(fù)位處理程序
復(fù)位處理程序(Reset_Handler)是系統(tǒng)啟動后首先執(zhí)行的函數(shù)。它負(fù)責(zé)初始化堆棧、復(fù)制.data段數(shù)據(jù)到SRAM、清零.bss段,并最終調(diào)用main函數(shù)。
cpp
extern "C" void Reset_Handler(void) {
// 將.data段從Flash復(fù)制到SRAM
uint8_t *pSramData = (uint8_t *)&_sdata;
uint8_t *pFlashData = (uint8_t *)&_la_data;
uint32_t data_size = (uint32_t)&_edata - (uint32_t)&_sdata;
for (uint32_t i = 0; i < data_size; i++) {
*pSramData++ = *pFlashData++;
}
// 在SRAM中將.bss段初始化為零
uint32_t bss_size = (uint32_t)&_ebss - (uint32_t)&_sbss;
uint8_t *pBssData = (uint8_t *)&_sbss;
for (uint32_t i = 0; i < bss_size; i++) {
*pBssData++ = 0;
}
// 調(diào)用main函數(shù)
main();
}
主函數(shù)
主函數(shù)是用戶程序的入口點。在這里,你可以初始化外設(shè)、配置GPIO、啟動任務(wù)等。
cpp
int main(void) {
// 初始化GPIO
GPIO::enable_PortC();
GPIO::setMode(GPIOC, GPIO::PIN_13, GPIO::OUTPUT_2MHZ);
GPIO::setConfig(GPIOC, GPIO::PIN_13, GPIO::OUTPUT_PUSH_PULL);
// 在無限循環(huán)中閃爍LED
while (1) {
GPIO::toggle(GPIOC, GPIO::PIN_13);
ms_delay(1000U);
}
}
三、總結(jié)
通過本文的介紹,我們詳細(xì)了解了如何使用C++編寫STM32F103的啟動腳本。這個過程包括定義SRAM和堆棧地址、初始化中斷向量表、編寫鏈接器腳本、實現(xiàn)復(fù)位處理程序以及編寫主函數(shù)。希望這些內(nèi)容能幫助你更好地理解STM32的啟動過程,并為你的嵌入式系統(tǒng)開發(fā)提供有價值的參考。