IAR在运行到主()函数之前,要运行一段启动代码来初初始化寄存器,中断向量表以及系统时钟的初始化。我们先来了解一下中断向量表,可以再自己的工作空间找到相应的启动文件,我的叫startup_stm32f412vx.s,我们只要了解两个重要的。
__vector_table
DCD sfe(CSTACK)
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0
...............省略..........
首先我们了解“__vector_table”这个的含义,看一下此文件中的对这个的描述。
; The name "__vector_table" has special meaning for C-SPY:
; it is where the SP start value is found, and the NVIC vector
; table register (VTOR) is initialized to this address if != 0.
;
在stm32f412vx_flash.icf文件中是这个值(根据自己设定):
define symbol __ICFEDIT_intvec_start__ = 0x0800C000;
接下来在了解第一个向量CSTACK指针,第二个是复位地址Reset_Handler指针.CSTACK是在“stm32f412vx_flash.icf”文件定义的一个快用来存放栈数据,看下定义:
define symbol __ICFEDIT_size_cstack__ = 0x600;
define block CSTACK with alignment = 8, size = __ICFEDIT_size_cstack__ { };
SFE(CSTACK)代表取这个块的最后一个地址的下一个地址,因为栈有先进后出的特性,栈数据的存储是从下而上的,栈数据的读取是由上而下的。
第二个是单片机开始(复位)地址,Reset_Handler,我们先在IAR设置一下,把勾去掉,如图:
之后下载运行,界面如图:
系统跑到复位地址执行一些操作,来粗略解释下这个流程:
Reset_Handler
LDR R0, =SystemInit
BLX R0
LDR R0, =__iar_program_start
BX R0
线路1:表明执行的地址。
线路2:将SystemInit函数地址给R0寄存器
line3中:跳到R0寄存器保存的地址去执行(也就是执行SystemInit这个函数)
line4,line5同上。
所以单片机上电的顺序就是初始化SP,PC寄存器,接下来跳到PC寄存器指向的方向执行程序。顺着这个思路,如果你有好几个程序,在上电的时候通过外部触发初始化SP以及PC,就可以分别进入不同的程序。放个启动程序链接体验一下,点我。
接下来去看SystemInit这个函数做了什么:
/**
* @brief Setup the microcontroller system
* Initialize the FPU setting, vector table location and External memory
* configuration.
* @param None
* @retval None
*/
void SystemInit(void)
{
/* FPU settings ------------------------------------------------------------*/
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
#endif
/* Reset the RCC clock configuration to the default reset state ------------*/
/* Set HSION bit */
RCC->CR |= (uint32_t)0x00000001;
/* Reset CFGR register */
RCC->CFGR = 0x00000000;
/* Reset HSEON, CSSON and PLLON bits */
RCC->CR &= (uint32_t)0xFEF6FFFF;
/* Reset PLLCFGR register */
RCC->PLLCFGR = 0x24003010;
/* Reset HSEBYP bit */
RCC->CR &= (uint32_t)0xFFFBFFFF;
/* Disable all interrupts */
RCC->CIR = 0x00000000;
#if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */
/* Configure the Vector Table location add offset address ------------------*/
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif
}
本想到此结束的,还是在解释一波吧,首先是进行了FPU(Float Point Unit)的初始化,SCB-> CPACR(系统控制块 - >协处理器门禁控制寄存器)系统控制块的协处理器访问控制器,看下这个寄存器的样子。
位 | CP10和CP11设置 |
00 | 拒绝访问。任何访问都会产生使用错误(类型为NOCP - 无协处理器) |
01 | 只支持特权访问,非特权访问会产生使用错误。 |
10 | 保留 - 结果无法预测 |
11 | 全访问 |
下面就是RCC时钟的初始化以及内部闪存向量的重定位。