在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
电子发烧友
开通电子发烧友VIP会员 尊享10大特权
海量资料免费下载
精品直播免费看
优质内容免费畅学
课程9折专享价
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Cortex-M3精通之路-1(匯編啟動文件)

云深之無跡 ? 來源:云深之無跡 ? 2023-05-28 11:32 ? 次閱讀

源碼分析寫了一堆,但是每次都分析不完就鴿了,氣死我了。也思考了很久究竟寫一些怎么樣的文章。

我想通了,要寫一個合集,精通Cortex-M3!

基本上你加上幾個實戰的項目就穩了。

其次就是廣泛性,M3會了,M0難嗎?M0+呢?不言而喻了。我準備從一個工程入手,一行一行的代碼,多方權威資料查詢寫出這個合集,首先這篇是匯編的啟動文件,要求對讀者的要求是較高的,建議細細閱讀。

精通ARM Cortex-M意味著對ARM Cortex-M內核及相關微控制器有深入全面的理解和高超的開發技能。具體需要:

1. 深入理解ARM Cortex-M內核架構,包括核心模塊如內存保護單元、中斷控制器定時器等,以及指令系統和編程模型。

2. 熟悉各系列ARM Cortex-M內核如M0/M3/M4/M7的具體特征與差異,尤其是外設配置與編程方法。

3.掌握主流MCU廠商基于ARMCortex-M內核的微控制器產品,如STM32、Kinetis、EFM32等的詳細特性和使用方法。

4. 精通CMSIS標準,熟練使用其提供的寄存器訪問函數、外設驅動、啟動文件、調試工具等,快速開發MCU應用程序。

5. 具有豐富的開發經驗,能夠熟練完成MCU資源配置、時鐘選擇、中斷服務程序編寫、外設驅動開發等,并對各種復雜問題進行快速解決。

6. 具備一定的創新能力,能基于對內核和MCU的深入理解進行功能優化與改進,開發出技術水平更高的程序。

ARM官網找的圖

6b0e107e-fcc0-11ed-90ce-dac502259ad0.png

這個就是M3內核的特性

6b2aec12-fcc0-11ed-90ce-dac502259ad0.png

一個和別的內核的對比圖

6b59fe9e-fcc0-11ed-90ce-dac502259ad0.png

Corstone-101,官網看見的

我真服了,全網找不到這個開發板啥樣子??

Corstone-101是Arm推出的一款入門級的MCU開發板,目的是幫助工程師輕松學習 Arm Cortex-M系列MCU。

Corstone-101開發板的主要特性:-

- MCU:采用Arm Cortex-M3內核的STM32F103RB芯片

-集成CMSIS-DAP debug probe,支持無縫調試- 板載各類外設和接口:LCD顯示屏、LED、按鍵、USB轉串口、MicroSD卡槽等

- Arm Mbed OS預裝,可直接進行MCU軟件開發

-免費開源的課程教材,包括從零開始快速入門stm32的開發環境搭建,到深入學習stm32的定時器、串口、DMA等。

-低成本,入門友好,非常適合Cortex-M內核和MCU開發入門Corstone-101的主要特點是:

1)集成度高:MCU、Debug probe、豐富外設、Mbed OS軟件平臺均集成在一塊開發板上,使用非常方便。

2)教學優勢:提供極為詳盡的中文視頻教程和文檔,可以零基礎快速學習stm32和MCU開發。

3) 低成本:Corstone-101開發板定價非常低,大約250元人民幣,非常適合入門體驗。

4) Mbed OS支持:基于Mbed OS,可以使用其豐富的庫函數包以及在線編譯環境快速開發應用程序,降低學習門檻。

250這個價格,我還是覺得不夠低成本哈。

6b745122-fcc0-11ed-90ce-dac502259ad0.png

倒是和我說的差不多

本來今天就是要說這個CMSIS的,但是這個已經是抽象的高層了,所以先寫匯編。

CMSIS是ARM Cortex-M系列內核的設備抽象層,全稱為Cortex Microcontroller Software Interface Standard。它定義了一套標準的軟件接口,用于開發基于ARM Cortex-M內核的MCU設備的固件。主要包含:

- 設備寄存器及外設訪問函數庫:提供標準的API訪問MCU內核寄存器和外設,屏蔽不同芯片的寄存器差異。

- 啟動文件和鏈接腳本:提供標準的MCU啟動和內存布局文件,用于引導程序運行和鏈接。

- 中斷服務例程:提供標準的中斷服務例程入口定義,用于編寫設備特定的中斷服務程序。

- 調試寄存器訪問宏:標準宏定義訪問用于MCU調試的寄存器。

- 標準外設驅動庫:為常用外設提供跨MCU通用的驅動庫,如GPIO、USART等。

CMSIS的主要目的是降低基于Cortex-M內核MCU的開發復雜度,屏蔽芯片差異,實現跨MCU的代碼可重用性。

開發者可以專注于應用程序本身的開發,而不需過多關注具體MCU的配置細節。目前,主流的MCU廠商如ST、NXP、TI等都提供了基于CMSIS標準的固件庫和例程,使用這些CMSIS標準的固件庫可以輕松配置和訪問MCU資源,開發出高質量的程序。

CMSIS本質上是一個標準的固件庫和軟件接口。它提供:

1. 標準的寄存器外設訪問函數庫,相當于一個硬件抽象層,屏蔽不同MCU的寄存器差異,實現統一的訪問接口。

2. 標準的啟動文件、鏈接腳本和外設驅動,支持MCU啟動、內存配置和常用外設的使用。

3. 標準的中斷服務例程入口定義和調試宏定義。

4. 其他工具和示例程序等。

所以,CMSIS為MCU開發提供了一系列標準化和統一的固件基礎設施,開發者可以直接基于CMSIS開發應用程序,而不需過多關注MCU本身的配置細節。這大大降低了基于ARM Cortex-M內核MCU開發的難度,實現了跨MCU的代碼可重用性。但是,CMSIS本身并不是一個完整的MCU固件庫。它僅定義了一系列標準,提供基本的訪問接口和工具,但具體的 periperal drivers(外設驅動)和board support package(板級支持包)還需要芯片廠商及開發板廠商基于CMSIS標準開發和提供。所以,CMSIS給MCU開發帶來的主要價值在于:

1. 標準化:定義一套行業標準的軟件接口和規范。

2. 抽象化:提供硬件抽象層,屏蔽MCU差異,實現統一編程模型。

3. 可重用性:基于標準接口開發的代碼可以重用于不同的MCU,降低開發成本。

ARM Cortex-M3內核適合使用CMSIS版本3.0及以上版本。CMSIS最初發布于2008年,當時的版本為1.0,主要支持ARM Cortex-M0和M3內核。隨著CMSIS標準的不斷發展和Cortex-M內核系列的增加,CMSIS也在發布新版本以支持更多內核和增加更多功能。主要版本發布情況如下:

- CMSIS 1.0:2008年,支持Cortex-M0和M3內核。

- CMSIS 2.0:2010年,增加對Cortex-M4內核的支持。- CMSIS 3.0:2012年,統一設備抽象層,增加debug和RTOS功能。

- CMSIS 3.1:2013年,添加CMSIS-DAP接口和新增Cortex-M7內核支持。- CMSIS 3.2:2014年,新增配置 wizard 工具和ARM Compiler 6支持。

- CMSIS 5.0:2016年,重構代碼結構,支持基于CMSIS-Core的Middleware組件。

- CMSIS 5.1:2017年,新增CMSIS-Driver概念,擴充外設驅動支持。所以,對于ARM Cortex-M3內核,CMSIS 3.0版本及以后版本均有很好的支持,提供設備抽象層、debug工具、標準外設驅動等,可以很好地滿足基于Cortex-M3內核MCU開發的需求。

6ba33226-fcc0-11ed-90ce-dac502259ad0.png

我們來學學32里面的CMSIS里面的啟動文件

startup_stm32f10x_cl.s是STM32F10x系列MCU的啟動文件,由ARM官方提供。啟動文件主要完成以下工作:

1. 設置堆棧指針(MSP),為中斷服務程序和中斷向量表預留堆棧空間。

2.檢查復位源,并清除相應的復位標志。

3.設置中斷向量表偏移地址,指向__Vectors段。

4.如果需要,設置中斷向量表在RAM中的拷貝。

5. 如果需要,設置FPU寄存器為默認值。

6. 調用SystemInit()函數初始化系統時鐘和外設。

7.調用__main()函數進入C代碼。啟動文件一般由匯編語言編寫,需要做的工作主要是CPU及外設的最低層初始化配置,為后續C代碼的運行做好準備。

6bba22ec-fcc0-11ed-90ce-dac502259ad0.png

看這個代碼

Stack_SizeEQU0x00000400

這行定義了堆棧的大小為0x400字節,即1024字節。

AREA STACK, NOINIT, READWRITE, ALIGN=3

這行指令定義了一個名為STACK的無初值段,屬性為可讀寫,并且地址對齊到4字節。這個段用于定義堆棧空間。

Stack_Mem SPACE Stack_Size

這行在STACK段內分配了0x400字節的存儲空間,作為MCU的堆棧空間。

__initial_sp

這行將當前位置的值定義為__initial_sp標號,該標號的值為堆棧底地址,即堆棧指針SP的初始值。

所以,這段匯編代碼完成了以下工作:

1. 定義了1024字節的堆棧存儲空間Stack_Mem。

2.使用__initial_sp標號來指向這個堆棧空間的底部,這就是SP寄存器的復位值。

3.啟動文件會將MSP(主堆棧指針)初始化為__initial_sp的值,這樣堆棧空間Stack_Mem就關聯到主堆棧,為中斷服務程序的執行做好準備。

4.該堆棧空間也會在復位后由C代碼繼續使用,以存儲函數調用的返回信息等。

該段代碼定義的堆棧空間Stack_Mem所在的區域是MCU的內存空間。

MCU的內存空間一般可以分為以下幾個區域:

1.FLASH:程序存儲區域,用于存儲程序代碼

2.SRAM:靜態RAM,用于數據存儲和堆棧空間。

3.HEAP:動態內存分配區域,一般也在SRAM中,用于malloc/free管理。

4.STACK:函數調用堆棧空間,用于存儲函數調用的返回地址、參數等信息,一般在SRAM頂部。

所以,對于Startup文件來說,在SRAM中預留一段空間作為堆棧區域,這個區域就是用來作為中斷服務程序和系統調用等的堆棧空間,用于保存CPU運行現場等信息,這個區域就是我們這段代碼定義的Stack_Mem。

具體來說:

AREA STACK, NOINIT, READWRITE, ALIGN=3

這行定義了一個名為STACK的段,該段位于SRAM中,用于定義堆棧空間。

Stack_Mem SPACE Stack_Size

這行在該STACK段內分配了Stack_Size字節的存儲空間,作為MCU的堆棧空間。

__initial_sp 該標號的值為這個Stack_Mem的底部地址,即堆棧指針SP的初值。所以,總結來說:

1.該段代碼定義的堆棧空間Stack_Mem位于MCU的內存空間SRAM中。

2.它為中斷服務程序和函數調用預留了一片存儲區域。

3.這個存儲區域的底部地址被__initial_sp標號指向,用于初始化SP寄存器的值。

4.然后SP寄存器(堆棧指針)在程序運行過程中會根據推棧和出棧操作在這個區域中上移和下移。

堆棧底地址指的是堆棧空間的最低可用地址,它表示堆棧區域中最先分配的空間,用于存放最早推入堆棧的數據。

對于Startup文件來說,它使用__initial_sp標號來標記堆棧底地址,這就是SP寄存器(堆棧指針)的初始值。

具體代碼如下:

AREA STACK, NOINIT, READWRITE, ALIGN=3

Stack_Mem SPACE Stack_Size __initial_sp

這段代碼:

1. 定義了大小為Stack_Size的堆棧空間Stack_Mem。

2.__initial_sp標號的值指向Stack_Mem的最低地址,這就是堆棧底地址。3.啟動文件會將SP寄存器初始化為__initial_sp的值,使其指向堆棧底,以便后續的推棧操作。

4.SP寄存器的值會隨著推棧和出棧操作在Stack_Mem區域內增大和減小。所以,對這個堆棧空間來說:

1.堆棧底地址由__initial_sp標號的值決定,它指向Stack_Mem的最低可用地址。

2.SP寄存器初值為__initial_sp,會從堆棧底開始向高地址生長,來保存推入堆棧的數據。

3.出棧操作會使SP寄存器的值減小,釋放堆棧頂的數據,直到恢復到堆棧底。

4.堆棧空間的使用是從底至頂,底部空間存放最早的數據。所以,總結來說,堆棧底地址就是堆棧空間中最先被使用的那部分地址空間,它標記了堆棧區域的起始,為堆棧的推棧和出棧操作提供基礎。

6bf08f4e-fcc0-11ed-90ce-dac502259ad0.png

堆!

這段代碼定義了MCU的堆空間以及相關標號。

具體分析下

Heap_Size EQU 0x00000200

該行定義了堆大小為512字節(0x200)

AREA HEAP, NOINIT, READWRITE, ALIGN=3

該行指令定義一個名為HEAP的無初值段,具有讀寫屬性,地址按4字節對齊。這個段用于定義堆空間。

__heap_base 該標號的值為HEAP段的起始地址,表示堆底地址。

Heap_Mem SPACE Heap_Size

該行在HEAP段中分配了0x200字節的存儲空間作為MCU的堆空間。

__heap_limit

該標號的值為HEAP段結束地址,表示堆頂地址。

所以,這段匯編代碼完成了以下工作:

1. 定義了512字節的堆存儲空間Heap_Mem。

2. 使用__heap_base標號來標記這個堆空間的起始地址,表示堆底。

3. 使用__heap_limit標號來標記這個堆空間的結束地址,表示堆頂。

4. 該堆空間在程序運行過程中由malloc/free管理動態內存的分配與釋放。5. C語言中可以通過extern聲明__heap_base和__heap_limit,以獲取堆信息。

6. PRESERVE8指令用于強制8字節對齊。

THUMB指令指定該代碼區使用Thumb-2指令。

該段代碼定義的堆空間為MCU動態內存管理提供了存儲區域與邊界標記。通過__heap_base和__heap_limit,C代碼可以方便獲取到這個堆空間的信息,為malloc/free調用服務。所以,這段匯編代碼對MCU的動態內存分配起到很關鍵的作業。

這里說下如何訪問棧空間。

對于啟動文件定義的堆棧空間Stack_Mem來說,如果要在C代碼中訪問和使用這個區域,有以下幾種方法:

1. 使用全局變量在Startup文件中定義:

Stack_Mem SPACE Stack_Size __initial_sp

然后在C代碼中使用全局變量聲明:

6c1f0b62-fcc0-11ed-90ce-dac502259ad0.png

這樣stack_ptr變量就指向了堆棧頂,可以通過stack_ptr訪問和修改堆棧空間中的數據。

2. 基址偏移Startup文件中Label Stack_Mem的地址為base_addr,則在C代碼中可以通過:

unsigned int stack_ptr = (unsigned int )(base_addr + Stack_Size -

4);

來得到堆棧頂指針,以訪問數據。需要知道Stack_Mem的絕對地址base_addr。

3. 定義指針數組在C代碼中聲明指針數:

unsigned int Stack[Stack_Size];然后stack_ptr = &Stack[Sta

ck_Size - 1];

這種方法需要在鏈接過程中正確設置該指針數組與Startup文件中Stack_Mem區域的關聯。

4. 借助寄存器因為Startup文件已經將SP寄存器初始化為__initial_sp,也就是Stack_Mem底部,所以我們可以通過SP寄存器讀寫這個區域:

6c31b1ae-fcc0-11ed-90ce-dac502259ad0.png

使用inline匯編直接通過SP寄存器讀寫堆棧空間。

好了,再說堆空間的作用:

堆空間主要用于MCU的動態內存分配,它為malloc()和free()函數調用提供內存池。具體來說,堆空間在MCU中的作用有:

1. 為動態內存申請提供存儲空間:在程序運行時,可以通過malloc()函數向堆空間申請任意大小的內存塊,以保存數據或創建對象。

2. 釋放不再使用的內存:通過free()函數可以將堆空間中的內存塊釋放,以便下次繼續使用。

3. 管理內存碎片:堆空間可以由malloc()函數自動管理內存碎片,試圖利用所有的內存空間。

4. 實現內存動態擴展:如果堆空間初始分配的空間不足,還可以在運行時通過sbrk()函數向操作系統申請更多內存空間。

5. C++程序的new和delete操作也依賴于堆空間,用于創建和銷毀對象。

所以,簡單來說,堆空間為動態內存分配提供內存池,它最大的作用在于:

1. 滿足運行時變化的內存需求,程序不確定對象到底占用多少內存空間,只能在運行時進行內存分配。

2. 實現堆內存的重復分配與釋放,節省內存空間,避免內存浪費。

3. C++程序可以在堆上創建對象,并使用new和delete進行內存管理。總之,堆空間為程序運行時的動態內存分配需求提供了支持,它的管理主要依賴malloc、free和new、delete函數。

堆棧的區別是什么?

堆空間和棧空間都是MCU內存空間的一部分,但二者有以下主要區別:

1. 生長方向不同:

堆空間:向高地址生長,使用malloc()函數申請內存,按需動態分配。

棧空間:向低地址生長,大小在編譯時確定,自動分配。

2. 申請方式不同:堆空間:使用malloc()和free()函數顯式申請和釋放內存。棧空間: FUN調用時自動分配和釋放,不需要顯式控制。

3. 生命周期不同:

堆空間:使用完畢須手動釋放,生命周期不確定,動態決定。

棧空間:函數調用結束自動釋放,生命周期與函數調用一致,編譯時決定。

4. 使用目的不同:

堆空間:用于動態內存分配,對象創建。

棧空間:用于存儲函數參數、局部變量、返回地址等,實現函數調用機制。

5. 空間大小不同:

堆空間:大小動態變化,按需分配。

棧空間:大小在編譯時確定,固定分配。

所以,總結來說:

堆空間為動態內存分配和對象創建提供能力,大小和生命周期不定,需要手工管理。

棧空間為函數調用機制提供自動分配和釋放的臨時存儲空間,大小和生命周期在編譯時決定,無需手工控制。

我直接回馬槍,堆空間如何訪問呢?

1. 使用全局變量
在啟動文件中定義:

6c5cf3a0-fcc0-11ed-90ce-dac502259ad0.png?

然后在C代碼中使用全局變量聲明:

6c7eb42c-fcc0-11ed-90ce-dac502259ad0.png
這樣heap_ptr變量就指向了堆底,通過它可以訪問堆空間中的數據。
2. 基址偏移
啟動文件中Label __heap_base和__heap_limit的地址分別為base_addr和limit_addr,則在C代碼中可以通過:

6c9b4510-fcc0-11ed-90ce-dac502259ad0.png

來訪問堆空間數據,offset為任意偏移量。需要知道__heap_base和__heap_limit的絕對地址。
3. 定義指針數組
在C代碼中聲明指針數組:

6cb17cfe-fcc0-11ed-90ce-dac502259ad0.png

然后heap_ptr = Heap;
這種方法需要在鏈接過程中正確設置該指針數組與啟動文件中Heap_Mem區域的關聯。
4. 借助malloc()函數
因為啟動文件已經定義了__heap_base和__heap_limit來標識堆空間的范圍,所以我們可以直接使用malloc()函數向這個區域申請內存:

6cc45f4a-fcc0-11ed-90ce-dac502259ad0.png

malloc()函數會在Heap_Mem區域分配size大小的內存,并返回其起始地址,通過ptr訪問這片內存。

6ce473fc-fcc0-11ed-90ce-dac502259ad0.png

接下來看這一段

這段代碼定義了STM32的矢量表。

矢量表包含了中斷服務程序的入口地址,以及系統使用的其他處理程序的入口地址。它位于地址0x00000000,并在復位后被映射到這個位置。所以,這段代碼完成的主要工作有:

1. 定義了名為RESET的只讀數據段,該段位置是0x00000000。

2. EXPORT指令導出了__Vectors、__Vectors_End和__Vectors_Size標號,以方便其他文件使用。

3. __Vectors標號指向矢量表的起始地址。__Vectors_End和__Vectors_Size分別用于標識矢量表的結束地址和大小。

4. 表起始地址存儲初始堆棧指針SP的值(__initial_sp標號)。

5. 接下來定義了各種異常和中斷處理程序的入口地址:

Reset_Handler:復位中斷服務程序

NMI_Handler:NMI中斷服務程序

HardFault_Handler:硬件故障中斷服務程序

......

SysTick_Handler:SysTick中斷服務程序

6. 一些入口保留為0,用于未來擴展。

所以,這個矢量表完成了以下工作:

1. 定義了各種異常和中斷處理程序的入口地址,這些入口地址指向對應中斷服務程序。

2. 矢量表位于地址0x00000000,并在MCU復位后被映射到這個位置。

3. 初始SP寄存器的值由矢量表的第一個字存儲。

4. 中斷發生時,MCU會根據中斷類型在矢量表中找到對應的入口地址,然后跳轉到此地址執行中斷服務程序。矢量表是MCU中斷機制的基礎,它為異常和中斷提供入口和服務程序。所以,這個矢量表定義對MCU的中斷配置和服務起到了基礎作用。

矢量表在MCU的內存空間中保存。它以數組的形式保存,每個數組元素存儲一個函數入口地址。

具體來說:

1. 這個矢量表定義在啟動文件中的RESET數據段中,該數據段位于內存地址0x00000000處。

2. __Vectors標號指向這個矢量表的數組起始地址,__Vectors_End標號指向數組結束地址。

3. 數組每個元素大小為4字節(32位MCU),用于存儲一個函數入口地址。

4. 數組第一個元素存儲了初始SP值,接下來的元素存儲各種異常和中斷處理程序的入口地址。

5. 當某個中斷發生時,MCU會計算出中斷類型對應的數組下標,然后跳轉到該下標元素所指向的入口地址執行中斷服務程序。

6. 這個數組實際上定義在編譯器產生的啟動文件匯編代碼中。然后通過鏈接器與其他文件連接,最終在MCU的內存空間布局中實現布局。

所以,總結來說:

1. 這個矢量表以數組的形式在MCU內存中實現,數組每個元素存儲一個入口地址。

2. 數組第一個元素存儲SP初值,其他元素存儲異常和中斷服務程序的入口地址。

3. 該數組位于地址0x00000000,在MCU上電復位后被映射到該位置。

4. 中斷發生時,通過中斷類型計算數組下標,獲取中斷服務程序入口地址。

5. 該數組最終通過鏈接過程存放在MCU內存的正確位置,并在程序運行時被使用。這種數組的形式便于通過索引快速查找對應的入口地址,實現MCU的異常和中斷機制。所以,矢量表采用這種數組結構實際上加快了中斷響應速度,提高了系統實時性。

6d2db9e0-fcc0-11ed-90ce-dac502259ad0.png

下面還有一段,其實和上面一樣,都是地址而已

6d868372-fcc0-11ed-90ce-dac502259ad0.png

接下來看這段

所有的單片機電腦,都會說復位這個事情。那它為什么如此的重要?因為我們為了可控,所有的初始化我們知道,運行的規律知道,所以復位是為了可控而已。

復位中斷服務程序(Reset Handler)之所以存在,主要是為了實現MCU的初始化配置和C語言程序的啟動。

具體來說,Reset Handler的作用有:

1. 完成MCU的初始化配置:設置時鐘,配置GPIO等,為程序正確運行做準備。這一般由啟動文件中定義的SystemInit函數完成。

2. 跳轉到C語言程序的入口函數__main:C語言程序的執行是從__main函數開始的,所以Reset Handler需要跳轉到__main函數。

3. 設置初始堆棧指針SP的值:在MCU上電復位后,SP寄存器的值是未知的,需要設置為一個正確的值,以便進行后續的堆棧操作。這個值通常存儲在矢量表的第一個元素中。

4. 作為MCU的最高優先級中斷服務程序:當MCU上電或復位時,首先會執行Reset Handler來完成系統的初始化和C語言程序的啟動。

5. 如果用戶定義了自己的Reset Handler,它將覆蓋啟動文件中的定義,用戶程序將從用戶定義的Reset Handler開始執行。

所以,簡單來說,Reset Handler之所以存在,主要是為了:

1. 完成MCU的初始化配置,為正確運行提供基礎。

2. 跳轉到C語言程序的入口,啟動程序執行。

3. 設置SP寄存器的初始值,為使用堆棧做準備。

4. 作為上電復位中斷服務程序,首先被執行。

5. 可以被用戶定義的Reset Handler覆蓋。如果沒有Reset Handler,MCU將啟動在未初始化的狀態,C語言程序也無法得到執行,SP的值是未知的,這會導致程序運行錯誤或無法運行。所以,Reset Handler對MCU的啟動起到了基礎作用,它為C程序的執行提供了啟動條件和基本環境,是MCU上電必不可少的初始化代碼。

來,這次有了前置知識再看代碼:

代碼定義了復位中斷服務程序Reset_Handler。具體分析如下:

__Vectors_Size EQU __Vectors_End - __Vectors

這行指令計算了矢量表的大小,用于其他文件使用。

AREA |.text|, CODE, READONLY

這行指令定義了一個只讀的代碼段,用于存放異常和中斷服務程序。

Reset_Handler PROC

這行指令定義了Reset_Handler的過程,表示復位中斷服務程序的起始。

EXPORT Reset_Handler [WEAK]

此行導出Reset_Handler過程,以便其他文件使用,并指定該過程為弱定義,意味著如果用戶定義了自己的Reset_Handler過程,那么編譯器會使用用戶定義的過程,而忽略此處的定義。

6d9fd0ca-fcc0-11ed-90ce-dac502259ad0.png

看這個

IMPORTSystemInit

此行導入SystemInit函數,表示Reset_Handler過程調用該函數。

IMPORT __main

此行導入C語言程序的入口函數__main。

LDR R0, =SystemInit

此指令將SystemInit函數地址加載到R0寄存器。

BLX R0

此指令調用SystemInit函數。

LDR R0, =__main

此指令將__main函數地址加載到R0寄存器。

BX R0

此指令跳轉到__main函數,執行C語言程序。

ENDP

此行指令表示Reset_Handler過程的結束。

所以,這個Reset_Handler過程完成了以下工作:

1. 調用SystemInit函數完成系統初始化。

2. 跳轉到__main函數,執行C語言程序。

3. 作為復位中斷的中斷服務程序,在MCU上電復位后首先被執行。

4. 如果用戶定義了自己的Reset_Handler,那么由于該過程被定義為弱定義,用戶定義會覆蓋此處定義,被執行。該過程為C語言程序的執行提供了啟動入口,完成了MCU的初始化配置.

6dc9daaa-fcc0-11ed-90ce-dac502259ad0.png

接下來的代碼很多的一樣,拿兩個看

這段代碼定義了NMI中斷服務程序NMI_Handler和硬件故障中斷服務程序HardFault_Handler。

NMI_Handler PROC

此行定義NMI_Handler過程,表示NMI中斷服務程序的起始。

EXPORT NMI_Handler [WEAK]

此行導出NMI_Handler過程,指定為弱定義,意味著如果用戶定義了自己的NMI_Handler過程,編譯器會使用用戶定義的過程,忽略此處定義。

B . 此指令是一個空操作,不執行任何動作。

ENDP

此行表示NMI_Handler過程的結束。

HardFault_HandlerPROC

此行定義HardFault_Handler過程,表示硬件故障中斷服務程序的起始。EXPORT HardFault_Handler [WEAK]

此行導出HardFault_Handler過程,指定為弱定義,意味著如果用戶定義了自己的HardFault_Handler過程,編譯器會使用用戶定義的過程,忽略此處定義。

B . 此指令是一個空操作,不執行任何動作。

ENDP 此行表示HardFault_Handler過程的結束。

所以,這兩個中斷服務程序定義完成了:

1. 定義了對應的中斷服務程序入口,但程序體為空。

2. 將這兩個過程定義為弱定義,意味著如果用戶定義了自己的服務程序,編譯器會使用用戶定義的過程。

3. 以空操作結束了這兩個過程。

之所以這兩個過程定義為空,主要是考慮到:

1. 出于兼容性考慮,啟動文件需要定義所有標準的異常與中斷入口,但具體處理由用戶決定是否定義。

2. 對于一些使用不太頻繁或處理比較復雜的中斷,用戶可能按需選擇是否定義自己的服務程序。

3. 定義為空的中斷服務程序不會對程序產生任何影響,保證定義后的兼容性。

所以,這兩個定義主要是為了實現異常與中斷入口的完整定義,但具體處理交由用戶根據需要選擇是否定義,如果未定義則默認為空操作。

6de2a4a4-fcc0-11ed-90ce-dac502259ad0.png

今日最后的代碼,我覺得全網我是最全的啟動代碼解析

這段代碼主要用于定義堆棧的初始化。具體分析如下:

IF __MICROLIB

此行條件指令檢查__MICROLIB宏是否被定義。如果定義,執行IF內語句,否則執行ELSE內語句。

EXPORT __initial_sp

此行導出__initial_sp符號,用于初始化SP寄存器。

EXPORT __heap_base

此行導出__heap_base符號,用于標識堆起始地址。

EXPORT __heap_limit

此行導出__heap_limit符號,用于標識堆結束地址。

IMPORT __use_two_region_memory

此行導入__use_two_region_memory符號,用于檢查是否使用兩片內存區域。

__user_initial_stackheap 此符號用于標識初始化堆棧的過程。

LDR R0, = Heap_Mem 此指令將堆起始地址加載到R0寄存器。

這幾行指令完成堆起始地址,堆結束地址,棧起始地址以及棧結束地址的加載與設置。

BX LR此指令用于返回,結束初始化堆棧的過程。

ALIGN此行用于4字節對齊。

ENDIF 此行表示IF語句的結束。

END 此行表示文件的結束。

所以,這段代碼的主要工作是:

1. 如果定義了__MICROLIB宏,則直接導出__initial_sp等符號,否則執行初始化堆棧的過程。

2. __user_initial_stackheap過程用于根據傳入的Heap_Mem和Stack_Mem參數設置堆棧的參數。

3. 該過程判斷是否使用兩片區域設置堆棧參數,如果使用則設置兩片區域的起始結束地址。

4. 這段代碼的目的是在啟動文件中設置初始化堆棧所需要的參數與地址,為C語言程序的執行提供堆棧環境。這段代碼的定義為用戶編寫的C語言程序提供了基本的堆棧初始化與設置,完成了與編譯器相關的參數定義,實現了C語言程序運行所需要的內存環境配置。

我再總結一下:

1. 定義了RESET只讀數據段,用于存放矢量表,該段位于地址0x00000000,在上電復位后被映射到該地址。

2. 定義并導出了__Vectors、__Vectors_End和__Vectors_Size等符號,方便其他文件使用矢量表。

3. 矢量表首地址存儲初始SP值,其他地址存儲各種異常和中斷服務程序入口。

4. 定義了復位中斷服務程序Reset_Handler,它調用SystemInit完成系統初始化,然后跳轉到__main函數執行C語言程序。

5. 定義了部分中斷服務程序如NMI_Handler和HardFault_Handler,但僅定義了入口,程序體為空,這主要考慮程序的兼容性與擴展性。

6. 根據__MICROLIB的定義情況,選擇是否執行__user_initial_stackheap過程來設置堆棧參數,為C語言程序執行提供堆棧環境。

7. 定義并導出了__initial_sp、__heap_base和__heap_limit等符號,用于標識SP、堆和棧的起始和結束地址。

8. 使用ALIGN指令實現了部分字節對齊,提高了程序的兼容性和效率。

9. 文件最后通過END指令實現了文件的結束。

所以,這個啟動文件完成的主要工作是:

1. 完成MCU的復位向量表定義,為各種中斷提供入口與服務。

2. 定義復位中斷服務程序,完成系統初始化和C語言程序啟動。

3. 定義部分中斷服務程序入口,但程序體為空,由用戶決定是否具體定義。

4. 根據情況設置堆棧參數,為C語言程序執行提供環境。

5. 定義各種導出符號,方便其他文件使用。

6. 使用ALIGN實現部分字節對齊,提高效率。7. 標識文件的結束。

頻繁的說這個對齊,我補個對齊的作用:

字節對齊指的是存儲單元的地址要遵循某種邊界限制,即地址的低幾位要為0。

它的主要作用有:

1. 提高訪問效率:當數據的地址是某種邊界的整數倍時,CPU可以以更大寬度的訪問單元去訪問數據,這樣可以減少CPU讀取數據的次數,提高訪問效率。2. 減少存儲空間浪費:如果不對齊,在訪問更大寬度的數據類型時,CPU需要訪問的數據可能超出實際需要,這會占用額外的存儲空間并影響總線帶寬,對齊可以避免這種浪費。

3. 方便數據的轉換:在某些特定的地址處訪問的數據可以直接轉換為其他數據類型,這可以提高處理效率,如果地址未對齊,這種直接轉換就無法實現。

總的來說,字節對齊主要帶來三個方面的好處:

1. 提高訪問和處理數據的效率。

2. 避免存儲空間的浪費。

3. 方便數據之間的直接轉換。

而對齊的基本實現方法是:

1. 指定對齊類型:如2字節對齊、4字節對齊等,這通常由編譯器來指定,可以通過定義宏的方式實現。

2. 數據聲明時通過對齊類型進行限定:如int __align(4) num;表示num為4字節對齊。

3. 在某些關鍵數據聲明前通過專用指令進行手動對齊:如ALIGN 4對下一數據進行4字節對齊。

4. 編譯器會自動選擇合適的對齊方式來對數據進行對齊,通常為最大數據類型的長度,這樣可以發揮對齊帶來的所有好處。

6e0ba05c-fcc0-11ed-90ce-dac502259ad0.png

這幾個文件詳細的作用我之后的文章來說

core_cm3.c是CMSIS標準定義的Cortex-M3內核芯片支持包,它提供了以下內容:

1. MCU寄存器結構體定義:SCB, SysTick, NVIC等內核寄存器的結構體定義。

2. 系統時鐘配置函數:CMU_ClkInit()、CMU_ClockSelectConfig()等。

3. 系統滴答配置函數:SysTick_Config()用于配置SysTick定時器的溢出中斷周期。

4. 中斷配置函數:NVIC_EnableIRQ()、NVIC_DisableIRQ()、NVIC_SetPriority()等,用于配置中斷優先級與使能狀態。

5. 系統SoftReset和HardReset函數:用于軟復位和硬復位MCU。

6. 內部Flash配置函數:用于配置Flash預取指緩存、等待信號以及訪問權限等。

7. 函數調用棧配置函數:用于配置PSP堆棧指針和MSP主堆棧指針。

8. 系統滴答定時器SysTick的中斷服務函數SysTick_Handler()。

9. 設備向量表__Vectors定義,里面包含SysTick和外設中斷服務程序入口,以及各類異常入口。

10. 用戶可根據需要實現的空操作清零函數:Default_Handler()。

11. MPU配置函數:用于配置內存保護單元,設定不同存儲區域的訪問權限。 所以,core_cm3.c文件為基于Cortex-M3內核的MCU提供了系統級配置與接口,包括時鐘、中斷、內存管理等方方面面。用戶可以直接調用這些函數接口來配置MCU,也可以根據需要修改或擴充。這個文件遵循CMSIS標準,具有很高的通用性,主要目的是降低不同MCU的移植difficulty,加速嵌入式工程師的開發工作。所以,它是開發基于ARM Cortex-M3內核MCU的重要基石。

stm32f10x.h是ST公司為STM32F10x系列MCU提供的頂層頭文件,它包含以下內容:

1. 對標準庫的引用,如stdint.h、stdbool.h等。

2. 對CMSIS標準的引用,引入CMSIS定義的一些數據類型、寄存器定義和函數原型。

3. MCU型號選擇,根據具體的芯片型號選擇正確的定義。

4. 外設時鐘使能宏定義,方便開啟或關閉外設時鐘。

5. 寄存器定義,定義MCU所有的內核寄存器和外設寄存器結構體。

6. 中斷編號定義,定義所有的中斷源所對應的值。

7. 位帶操作宏定義,提供對寄存器位的設置、清除以及翻轉等操作。

8. FLASH和OTP操作函數,提供FLASH讀、寫、擦除以及OTP讀寫的函數原型。

9. 復位和時鐘控制寄存器地址的定義以及函數原型。

10. GPIO通用IO口操作函數原型定義。

11. 中斷和事件管理函數原型定義,NVIC相關操作函數。

12. 其他外設(串口、SPI、I2CADC等)配置函數和外設特有的一些定義。所以,stm32f10x.h作為STM32F10x系列MCU的頂層頭文件,它包含了影響MCU所有外設和內核的內容,包括但不限于:寄存器定義、中斷定義、位帶操作、復位和時鐘配置、GPIO配置、串口配置等等。開發人員可以直接include這個頭文件,就可以使用里面定義的東西,非常方便。

system_stm32f10x.c文件主要包含STM32F10x系列MCU的系統時鐘配置函數。系統時鐘配置主要完成以下工作:

1. 根據外部晶振的頻率,配置PLL時鐘進行倍頻,得到MCU內核時鐘和AHB/APB總線時鐘。

2. 配置HSE(高速外部時鐘)作為系統時鐘,或者作為PLL的時鐘源。

3. 配置LSE(低速外部時鐘)作為RTC的時鐘源。

4. 選擇不同的預分頻因子,以滿足系統時鐘等于內核時鐘的要求。

5. 配置各總線時鐘AHB, APB1和APB2的預分頻因子。

6. 根據配置的總線時鐘頻率,配置外設的時鐘。

7. 根據CPU工作頻率,設置Flash訪問時間 FlashLatency。

所以,system_stm32f10x.c文件主要通過調用stm32f10x_clk.c和stm32f10x_rcc.c等文件里的函數,來配置MCU的整個時鐘系統,包括選擇PLL時鐘源、PLL倍頻系數、AHB/APB總線分頻系數以及外設時鐘使能等。這個文件實現了ClockConfiguration()函數,該函數會在MCU啟動時被startup_stm32f10x_xx.s的SystemInit()調用,從而完成MCU系統時鐘的初始化配置。

https://developer.arm.com/Processors/Cortex-M3
https://github.com/ARM-software/CMSIS-Drive


聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • ARM
    ARM
    +關注

    關注

    134

    文章

    9242

    瀏覽量

    372208
  • Cortex
    +關注

    關注

    2

    文章

    203

    瀏覽量

    46877
  • 源碼
    +關注

    關注

    8

    文章

    662

    瀏覽量

    29918

原文標題:Cortex-M3精通之路-1(匯編啟動文件)

文章出處:【微信號:TT1827652464,微信公眾號:云深之無跡】歡迎添加關注!文章轉載請注明出處。

收藏 0人收藏

    評論

    相關推薦

    STM32的上電啟動過程分享

    main函數之間的這段時間都做了什么。第一步是硬件設置SP、PC 我們參考《Cortex-M3權威指南》向量表章節表7.6,如下圖所示: 上電后的向量表 前兩段地址主要是用來指定SP和PC的初值,上一節
    發表于 03-07 08:09

    具有大型嵌入式SRAM,用于一般MCU應用程序的指紋芯片-P1032BF1

    P1032BF1是一款基于ARM Cortex-M3的單片機,專為Wi-Fi /藍牙通信控制而設計;能夠實現指紋的圖像采集、特征提取、特征比對,可應用于智能鎖;支持大型程序代碼和擁有大型嵌入式SRAM,也可用于一般的MCU應用。
    的頭像 發表于 03-04 09:27 ?199次閱讀

    RK3562J 處理器 M啟動實操

      一、RK3562J處理器概述   RK3562J處理器是一款高性能、多核心的處理器,采用了獨特的異構架構設計。它集成了4個Cortex-A53核心和1Cortex-M0核心,其中4個
    發表于 02-27 08:59

    MHMF012L1D4M-MINAS A6 系列 Block動作應用說明資料 -Modbus啟動- 松下

    電子發燒友網為你提供Panasonic(Panasonic)MHMF012L1D4M-MINAS A6 系列 Block動作應用說明資料 -Modbus啟動-相關產品參數、數據手冊,更有
    發表于 02-21 19:14
    MHMF012L<b class='flag-5'>1D4M</b>-MINAS A6 系列 Block動作應用說明資料 -Modbus<b class='flag-5'>啟動</b>- 松下

    Cortex-M3/M4F指令集技術用戶手冊

    電子發燒友網站提供《Cortex-M3/M4F指令集技術用戶手冊.pdf》資料免費下載
    發表于 12-23 16:31 ?5次下載
    <b class='flag-5'>Cortex-M3</b>/<b class='flag-5'>M</b>4F指令集技術用戶手冊

    如何使用Ozone分析Cortex-M異常

    Ozone可以幫助用戶快速分析和查找導致CPU故障的軟件bug。本文解釋如何使用Ozone的調試功能,深入了解Cortex-M架構上的這些錯誤。
    的頭像 發表于 11-29 11:14 ?1237次閱讀
    如何使用Ozone分析<b class='flag-5'>Cortex-M</b>異常

    適用于低功耗和無線通信距離要求較高應用的智能通信模組-RF-SM-1077B1

    RF-SM-1077B1是RF-star推出的Sub-1G系列模塊,其芯片CC1310內置高性能的ARM Cortex-M3 + ARM Cortex-M0雙核處理器,主MCU ARM
    的頭像 發表于 10-24 09:37 ?405次閱讀
    適用于低功耗和無線通信距離要求較高應用的智能通信模組-RF-SM-1077B<b class='flag-5'>1</b>

    基于ARM Cortex-M3單片機研發的國產指紋芯片 - P1032BF1

    指紋芯片 - P1032BF1是一款基于ARM Cortex-M3的單片機,專為Wi-Fi /藍牙通信控制而設計;可應用于智能鎖;支持大型程序代碼和擁有大型嵌入式SRAM,也可用于一般的MCU應用。
    的頭像 發表于 07-10 09:22 ?822次閱讀
    基于ARM <b class='flag-5'>Cortex-M3</b>單片機研發的國產指紋芯片 - P1032BF<b class='flag-5'>1</b>

    FSP庫啟動文件說明

    FSP庫啟動文件說明
    的頭像 發表于 06-04 08:06 ?695次閱讀
    FSP庫<b class='flag-5'>啟動</b><b class='flag-5'>文件</b>說明

    GD32F103xxArm?Cortex?-M3 32位MCU芯片手冊

    電子發燒友網站提供《GD32F103xxArm?Cortex?-M3 32位MCU芯片手冊.pdf》資料免費下載
    發表于 05-22 14:13 ?4次下載

    請問stm32的cortex-m0怎么樣?

    為什么都說STM32專業做M3 ,M4,M0要找芯唐和NXP,難道因為ST的CORTEX-M0芯片不穩定還是有其他問題?
    發表于 05-15 06:31

    求助,關于cortex-M3的壓棧問題求解

    我們都知道cortex-m3中斷時是硬件自動壓棧的,這樣可以減少中斷響應和恢復時間。中斷硬件壓棧的寄存器為xPSR, PC, LR, R12, R0-R3,為什么其他寄存器不需要壓棧呢?
    發表于 04-28 08:18

    請問cortex-M7核單片機主要應用在哪些領域?

    看到st和nxp的M7核單片機,動不動幾百兆的主頻,有的還要外置DDR,還有的成本低于1刀。想知道這些單片機的目標應用場合是哪些? 單片機發展,從51過渡到cortex m3,現在又要
    發表于 04-17 07:49

    STM32F103是如何知道要從啟動文件開始執行的?

    STM32F103芯片是如何知道要從startup_stm32f10x_hd.s啟動文件開始執行的?為什么不是從其他的文件開始執行的?如果我的項目中有多個匯編
    發表于 04-08 07:56

    如何用Keil生成bin、匯編、C與匯編混合文件

    用Keil生成bin、匯編、C與匯編混合文件
    發表于 04-08 06:17

    電子發燒友

    中國電子工程師最喜歡的網站

    • 2931785位工程師會員交流學習
    • 獲取您個性化的科技前沿技術信息
    • 參加活動獲取豐厚的禮品
    主站蜘蛛池模板: 奇米影视奇米色777欧美 | 奇米影视亚洲狠狠色777不卡 | 有码视频在线观看 | 午夜噜噜噜私人影院在线播放 | 狠狠色噜噜狠狠狠狠888奇米 | 亚洲国产一区二区在线 | 男女交性拍拍拍高清视频 | 一级做a爰片久久毛片一 | 日本黄色大片在线播放视频免费观看 | 在线免费影视 | 福利视频自拍偷拍 | 日本wwwxx| 久久久久久国产精品免费免 | 久综合色 | 欧美人与禽交 | 手机在线视频你懂的 | 亚洲综合香蕉 | 婷婷深爱网 | 成人在线播放av | freexxxx性欧美| 色网站免费视频 | 综合网 色天使 | 午夜免费 | 色在线视频观看 | 熊出没之环球大冒险旧版免费观看 | 2021国产精品自在拍在线播放 | 欧美成人鲁丝片在线观看 | 国产婷婷一区二区三区 | jizz性欧美12 | 日本不卡视频在线视频观看 | 天天躁天天狠天天透 | 女人张开腿让男人做爽爽 | 高清一区二区 | 日本免费一区二区三区视频 | 天堂网 | 亚洲欧洲一区二区三区在线 | 欧美福利一区 | 欧美男人天堂网 | 欧美婷婷六月丁香综合色 | 狠狠色狠色综合曰曰 | 熊出没之环球大冒险旧版免费观看 |