STM32 IAP在线升级详解

来源:互联网 发布:淘宝网咖啡杯 编辑:程序博客网 时间:2024/06/05 16:15

转自:http://blog.csdn.net/yx_l128125/article/details/12992773/

(扩展-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或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 的选项中,选择C / C ++ / PREPROMCESSOR符号的定义栏里定义,把有关STM32F10X_MD的宏定义改成:STM32F10X_MD_VL

也可以在STM32F10X.H里用宏定义
[平原] 视图平原拷贝 
  1.   / *根据您使用的目标STM32设备取消下面的行注释  
  2.    应用   
  3.   * /  
  4.   
  5. (STM32F10X_LD)&&!定义(STM32F10X_LD)&&!定义(STM32F10X_MD)&&!定义(STM32F10X_MD)&&!定义(STM32F10X_HD)&&!定义(STM32F10X_HDL)&&!   
  6.   / * #define STM32F10X_LD * / / *!<STM32F10X_LD:STM32低密度设备* /  
  7.   / * #define STM32F10X_LD_VL * / / *!<STM32F10X_LD_VL:STM32低密度值线路设备* /    
  8.   / * #define STM32F10X_MD * / / *!<STM32F10X_MD:STM32中密度设备* /  
  9.    #define STM32F10X_MD_VL / *!<STM32F10X_MD_VL:STM32中密度值线路设备* /    
  10.   / * #define STM32F10X_HD * / / *!<STM32F10X_HD:STM32高密度设备* /  
  11.   / * #define STM32F10X_HD_VL * / / *!<STM32F10X_HD_VL:STM32高密度值线路设备* /    
  12.   / * #define STM32F10X_XL * / / *!<STM32F10X_XL:STM32 XL-density devices * /  
  13.   / * #define STM32F10X_CL * / / *!<STM32F10X_CL:STM32连接线设备* /  
  14. #万一  

上面代码说的是如果没有定义STM32F10X_MD_VL,则宏定义STM32F10X_MD_VL

C.外部时钟问价在stm32f10x.h依据实际修改,原文是说如果没有宏定义外部时钟HES_VALUE的值,但是宏定义了stm32f10x_cl则外部时钟设置为25MHZ,否则外部时钟都设置为8MHZ; 我用的外部晶振是8MHZ的所以不必修改这部分代码;

[平原] 视图平原拷贝 
  1. #if!定义HSE_VALUE  
  2.  #ifdef STM32F10X_CL     
  3.   #define HSE_VALUE((uint32_t)25000000)//外部振荡器的值为Hz <pre name =“code”class =“plain”> #else </ pre> #define HSE_VALUE((uint32_t)8000000)//值外部振荡器以Hz #endif / * STM32F10X_CL * /#endif / * HSE_VALUE * /  

D.做系统主频时钟的更改

system_stm32f10x.c的系统主频率,依实际情况修改;我用的芯片主频时钟是24MHZ;
[平原] 视图平原拷贝 
  1.       
  2. #if defined(STM32F10X_LD_VL)|| (定义为STM32F10X_MD_VL)|| (定义为STM32F10X_HD_VL)  
  3. / * #define SYSCLK_FREQ_HSE HSE_VALUE * /  
  4.  #define SYSCLK_FREQ_24MHz 24000000  
  5. #其他  
  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. #万一  
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 APP到达0x08003000开始的位置,中断向量表也放在0x0800 3000开始的位置;如图


所以我们需要先查看一下misc.h文件中的中断向量表的初始位置宏定义为NVIC_VectTab_Flash 0x0800 0000

那么要就要设置编译器keil中的目标的选项选项中的IROM1地址为0x0800 0000大小为0x20000即128K;

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

(提示:这一项IROM1地址即为当前程序下载到flash的地址的起始位置)

下面我们来分析一下修改后的IAP代码:

[平原] 视图平原拷贝 
  1. / ******************* ******************************  
  2.   * @函数名称主  
  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 =应用程序编程应用程序(版本1.0.0)=”);  
  27.         SerialPutString(“\ r \ n = =”);  
  28.         SerialPutString(“\ r \ n =由wuguoyan =”);  
  29.         SerialPutString(“\ r \ n =========================================== ===========================“);  
  30.         SerialPutString( “\ r \ n \ r \ n”);  
  31.         主菜单 ();  
  32.     }  
  33.     //否则执行用户程序  
  34.     其他  
  35.     {  
  36.         //判断用处是否已经下载了用户程序,因为正常情况下此地址是栈地址  
  37.         //若没有这一句话,即使没有下载程序也会进入而导致跑飞。  
  38.         if(((*(__ IO uint32_t *)ApplicationAddress)&0x2FFE0000)== 0x20000000)  
  39.         {  
  40.             SerialPutString(“执行用户程序\ 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.         其他  
  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)的一个别名;例如:

[cpp] 查看纯文本 
  1. p功能a1,a2,a3;  
  2.   
  3. void   fun(void )  
  4. {  
  5.     ......  
  6. }  
  7.   
  8. a1 =乐趣  

所以,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 的启动过程

的当前嵌入式应用程序开发过程里,并且Ç 语言成为了绝大部分场合的最佳选择如此一来。函数似乎成为了理所当然的起点-因为Ç 程序往往从。函数开始执行但一个经常会被忽略的问题是:微控制器(单片机)上电后,是如何寻找到并执行?函数的呢很显然微控制器无法从硬件上定位函数的入口地址,因为使用ç 语言作为开发语言后,变量函数的地址便由编译器在编译时自行分配,这样一来函数的入口地址在微控制器的内部存储空间中不再是绝对不变的。相信读者都可以回答这个问题,答案也许大同小异,但肯定都有个关键词,叫启动文件,用英文单词来描述是 Bootloader ” 

无论性能高下,结构简繁,价格贵贱,每一种微控制器(处理器)都必须有启动文件,启动文件的作用便是负责执行微控制器从复位开始执行函数最近常见的51 AVR MSP430 等微控制器当然也有对应启动文件,但开发环境往往自动完整地提供了这个启动文件,不需要开发人员再行干预启动过程,只需要从函数开始进行应用程序的设计即可。

话题转到STM32 微控制器, 无论keil 
uvision4 
还是IAR EWARM 开发环境,ST 公司都提供了现成的直接可用的启动文件,程序开发人员可以直接引用启动文件后直接进行应用程序的开发。大型减小开发人员从其它微控制器平台跳转至STM32 平台,也降低了适应STM32 微控制器的难度(对于上一代ARM 的当家花旦ARM9 ,启动文件往往是第一道难叮而又无法逾越的坎)。

相对于ARM 上一代的主流ARM7 / ARM9 内核架构,新一代Cortex 内核架构的启动方式有了比较大的变化。ARM7 / ARM9 内核的控制器在复位后,CPU 会从存储空间的绝对地址0x000000 取出第一条指令执行复位中断服务程序的方式启动,即固定了复位后的起始地址为0x000000 PC = 0x000000 )同时中断向量表的位置并不是固定的。而Cortex-M3 内核则正好相反,有种情况
通过引导设置可以将中断向量表定位于SRAM 区,即起始地址为0x2000000 ,同时复位后PC 指针位于0x2000000 处; 通过引导引脚设置可以将中断向量表定位于 
 区,即起始地址为0x8000000 ,同时复位后PC 指针位于0x8000000 处; 
通过引导设置可以将中断向量表定位于内置Bootloader 区,本文不对这种情况做论述; Cortex-M3 内核规定,起始地址必须存放堆顶指针,而第二个地址则必须存放复位中断入口向量地址,这样在Cortex-M3的内核复位后,会自动从起始地址的下一个32 位空间取出复位中断入口向量,跳转执行复位中断服务程序。对比 ARM7 / ARM9 内核,Cortex-M3的内核则是固定了中断向量表的位置而起始地址是可变化的有了上述准备只是后,下面以STM32 2.02 固件库提供的启动文件“ stm32f10x_vector.s ” 为模板,对STM32的启动过程做一个简要而全面的解析。程序清单一:;文件 



stm32f10x_vector.s “ ,其中注释为行号
DATA_IN_ExtSRAM EQU 0  

Stack_Size EQU 0x00000400  

AREA STACK,NOINIT,READWRITE,ALIGN = 3  

Stack_Mem SPACE Stack_Size  

__initial_sp  

Heap_Size EQU 0x00000400  

AREA HEAP,NOINIT, READWRITE,ALIGN = 3  

__heap_base  

Heap_Mem SPACE Heap_Size  

__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_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 行,使用了汇编语言编写,这其中的主要原因下文将会给出交代现在从第一行开始分析: 行:定义是否使用外部SRAM ,为则使用,为则表示不使用此语行若用ç 语言表达则等价于: #定义DATA_IN_ExtSRAM 0  行:定义栈空间大小为0x00000400时个字节,即1KB的此语行亦等价于: #定义STACK_SIZE 0x00000400时 行:伪指令AREA
 

 

 ,表示 行:开辟一段大小为Stack_Size 的内存空间作为栈 行:标号__initial_sp ,表示栈空间顶地址 行:定义堆空间大小为0x00000400 个字节,也为1Kbyte  行:伪指令AREA ,表示 行:标号__heap_base ,表示堆空间起始地址。 行:开辟一段大小为Heap_Size 。内存的空间作为堆 10 行:标号__heap_limit ,表示堆空间结束地址 11 行:告诉编译器使用
 
 
 
 
 
 
 
 THUMB 指令集。 12 行:告诉编译器以字节对齐。 13 81 行:IMPORT 指令,指示后续符号是在外部文件定义的(类似ç 语言中的全局变量声明),而下文可以会使用到这些符号 82 行:定义只读数据段,实际上是在CODE 区(假设STM32 FLASH 启动,则此中断向量表起始地址即为0x8000000  83 行:将标号__Vectors 声明为全局标号,这样外部文件就可以使用这个标号。 84 行:标号__Vectors 。,中断表示向量表入口地址 85 160
 
 
 
 
 
 行:建立中断向量表 161 行: 162 行:复位中断服务程序,PROC ... ENDP 结构表示程序的开始和结束 163 行:声明复位中断向量Reset_Handler 为全局属性,这样外部文件就可以调用此复位中断服务。 164 行:IF ... ENDIF 为预编译结构,判断是否使用外部SRAM ,在第行中已定义为“ 不使用”  165 201 行:此部分代码的作用是设置FSMC 总线以支持SRAM ,因不使用外部SRAM 因此此部分代码不会被编译 
 
 
 
 
 
 202 行:声明__main 标号 203 204 行:跳转__main 地址执行 207 行:IF ... ELSE ... ENDIF 结构,判断是否使用DEF:__ MICROLIB (此处为不使用) 208 210 行:若使用DEF:__ microlib中,则将__initial_sp __heap_base __heap_limit 亦即栈顶地址,堆始末地址赋予全局属性,使外部程序可以使用。 212 行:定义全局标号__use_two_region_memory  213
 
 
 
 
 行:声明全局标号__user_initial_stackheap ,这样外程序也可调用此标号 214 行:标号__user_initial_stackheap ,表示用户堆栈初始化程序入口 215 218 行:分别保存栈顶指针和栈大小,堆始地址和堆大小至R0 R1 R2 R3 寄存器。 224 行:完毕程序以上便是STM32 的启动代码的完整解析,接下来对几个小地方做解释: 1 AREA 指令:伪指令,用于定义代码段或数据段,后跟属性标号。其中比较重要的一个标号为“ READONLY ” 或者“ READWRITE ” ,其中“ READONLY 
 
 
 

 表示该段为只读属性,联系到STM32 的内部存储介质,可知具有只读属性的段保存于FLASH 区,即0x8000000 地址后。而READONLY ” 表示该段为“ 可读写” 属性,可知“ 可读写” 段保存于SRAM 区,即0x2000000 地址后。由此可以从第行代码知道,堆栈段位于SRAM 空间。从第82 行可知,中断向量表放置与FLASH 区,而这也是整片启动代码中最先被放入FLASH 区的数据。因此可以得到一条重要的信息:0x8000000 地址存放的是栈顶地址__initial_sp 0x8000004 地址存放的复位中断向量Reset_Handler STM32使用32 位总线,因此存储空间为字节对齐)
DCD 指令:作用是开辟一段空间,其意义等价于语言中的地址符“ ” 。因此从第84 行开始建立的中断向量表则类似于使用语言定义了一个指针数组,其每一个成员都是一个函数指针,分别指向各个中断服务函数 。3 标号:前文多处使用了“ 标号一词。标号主要用于表示一片内存空间的某个位置,等价于ç 语言中的“ 地址” 概念。地址仅仅表示存储空间的一个位置,从ç 语言的角度来看,变量的地址,数组的地址或是函数的入口地址在本质上并无区别 。4 202 行中的__main 标号并不表示程序中的函数入口地址,因此第 
 
 204 行也并不是跳转至函数开始执行程序__main 标号表示C / C ++ 标准实时库函数里的一个初始化子程序__main 的入口地址。该程序的一个主要作用是初始化堆栈(对于程序清单一来说则是跳转__user_initial_stackheap 标号进行初始化堆栈的),并初始化映像文件,最后跳转ç 程序中的函数这就解释了为何所有的。ç 程序必须有一个函数作为程序的起点因为这是由C / C ++ 标准实时库所规定的并且不能更改,因为C / C ++ 。标准实时库并不对外界开发源代码因此,实际上在用户可见的前提下,程序在第204 行后就跳转至.C 文件中的函数,开始执行ç 程序了。至此可以总结一下STM32
的启动文件和启动过程。首先对栈和堆的大小进行定义,并在代码区的起始处建立中断向量表,其第一个表项是栈顶地址,第二个表项是复位中断服务入口地址。然后在复位中断服务程序中跳转¬¬ C / C ++ 标准实时库的__main 函数,完成用户堆栈等的初始化后,跳转的.c 文件中的函数开始执行ç 程序。假设STM32 被设置为从内部FLASH 启动(这也是最常见的一种情况),中断向量表起始地位为0x8000000 ,则栈顶地址存放于0x8000000 处,而复位中断服务入口地址存放于0x8000004 处。当STM32 遇到复位信号后,则从0x80000004 处取出复位中断服务入口地址,继而执行复位中断服务程序,然后跳转__main 函数,最后进入函数,来到ç 的世界。




原创粉丝点击