關於STM32的IAP在線升級的整理

升級的IAP 重要代碼解析請看這個文章:

一、擴展-IAP主要用於產品出廠後應用程序的更新作用,考慮到出廠時要先燒寫IAP  再燒寫APP應用程序要燒寫2次增加工人勞動力基礎上寫了“STM32 IAP+APP ==>雙劍合一”鏈接稍後發)

一、在進入主題之前我們先了解一些必要的基礎知識----stm32系列芯片的種類和型號

startup_stm32f10x_cl.s 互聯型的器件,STM32F105xx,STM32F107xx
startup_stm32f10x_hd.s 大容量的STM32F101xx,STM32F102xx,STM32F103xx
startup_stm32f10x_hd_vl.s 大容量的STM32F100xx
startup_stm32f10x_ld.s 小容量的STM32F101xx,STM32F102xx,STM32F103xx
startup_stm32f10x_ld_vl.s 小容量的STM32F100xx
startup_stm32f10x_md.s 中容量的STM32F101xx,STM32F102xx,STM32F103xx
startup_stm32f10x_md_vl.s 中容量的STM32F100xx  (我項目中用的是此款芯片 stm32f100CB)
startup_stm32f10x_xl.s FLASH在512K到1024K字節的STM32F101xx,STM32F102xx,STM32F103xx
(例如:像stm32f103re 這個型號的 芯片flash是512k 的, 啓動文件用startup_stm32f10x_xl.s  或者startup_stm32f10x_hd.s  都可以;)

cl:互聯型產品,stm32f105/107系列
vl:超值型產品,stm32f100系列
xl:超高密度產品,stm32f101/103系列
ld:低密度產品,FLASH小於64K
md:中等密度產品,FLASH=64 or 128
hd:高密度產品,FLASH大於128


二、在拿到ST公司官方的IAP 程序後 我們要思考幾點:

        1.ST 官方IAP是什麼針對什麼芯片型號的,我們要用的又是什麼芯片型號;

2.我們要用官方IAP適合我們芯片的程序升級使用,要在原有的基礎上做那些改變;

(我的資源裏有官方IAP源碼:http://download.csdn.net/detail/yx_l128125/6445811)

       

初略看了一下IAP源碼後,現在我們可以回答一下上面的2個問題了:

1.官網剛下載的IAP針對的是stm32f103c8芯片的,所以他的啓動代碼文件選擇的是 startup_stm32f10x_md.s,而我的芯片是stm32f100cb,所以我的啓動代碼文件選擇的是  startup_stm32f10x_md_lv.s 




          2 .第二個問題就是今天我們要做詳細分析才能回答的問題了;

          (1).知道了IAP官方源碼的芯片和我們要用芯片的差異,首先我們要在源碼的基礎上做芯片級的改動;

A.首先改變編譯器keil的芯片型號上我們要改成我們的芯片類型---STM32F100CB;

 B.在keil的options for  targer 選項C/C++/PREPROMCESSOR symbols的Define欄裏定義,把有關STM32F10X_MD的宏定義改成:STM32F10X_MD_VL

也可以在STM32F10X.H裏用宏定義
[plain] view plaincopy
  1.   /* Uncomment the line below according to the target STM32 device used in your  
  2.    application   
  3.   */  
  4.   
  5. #if !defined (STM32F10X_LD) && !defined (STM32F10X_LD_VL) && !defined (STM32F10X_MD) && !defined (STM32F10X_MD_VL) && !defined (STM32F10X_HD) && !defined (STM32F10X_HD_VL) && !defined (STM32F10X_XL) && !defined (STM32F10X_CL)   
  6.   /* #define STM32F10X_LD */    /*!< STM32F10X_LD: STM32 Low density devices */  
  7.   /* #define STM32F10X_LD_VL */ /*!< STM32F10X_LD_VL: STM32 Low density Value Line devices */    
  8.   /* #define STM32F10X_MD  */  /*!< STM32F10X_MD: STM32 Medium density devices */  
  9.    #define STM32F10X_MD_VL     /*!< STM32F10X_MD_VL: STM32 Medium density Value Line devices */    
  10.   /* #define STM32F10X_HD */    /*!< STM32F10X_HD: STM32 High density devices */  
  11.   /* #define STM32F10X_HD_VL */ /*!< STM32F10X_HD_VL: STM32 High density value line devices */    
  12.   /* #define STM32F10X_XL */    /*!< STM32F10X_XL: STM32 XL-density devices */  
  13.   /* #define STM32F10X_CL */    /*!< STM32F10X_CL: STM32 Connectivity line devices */  
  14. #endif  

上面代碼說的是如果沒有定義 STM32F10X_MD_VL, 則宏定義 STM32F10X_MD_VL

C.外部時鐘問價在stm32f10x.h  依據實際修改,原文是 說如果沒有宏定義外部時鐘HES_VALUE的值,但是宏定義了stm32f10x_cl 則外部時鐘設置爲25MHZ, 否則外部時鐘都設置爲8MHZ;  我用的外部晶振是8MHZ的所以不必修改這部分代碼;

[plain] view plaincopy
  1. #if !defined  HSE_VALUE  
  2.  #ifdef STM32F10X_CL     
  3.   #define HSE_VALUE    ((uint32_t)25000000) // Value of the External oscillator in Hz <pre name="code" class="plain"> #else </pre>  #define HSE_VALUE    ((uint32_t)8000000) //Value of the External oscillator in Hz  #endif /* STM32F10X_CL */#endif /* HSE_VALUE */  

D.做系統主頻時鐘的更改

system_stm32f10x.c的系統主頻率,依實際情況修改 ;我用的芯片主頻時鐘是24MHZ;
[plain] view plaincopy
  1.       
  2. #if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)  
  3. /* #define SYSCLK_FREQ_HSE    HSE_VALUE */  
  4.  #define SYSCLK_FREQ_24MHz  24000000  
  5. #else  
  6. /* #define SYSCLK_FREQ_HSE    HSE_VALUE */  
  7.  #define SYSCLK_FREQ_24MHz  24000000   
  8. /* #define SYSCLK_FREQ_36MHz  36000000 */  
  9. /* #define SYSCLK_FREQ_48MHz  48000000 */  
  10. /* #define SYSCLK_FREQ_56MHz  56000000 */  
  11. /*#define SYSCLK_FREQ_72MHz  72000000*/   
  12. #endif  
E.下面是關鍵部分操作了,在說這部分操作前我們先來說一下內存映射:
          下圖在stm32f100芯片手冊的29頁,我們只截取關鍵部分


從上圖我們看出幾個關鍵部分:

1.內部flash 是從0x0800 0000開始 到0x0801 FFFF  結束,    0x0801FFFF-0x0800 0000= 0x20000 =128k    128也就是flash的大小;

2.SRAM的開始地址是   0x2000 0000 ;

我們要把我們的在線升級程序IAP放到FLASH裏以0x0800 0000 開始的位置,   應用程序放APP放到以0x08003000開始的位置,中斷向量表也放在0x0800 3000開始的位置;如圖


所以我們需要先查看一下misc.h文件中的中斷向量表的初始位置宏定義爲  NVIC_VectTab_Flash  0x0800 0000

那麼要就要設置編譯器keil 中的  options  for target 的target選項中的 IROM1地址 爲0x0800 0000 大小爲 0x20000即128K;

                                                                                                   IRAM1地址爲0x2000 0000  大小爲0x2000;

(提示:這一項IROM1 地址 即爲當前程序下載到flash的地址的起始位置)

下面我們來分析一下修改後的IAP代碼:

[plain] view plaincopy
  1. /*******************************************************************************  
  2.   * @函數名稱   main  
  3.   * @函數說明   主函數  
  4.   * @輸入參數   無  
  5.   * @輸出參數   無  
  6.   * @返回參數   無  
  7. *******************************************************************************/  
  8. int main(void)  
  9. {  
  10.     //Flash 解鎖  
  11.     FLASH_Unlock();  
  12.   
  13.     //配置PA15管腳  
  14.     KEY_Configuration() ;  
  15.     //配置串口1  
  16.     IAP_Init();  
  17.     //PA15是否爲低電平  
  18.     if (GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_15)  == 0x00)  
  19.     {  
  20.           
  21.         //執行IAP驅動程序更新Flash程序  
  22.   
  23.         SerialPutString("\r\n======================================================================");  
  24.         SerialPutString("\r\n=              (C) COPYRIGHT 2011 Lierda                             =");  
  25.         SerialPutString("\r\n=                                                                    =");  
  26.         SerialPutString("\r\n=     In-Application Programming Application  (Version 1.0.0)        =");  
  27.         SerialPutString("\r\n=                                                                    =");  
  28.         SerialPutString("\r\n=                                   By wuguoyan                      =");  
  29.         SerialPutString("\r\n======================================================================");  
  30.         SerialPutString("\r\n\r\n");  
  31.         Main_Menu ();  
  32.     }  
  33.     //否則執行用戶程序  
  34.     else  
  35.     {  
  36.         //判斷用處是否已經下載了用戶程序,因爲正常情況下此地址是棧地址  
  37.         //若沒有這一句話,即使沒有下載程序也會進入而導致跑飛。  
  38.         if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)  
  39.         {  
  40.             SerialPutString("Execute user Program\r\n\n");  
  41.             //跳轉至用戶代碼  
  42.             JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);  
  43.             Jump_To_Application = (pFunction) JumpAddress;  
  44.   
  45.             //初始化用戶程序的堆棧指針  
  46.             __set_MSP(*(__IO uint32_t*) ApplicationAddress);  
  47.             Jump_To_Application();  
  48.         }  
  49.         else  
  50.         {  
  51.             SerialPutString("no user Program\r\n\n");  
  52.         }  
  53.     }  

這裏重點說一下幾句經典且非常重要的代碼:

第一句: if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)   //判斷棧定地址值是否在0x2000 0000 - 0x 2000 2000之間

怎麼理解呢? (1),在程序裏#define ApplicationAddress    0x8003000 ,*(__IO uint32_t*)ApplicationAddress)  即取0x8003000開始到0x8003003 的4個字節的值, 因爲我們的應用程序APP中設置把 中斷向量表 放置在0x08003000 開始的位置;而中斷向量表裏第一個放的就是棧頂地址的值

也就是說,這句話即通過判斷棧頂地址值是否正確(是否在0x2000 0000 - 0x 2000 2000之間) 來判斷是否應用程序已經下載了,因爲應用程序的啓動文件剛開始就去初始化化棧空間,如果棧頂值對了,說應用程已經下載了啓動文件的初始化也執行了;



第二句:    JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);   [  common.c文件第18行定義了:  pFunction   Jump_To_Application;]
                      

ApplicationAddress + 4  即爲0x0800 3004 ,裏面放的是中斷向量表的第二項“復位地址”  JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4); 之後此時JumpAddress

第三句:    Jump_To_Application = (pFunction) JumpAddress;
 startup_stm32f10x_md_lv. 文件中別名  typedef  void (*pFunction)(void);     這個看上去有點奇怪;正常第一個整型變量   typedef  int  a;  就是給整型定義一個別名 a

 void (*pFunction)(void);   是聲明一個函數指針,加上一個typedef 之後  pFunction只不過是類型 void (*)(void) 的一個別名;例如:

  1. pFunction   a1,a2,a3;  
  2.   
  3. void  fun(void)  
  4. {  
  5.     ......  
  6. }  
  7.   
  8. a1 = fun;  

所以,Jump_To_Application = (pFunction) JumpAddress;  此時Jump_To_Application指向了復位函數所在的地址;

第四 、五句: __set_MSP(*(__IO uint32_t*) ApplicationAddress);      \\設置主函數棧指針
               Jump_To_Application();                         \\執行復位函數

我們看一下啓動文件startup_stm32f10x_md_vl。s 中的啓動代碼,更容易理解


移植後的IAP代碼在我的資源(如果是stm32f100cb的芯片可以直接用):http://download.csdn.net/detail/yx_l128125/6475219

三、我們來簡單看下啓動文件中的啓動代碼,分析一下這更有利於我們對IAP的理解: (下面這篇文章寫的非常好,有木有!)

下文來自於:http://blog.sina.com.cn/s/blog_69bcf45201019djx.html

解析 STM32 的啓動過程

解析STM32的啓動過程

當前的嵌入式應用程序開發過程裏,並且C語言成爲了絕大部分場合的最佳選擇。如此一來main函數似乎成爲了理所當然的起點——因爲C程序往往從main函數開始執行。但一個經常會被忽略的問題是:微控制器(單片機)上電後,是如何尋找到並執行main函數的呢?很顯然微控制器無法從硬件上定位main函數的入口地址,因爲使用C語言作爲開發語言後,變量/函數的地址便由編譯器在編譯時自行分配,這樣一來main函數的入口地址在微控制器的內部存儲空間中不再是絕對不變的。相信讀者都可以回答這個問題,答案也許大同小異,但肯定都有個關鍵詞,叫啓動文件,用英文單詞來描述是Bootloader

無論性能高下,結構簡繁,價格貴賤,每一種微控制器(處理器)都必須有啓動文件,啓動文件的作用便是負責執行微控制器從復位開始執行main函數中間這段時間(稱爲啓動過程)所必須進行的工作。最爲常見的51AVRMSP430等微控制器當然也有對應啓動文件,但開發環境往往自動完整地提供了這個啓動文件,不需要開發人員再行干預啓動過程,只需要從main函數開始進行應用程序的設計即可。

話題轉到STM32微控制器,無論是keil
uvision4
還是IAR EWARM開發環境,ST公司都提供了現成的直接可用的啓動文件,程序開發人員可以直接引用啓動文件後直接進行C應用程序的開發。這樣能大大減小開發人員從其它微控制器平臺跳轉至STM32平臺,也降低了適應STM32微控制器的難度(對於上一代ARM的當家花旦ARM9,啓動文件往往是第一道難啃卻又無法逾越的坎)。

相對於ARM上一代的主流ARM7/ARM9內核架構,新一代Cortex內核架構的啓動方式有了比較大的變化。ARM7/ARM9內核的控制器在復位後,CPU會從存儲空間的絕對地址0x000000取出第一條指令執行復位中斷服務程序的方式啓動,即固定了復位後的起始地址爲0x000000PC = 0x000000)同時中斷向量表的位置並不是固定的。而Cortex-M3內核則正好相反,有3種情況:
1
 通過boot引腳設置可以將中斷向量表定位於SRAM區,即起始地址爲0x2000000,同時復位後PC指針位於0x2000000處;
2
 通過boot引腳設置可以將中斷向量表定位於FLASH區,即起始地址爲0x8000000,同時復位後PC指針位於0x8000000處;
3
 通過boot引腳設置可以將中斷向量表定位於內置Bootloader區,本文不對這種情況做論述;
Cortex-M3內核規定,起始地址必須存放堆頂指針,而第二個地址則必須存放復位中斷入口向量地址,這樣在Cortex-M3內核復位後,會自動從起始地址的下一個32位空間取出復位中斷入口向量,跳轉執行復位中斷服務程序。對比ARM7/ARM9內核,Cortex-M3內核則是固定了中斷向量表的位置而起始地址是可變化的。
有了上述準備只是後,下面以STM322.02固件庫提供的啓動文件stm32f10x_vector.s爲模板,對STM32的啓動過程做一個簡要而全面的解析。
程序清單一:
;文件stm32f10x_vector.s,其中註釋爲行號
DATA_IN_ExtSRAM EQU 0 
1
Stack_Size EQU 0x00000400 
2
AREA STACK, NOINIT, READWRITE, ALIGN = 3 
3
Stack_Mem SPACE Stack_Size 
4
__initial_sp 
5
Heap_Size EQU 0x00000400 
6
AREA HEAP, NOINIT, READWRITE, ALIGN = 3 
7
__heap_base 
8
Heap_Mem SPACE Heap_Size 
9
__heap_limit 
10
THUMB 
11
PRESERVE8 
12
IMPORT NMIException 
13
IMPORT HardFaultException 
14
IMPORT MemManageException 
15
IMPORT BusFaultException 
16
IMPORT UsageFaultException 
17
IMPORT SVCHandler 
18
IMPORT DebugMonitor 
19
IMPORT PendSVC 
20
IMPORT SysTickHandler 
21
IMPORT WWDG_IRQHandler 
22
IMPORT PVD_IRQHandler 
23
IMPORT TAMPER_IRQHandler 
24
IMPORT RTC_IRQHandler 
25
IMPORT FLASH_IRQHandler 
26
IMPORT RCC_IRQHandler 
27
IMPORT EXTI0_IRQHandler 
28
IMPORT EXTI1_IRQHandler 
29
IMPORT EXTI2_IRQHandler 
30
IMPORT EXTI3_IRQHandler 
31
IMPORT EXTI4_IRQHandler 
32
IMPORT DMA1_Channel1_IRQHandler 
33
IMPORT DMA1_Channel2_IRQHandler 
34
IMPORT DMA1_Channel3_IRQHandler 
35
IMPORT DMA1_Channel4_IRQHandler 
36
IMPORT DMA1_Channel5_IRQHandler 
37
IMPORT DMA1_Channel6_IRQHandler 
38
IMPORT DMA1_Channel7_IRQHandler 
39
IMPORT ADC1_2_IRQHandler 
40
IMPORT USB_HP_CAN_TX_IRQHandler 
41
IMPORT USB_LP_CAN_RX0_IRQHandler 
42
IMPORT CAN_RX1_IRQHandler 
43
IMPORT CAN_SCE_IRQHandler 
44
IMPORT EXTI9_5_IRQHandler 
45
IMPORT TIM1_BRK_IRQHandler 
46
IMPORT TIM1_UP_IRQHandler 
47
IMPORT TIM1_TRG_COM_IRQHandler 
48
IMPORT TIM1_CC_IRQHandler 
49
IMPORT TIM2_IRQHandler 
50
IMPORT TIM3_IRQHandler 
51
IMPORT TIM4_IRQHandler 
52
IMPORT I2C1_EV_IRQHandler 
53
IMPORT I2C1_ER_IRQHandler 
54
IMPORT I2C2_EV_IRQHandler 
55
IMPORT I2C2_ER_IRQHandler 
56
IMPORT SPI1_IRQHandler 
57
IMPORT SPI2_IRQHandler 
58
IMPORT USART1_IRQHandler 
59
IMPORT USART2_IRQHandler 
60
IMPORT USART3_IRQHandler 
61
IMPORT EXTI15_10_IRQHandler 
62
IMPORT RTCAlarm_IRQHandler 
63
IMPORT USBWakeUp_IRQHandler 
64
IMPORT TIM8_BRK_IRQHandler 
65
IMPORT TIM8_UP_IRQHandler 
66
IMPORT TIM8_TRG_COM_IRQHandler 
67
IMPORT TIM8_CC_IRQHandler 
68
IMPORT ADC3_IRQHandler 
69
IMPORT FSMC_IRQHandler 
70
IMPORT SDIO_IRQHandler 
71
IMPORT TIM5_IRQHandler 
72
IMPORT SPI3_IRQHandler 
73
IMPORT UART4_IRQHandler 
74
IMPORT UART5_IRQHandler 
75
IMPORT TIM6_IRQHandler 
76
IMPORT TIM7_IRQHandler 
77
IMPORT DMA2_Channel1_IRQHandler 
78
IMPORT DMA2_Channel2_IRQHandler 
79
IMPORT DMA2_Channel3_IRQHandler 
80
IMPORT DMA2_Channel4_5_IRQHandler 
81
AREA RESET, DATA, READONLY 
82
EXPORT __Vectors 
83
__Vectors 
84
DCD __initial_sp 
85
DCD Reset_Handler 
86
DCD NMIException 
87
DCD HardFaultException 
88
DCD MemManageException 
89
DCD BusFaultException 
90
DCD UsageFaultException 
91
DCD 0 
92
DCD 0 
93
DCD 0 
94
DCD 0 
95
DCD SVCHandler 
96
DCD DebugMonitor 
97
DCD 0 
98
DCD PendSVC 
99
DCD SysTickHandler 
100
DCD WWDG_IRQHandler 
101
DCD PVD_IRQHandler 
102
DCD TAMPER_IRQHandler 
103
DCD RTC_IRQHandler 
104
DCD FLASH_IRQHandler 
105
DCD RCC_IRQHandler 
106
DCD EXTI0_IRQHandler 
107
DCD EXTI1_IRQHandler 
108
DCD EXTI2_IRQHandler 
109
DCD EXTI3_IRQHandler 
110
DCD EXTI4_IRQHandler 
111
DCD DMA1_Channel1_IRQHandler 
112
DCD DMA1_Channel2_IRQHandler 
113
DCD DMA1_Channel3_IRQHandler 
114
DCD DMA1_Channel4_IRQHandler 
115
DCD DMA1_Channel5_IRQHandler 
116
DCD DMA1_Channel6_IRQHandler 
117
DCD DMA1_Channel7_IRQHandler 
118
DCD ADC1_2_IRQHandler 
119
DCD USB_HP_CAN_TX_IRQHandler 
120
DCD USB_LP_CAN_RX0_IRQHandler 
121
DCD CAN_RX1_IRQHandler 
122
DCD CAN_SCE_IRQHandler 
123
DCD EXTI9_5_IRQHandler 
124
DCD TIM1_BRK_IRQHandler 
125
DCD TIM1_UP_IRQHandler 
126
DCD TIM1_TRG_COM_IRQHandler 
127
DCD TIM1_CC_IRQHandler 
128
DCD TIM2_IRQHandler 
129
DCD TIM3_IRQHandler 
130
DCD TIM4_IRQHandler 
131
DCD I2C1_EV_IRQHandler 
132
DCD I2C1_ER_IRQHandler 
133
DCD I2C2_EV_IRQHandler 
134
DCD I2C2_ER_IRQHandler 
135
DCD SPI1_IRQHandler 
136
DCD SPI2_IRQHandler 
137
DCD USART1_IRQHandler 
138
DCD USART2_IRQHandler 
139
DCD USART3_IRQHandler 
140
DCD EXTI15_10_IRQHandler 
141
DCD RTCAlarm_IRQHandler 
142
DCD USBWakeUp_IRQHandler 
143
DCD TIM8_BRK_IRQHandler 
144
DCD TIM8_UP_IRQHandler 
145
DCD TIM8_TRG_COM_IRQHandler 
146
DCD TIM8_CC_IRQHandler 
147
DCD ADC3_IRQHandler 
148
DCD FSMC_IRQHandler 
149
DCD SDIO_IRQHandler 
150
DCD TIM5_IRQHandler 
151
DCD SPI3_IRQHandler 
152
DCD UART4_IRQHandler 
153
DCD UART5_IRQHandler 
154
DCD TIM6_IRQHandler 
155
DCD TIM7_IRQHandler 
156
DCD DMA2_Channel1_IRQHandler 
157
DCD DMA2_Channel2_IRQHandler 
158
DCD DMA2_Channel3_IRQHandler 
159
DCD DMA2_Channel4_5_IRQHandler 
160
AREA |.text|, CODE, READONLY 
161
Reset_Handler PROC 
162
EXPORT Reset_Handler 
163
IF DATA_IN_ExtSRAM == 1 
164
LDR R0,= 0x00000114 
165
LDR R1,= 0x40021014 
166
STR R0,[R1] 
167
LDR R0,= 0x000001E0 
168
LDR R1,= 0x40021018 
169
STR R0,[R1] 
170
LDR R0,= 0x44BB44BB 
171
LDR R1,= 0x40011400 
172
STR R0,[R1] 
173
LDR R0,= 0xBBBBBBBB 
174
LDR R1,= 0x40011404 
175
STR R0,[R1] 
176
LDR R0,= 0xB44444BB 
177
LDR R1,= 0x40011800 
178
STR R0,[R1] 
179
LDR R0,= 0xBBBBBBBB 
180
LDR R1,= 0x40011804 
181
STR R0,[R1] 
182
LDR R0,= 0x44BBBBBB 
183
LDR R1,= 0x40011C00 
184
STR R0,[R1] 
185
LDR R0,= 0xBBBB4444 
186
LDR R1,= 0x40011C04 
187
STR R0,[R1] 
188
LDR R0,= 0x44BBBBBB 
189
LDR R1,= 0x40012000 
190
STR R0,[R1] 
191
LDR R0,= 0x44444B44 
192
LDR R1,= 0x40012004 
193
STR R0,[R1] 
194
LDR R0,= 0x00001011 
195
LDR R1,= 0xA0000010 
196
STR R0,[R1] 
197
LDR R0,= 0x00000200 
198
LDR R1,= 0xA0000014 
199
STR R0,[R1] 
200
ENDIF 
201
IMPORT __main 
202
LDR R0, =__main 
203
BX R0 
204
ENDP 
205
ALIGN 
206
IF :DEF:__MICROLIB 
207
EXPORT __initial_sp 
208
EXPORT __heap_base 
209
EXPORT __heap_limit 
210
ELSE 
211
IMPORT __use_two_region_memory 
212
EXPORT __user_initial_stackheap 
213
__user_initial_stackheap 
214
LDR R0, = Heap_Mem 
215
LDR R1, = (Stack_Mem + Stack_Size) 
216
LDR R2, = (Heap_Mem + Heap_Size) 
217
LDR R3, = Stack_Mem 
218
BX LR 
219
ALIGN 
220
ENDIF 
221
END 
222
ENDIF 
223
END 
224
如程序清單一,STM32的啓動代碼一共224行,使用了彙編語言編寫,這其中的主要原因下文將會給出交代。現在從第一行開始分析:
 1行:定義是否使用外部SRAM,爲1則使用,爲0則表示不使用。此語行若用C語言表達則等價於:
#define DATA_IN_ExtSRAM 0
 2行:定義棧空間大小爲0x00000400個字節,即1Kbyte。此語行亦等價於:
#define Stack_Size 0x00000400
 3行:僞指令AREA,表示
 4行:開闢一段大小爲Stack_Size的內存空間作爲棧。
 5行:標號__initial_sp,表示棧空間頂地址。
 6行:定義堆空間大小爲0x00000400個字節,也爲1Kbyte
 7行:僞指令AREA,表示
 8行:標號__heap_base,表示堆空間起始地址。
 9行:開闢一段大小爲Heap_Size的內存空間作爲堆。
 10行:標號__heap_limit,表示堆空間結束地址。
 11行:告訴編譯器使用THUMB指令集。
 12行:告訴編譯器以8字節對齊。
 1381行:IMPORT指令,指示後續符號是在外部文件定義的(類似C語言中的全局變量聲明),而下文可能會使用到這些符號。
 82行:定義只讀數據段,實際上是在CODE區(假設STM32FLASH啓動,則此中斷向量表起始地址即爲0x8000000
 83行:將標號__Vectors聲明爲全局標號,這樣外部文件就可以使用這個標號。
 84行:標號__Vectors,表示中斷向量表入口地址。
 85160行:建立中斷向量表。
 161行:
 162行:復位中斷服務程序,PROCENDP結構表示程序的開始和結束。
 163行:聲明覆位中斷向量Reset_Handler爲全局屬性,這樣外部文件就可以調用此復位中斷服務。
 164行:IFENDIF爲預編譯結構,判斷是否使用外部SRAM,在第1行中已定義爲不使用
 165201行:此部分代碼的作用是設置FSMC總線以支持SRAM,因不使用外部SRAM因此此部分代碼不會被編譯。
 202行:聲明__main標號。
 203204行:跳轉__main地址執行。
 207行:IFELSEENDIF結構,判斷是否使用DEF:__MICROLIB(此處爲不使用)。
 208210行:若使用DEF:__MICROLIB,則將__initial_sp__heap_base__heap_limit亦即棧頂地址,堆始末地址賦予全局屬性,使外部程序可以使用。
 212行:定義全局標號__use_two_region_memory
 213行:聲明全局標號__user_initial_stackheap,這樣外程序也可調用此標號。
 214行:標號__user_initial_stackheap,表示用戶堆棧初始化程序入口。
 215218行:分別保存棧頂指針和棧大小,堆始地址和堆大小至R0R1R2R3寄存器。
 224行:程序完畢。
以上便是STM32的啓動代碼的完整解析,接下來對幾個小地方做解釋:
1
 AREA指令:僞指令,用於定義代碼段或數據段,後跟屬性標號。其中比較重要的一個標號爲READONLY或者READWRITE,其中READONLY表示該段爲只讀屬性,聯繫到STM32的內部存儲介質,可知具有隻讀屬性的段保存於FLASH區,即0x8000000地址後。而READONLY表示該段爲可讀寫屬性,可知可讀寫段保存於SRAM區,即0x2000000地址後。由此可以從第37行代碼知道,堆棧段位於SRAM空間。從第82行可知,中斷向量表放置與FLASH區,而這也是整片啓動代碼中最先被放進FLASH區的數據。因此可以得到一條重要的信息:0x8000000地址存放的是棧頂地址__initial_sp0x8000004地址存放的是復位中斷向量Reset_HandlerSTM32使用32位總線,因此存儲空間爲4字節對齊)。
2
 DCD指令:作用是開闢一段空間,其意義等價於C語言中的地址符&。因此從第84行開始建立的中斷向量表則類似於使用C語言定義了一個指針數組,其每一個成員都是一個函數指針,分別指向各個中斷服務函數。
3
 標號:前文多處使用了標號一詞。標號主要用於表示一片內存空間的某個位置,等價於C語言中的地址概念。地址僅僅表示存儲空間的一個位置,從C語言的角度來看,變量的地址,數組的地址或是函數的入口地址在本質上並無區別。
4
 202行中的__main標號並不表示C程序中的main函數入口地址,因此第204行也並不是跳轉至main函數開始執行C程序。__main標號表示C/C++標準實時庫函數裏的一個初始化子程序__main的入口地址。該程序的一個主要作用是初始化堆棧(對於程序清單一來說則是跳轉__user_initial_stackheap標號進行初始化堆棧的),並初始化映像文件,最後跳轉C程序中的main函數。這就解釋了爲何所有的C程序必須有一個main函數作爲程序的起點——因爲這是由C/C++標準實時庫所規定的——並且不能更改,因爲C/C++標準實時庫並不對外界開發源代碼。因此,實際上在用戶可見的前提下,程序在第204行後就跳轉至.c文件中的main函數,開始執行C程序了。
至此可以總結一下STM32的啓動文件和啓動過程。首先對棧和堆的大小進行定義,並在代碼區的起始處建立中斷向量表,其第一個表項是棧頂地址,第二個表項是復位中斷服務入口地址。然後在復位中斷服務程序中跳轉¬¬C/C++標準實時庫的__main函數,完成用戶堆棧等的初始化後,跳轉.c文件中的main函數開始執行C程序。假設STM32被設置爲從內部FLASH啓動(這也是最常見的一種情況),中斷向量表起始地位爲0x8000000,則棧頂地址存放於0x8000000處,而復位中斷服務入口地址存放於0x8000004處。當STM32遇到復位信號後,則從0x80000004處取出復位中斷服務入口地址,繼而執行復位中斷服務程序,然後跳轉__main函數,最後進入mian函數,來到C的世界。(轉載)

二、實驗的IAP串口在線升級案列可以看這個文章:http://blog.csdn.net/xiaoaid01/article/details/38110163(正點原子的)


三、說說個人學習中覺得重要的東西:

1.關於keil的配置這個很重要:比如bootloader:爲0x8000000----0x80040000 即16K大小爲boot程序區域 應用程序的起始地址爲0x80040000

那對應的keil和程序配置如下,bootloader:程序中設置 NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);配置中斷向量偏移量 keil中做好相對應的配置  

這個大小最好設置和bootloader預留空間大小一致。

這個地方一定要勾上,這隻代碼定位,只要勾上了NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0)這個函數才起作用,特別是在APP程序中。

在APP的配置時:NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x4000);keil裏面的配置IROM1:0x8004000  SIZE:實際的flash大小-bootloader預留區大小


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章