STM32文件详解
启动文件
启动文件的方式
1、初始化堆栈指针 SP = _initial_sp
2、初始化程序计数器指针 PC = Reset_Handler
3、设置堆和栈的大小
4、初始化中断向量表
5、配置外部 SRAM 作为数据存储器(可选)
6、配置系统时钟,通过调用 SystemInit 函数(可选)
7、调用 C 库中的 _main 函数初始化用户堆栈,最终调用 main 函数
打开MDK
MDK->Help->uVision Help,
栈空间开辟
堆空间开辟
PRESERVE8:指示编译器按照 8 字节对齐。
THUMB:指示编译器之后的指令为 THUMB 指令
中断向量表
__Vectors 为向量表起始地址, __Vectors_End 为向量表结束地址,__Vectors_Size 为向量表大小,>__Vectors_Size = __Vectors_End - __Vectors。
复位程序
接下来是定义只读代码段
复位子程序代码
利用 PROC、ENDP 这一对伪指令把程序段分为若干个过程,使程序的结构加清晰。
对于 weak 的理解
weak 顾名思义是"弱"的意思,在汇编中,在函数名称后面加[WEAK]来表示,而在 C 语言中,在函数名称前面加上__weak 修饰符来表示,这样的函数我们称为"弱函数"。
被[WEAK]或__weak 声明的函数,我们可以在自己的文件中重新定义一个同名函数, 最终编译器编译的时候,会选择我们定义的函数,如果我们没有重新定义这个函数,那么编译器就会执行[WEAK]或__weak 声明的函数,并且编译器不会报错
对于_main 函数的分析
_main 代码是编译器自动创建的,因此无法找到_main 代码。
程序经过汇编启动代码,执行到__main()后,负责初始化堆栈,完成库函数的初始化,最后自动跳转向 main()函数。
中断程序
堆栈初始化
ALIGN
指指令或数据的存放地址进行对齐,一般需要跟一个立即数
勾选了 Use MicroLIB 就代表定义了__MICROLIB 这个宏。
接下来进行堆栈空间初始化,堆是从低到高生长,栈是从高到低生长,是两个互相独立 的数据段,并且不能交叉使用。
Use MicroLIB
MicroLIB 是 MDK 自带的微库,是缺省 C 库的备选库,MicroLIB 进行了高度优化使得其代码变得很小,功能比缺省 C 库少。MicroLIB 是没有源码的,只有库。
系统启动流程
时钟树
一个简化的 STM32F1 时钟系统。图中已经把我们主要关注几处标注出来。
A 部分表示其它电路需要的输入源时钟信号;
B 为一个特殊的振荡电路"PLL",由几个部分构成;
C 为我们重点需要关注的 MCU 内的主时钟"SYSCLK";AHB 预分频器将 SYSCLK 分频或不分 频后分发给其它外设进行处理,包括到 F 部分的 Cortex-M 内核系统的时钟。
D、E 部分别为定时器等外设的时钟源 APB1/APB2。
G 是 STM32 的时钟输出功能,其它部分等我们学习到再详细探讨。
时钟源
对于 STM32F1,输入时钟源(Input Clock)主要包括 HSI,HSE,LSI,LSE。其中,从时钟频率来分可以分为高速时钟源和低速时钟源,其中 HSI、HSE 高速时钟,LSI 和 LSE 是低速时钟。
从来源可分为外部时钟源和内部时钟源,外部时钟源就是从 外部通过接晶振的方式获取时钟源,其中 HSE 和 LSE 是外部时钟源;其他是内部时钟源,芯片上电即可产生,不需要借助外部电路。
时钟配置函数
SYSCLK(系统时钟) =72MHz
AHB 总线时钟(HCLK=SYSCLK) =72MHz
APB1 总线时钟(PCLK1=SYSCLK/2) =36MHz
APB2 总线时钟(PCLK2=SYSCLK/1) =72MHz
PLL 主时钟 =72MHz
时钟初始化配置函数
c
void SystemInit (void)
{
/* Reset the RCC clock configuration to the default reset state(for debug purpose) */
/* Set HSION bit */
RCC->CR |= (uint32_t)0x00000001;
/* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
#ifndef STM32F10X_CL
RCC->CFGR &= (uint32_t)0xF8FF0000;
#else
RCC->CFGR &= (uint32_t)0xF0FF0000;
#endif /* STM32F10X_CL */
/* Reset HSEON, CSSON and PLLON bits */
RCC->CR &= (uint32_t)0xFEF6FFFF;
/* Reset HSEBYP bit */
RCC->CR &= (uint32_t)0xFFFBFFFF;
/* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
RCC->CFGR &= (uint32_t)0xFF80FFFF;
#ifdef STM32F10X_CL
/* Reset PLL2ON and PLL3ON bits */
RCC->CR &= (uint32_t)0xEBFFFFFF;
/* Disable all interrupts and clear pending bits */
RCC->CIR = 0x00FF0000;
/* Reset CFGR2 register */
RCC->CFGR2 = 0x00000000;
#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
/* Disable all interrupts and clear pending bits */
RCC->CIR = 0x009F0000;
/* Reset CFGR2 register */
RCC->CFGR2 = 0x00000000;
#else
/* Disable all interrupts and clear pending bits */
RCC->CIR = 0x009F0000;
#endif /* STM32F10X_CL */
#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)
#ifdef DATA_IN_ExtSRAM
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM */
#endif
/* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
/* Configure the Flash Latency cycles and enable prefetch buffer */
SetSysClock();
#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
}
SystemInit 函数开始通过条件编译,先复位 RCC 寄存器,同时通过设置 CR 寄存器的 HSI 时钟使能位来打开 HSI 时钟。
- 时钟控制寄存器(RCC_CR)
- 时钟配置寄存器(RCC_CFGR)
- 时钟中断寄存器(RCC_CIR)
- APB2 外设复位寄存器(RCC_APB2RSTR)和APB1 外设复位寄存器
默认情况下如果 CR 寄存器复位, 是选择 HSI 作为系统时钟,这点大家可以查看 RCC->CR 寄存器相关位描述可以得知,当低两位配置为 00 的时候(复位之后),会选择 HSI 振荡器为系统时钟。
在 system_stm32f10x.c 文件的开头就有对此宏定义,系统默认的宏定义是72MHz
c
#define SYSCLK_FREQ_72MHz 72000000
如果你要设置为 36MHz,只需要注释掉上面代码
c
#define SYSCLK_FREQ_36MHz 36000000