天天看點

單片機啟動流程分析(由單片機轉linux非常有必要看一下)

作者:勇哥讀曆史

一 單片機啟動流程概述

單片機上電後一直到準備好C語言運作環境并跳轉到main函數執行總共經曆了5個步驟:

1.核心初始化;

2.強制PC指針指向中斷向量表的複位中斷向量執行複位中斷函數;

3.在複位中斷函數中調用 SystemInit 函數,初始化時鐘,配置中斷向量表等

4.調用 __main 函數完成全局/靜态變量的初始化和重定位工作,初始化堆棧和庫函數

5.跳轉到main函數中執行

二 核心初始化

在單片機上電後首先會進行一系列核心的初始化,關于這部分工作我們隻需要了解即可,在核心初始化的過程中主要做了以下幾件事情:

1.核心複位和NVIC寄存器部厘清零;

2.核心設定堆棧:核心從向量表0位址讀出堆棧位址,并設定主堆棧指針(SP_main);

3.設定PC和LR寄存器

a. LR設定未初始複位值0xFFFF FFFF

b. 單片機的内部硬體機制自動将PC指針定位到中斷向量表的複位中斷向量出,把複位中斷函數Reset_Handler的位址指派給PC指針,然後跳轉執行Reset_Handler。

三 複位中斷函數 Reset_Handler

在核心複位的最後一步,将PC指針指向了複位中斷向量,而複位中斷服務函數中的内容才是我們真正需要關心的内容。檢視IAR環境中的* .s 單片機彙編啟動檔案有以下一段内容:

Reset_Handler PROC

EXPORT Reset_Handler [WEAK]

IMPORT __main

IMPORT SystemInit

LDR R0, =SystemInit ;加載SystemInit的位址到寄存器R0中

BLX R0 ;跳轉R0中的位址執行(執行SystemInit函數)

LDR R0, =__main ;加載SystemInit的位址到寄存器R0中

BX R0 ;跳轉R0中的位址執行(執行__main函數)

ENDP

1

2

3

4

5

6

7

8

9

上面的代碼就是Reset_Handler的中斷服務函數,可以看到在服務中斷函數中先使用 IMPORT聲明了兩個函數 __main,SystemInit,然後順序跳轉執行SystemInit和**__main**函數。下面我們再來了解一下這兩個函數具體幹了些什麼事情:

SystemInit 函數:

在system_stm32f4xx.c檔案中我們可以看到該函數的定義,該函數主要幹了以下兩件事情:

1.初始化時鐘:SYSCLK,HCLK,PCLK2 and PCLK1 prescalers

2.配置中斷向量表:中斷向量表的定位是在 Flash 還是 SRAM,是否需要偏移)

注意:可以通過system_stm32f4xx檔案中的宏定義修改系統時鐘頻率(通過設定鎖相環的相關系數),中斷向量表的位址(位于SRAM還是Flsah,是否偏移,偏移位址多少等參數)

__main()函數(在IAR中是 __iar_program_start ):

該函數被封裝進了編譯器的庫中,是以不同的IDE該函數的名稱有所差別,但所實作的功能大緻類似:

1.完成全局變量/靜态變量/常量的初始化和重定位工作:

a. 跳轉進入__scatterload_rt2函數:通過設定四個寄存器來配置待copy内容(靜态變量、全局變量、常量)的的加載域和運作域,設定待copy内容的大小,為後續__scatterload_cpy()函數服務。

b. 跳轉進入__scatterload_cpy函數,完成靜态變量、全局變量、常量的從flash到SRAM的重定位。

c. 跳轉進入__scatterload_zeroinit函數,完成未初始化的全局變量的初始化。

2.初始化堆棧(這裡指程式棧)和庫函數:

跳轉進入__user_steup_stackheap函數:調用**__user_libspac__user_libspace**為C庫保持了靜态資料。這是一個96位元組,0初始化的資料塊,該塊由C庫建立。在C庫初始化期間可以用來當做臨時棧。再調用 __user_initial_stackheap 使用者的初始化堆棧函數,實作使用者的堆棧的配置,調用 _fp_init 和 __rt_fp_status_addr (C庫函數) 兩個函數調用實作浮點運算的支援。

3.程式的跳轉,進入main()函數:

跳轉進入使用者的main函數

注意:

未初始化和初始值為零的全局變量,靜态變量一般在RAM中, 初始值不為零的全局變量/靜态變量 一般在FLASH中。

因為Flash不能随機寫(隻能寫0,不能寫1),是以一般會在程式運作之前将初始值不為零的全局變量重定位到RAM中。

全局變量和常量的位址在編譯時都已經被配置設定好了(是以能夠在 .map 檔案中看到), 而局部變量則是程式運作時在棧中建立的,棧空間大小可以在IDE中設定。

單片機啟動時,不需要用将代碼從ROM搬移到RAM ,而 ARM 則需要。單片機程式執行的過程分三個步驟:取執行->分析指令->執行指令。取指令的任務是:根據 PC的值從程式存儲器讀出指令,送到指令寄存器。然後分析執行執行。這樣單片機就從内部程式存儲器去代碼指令,從 RAM 存取相關資料。要知道RAM取數的速度是遠高于ROM的,但是單片機因為本身運作頻率不高,是以從ROM取指令慢并不影響。而ARM不同,CPU運作的頻率高,遠大于從 ROM讀寫的速度,是以一般有大型的作業系統時,都需要将代碼部分拷貝到RAM中再執行。

————————————————

版權聲明:本文為CSDN部落客「GWen9」的原創文章,遵循CC 4.0 BY-SA版權協定,轉載請附上原文出處連結及本聲明。

原文連結:https://blog.csdn.net/aa386447225/article/details/109546384