STM32启动过程分析
基于STM32H750XB HAL库分析
start.s
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP在进入main函数之前会先调用SystemInit函数,之后再调用__main进入main函数
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* Enable I-Cache---------------------------------------------------------*/
SCB_EnableICache();
/* Enable D-Cache---------------------------------------------------------*/
SCB_EnableDCache();
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* Configure the peripherals common clocks */
PeriphCommonClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
}
}主要分析如下几个函数
SystemInit();
HAL_Init();
SystemClock_Config();
PeriphCommonClock_Config();SystemInit(); 函数分析
void SystemInit (void)
{
#if defined (DATA_IN_D2_SRAM)
__IO uint32_t tmpreg;
#endif /* DATA_IN_D2_SRAM */
/* FPU设置:如果FPU存在并被使用,则设置完全访问权限 */
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
SCB->CPACR |= ((3UL << (10*2))|(3UL << (11*2))); /* 设置CP10和CP11完全访问 */
#endif
/* 重置RCC时钟配置到默认复位状态 */
/* 增加CPU频率前,检查并设置FLASH延迟 */
if(FLASH_LATENCY_DEFAULT > (READ_BIT((FLASH->ACR), FLASH_ACR_LATENCY)))
{
MODIFY_REG(FLASH->ACR, FLASH_ACR_LATENCY, (uint32_t)(FLASH_LATENCY_DEFAULT));
}
/* 设置HSION位,启用内部高速时钟 */
RCC->CR |= RCC_CR_HSION;
/* 重置CFGR寄存器 */
RCC->CFGR = 0x00000000;
/* 重置各种时钟控制位 */
RCC->CR &= 0xEAF6ED7FU;
/* 降低CPU频率后,检查并设置FLASH延迟 */
if(FLASH_LATENCY_DEFAULT < (READ_BIT((FLASH->ACR), FLASH_ACR_LATENCY)))
{
MODIFY_REG(FLASH->ACR, FLASH_ACR_LATENCY, (uint32_t)(FLASH_LATENCY_DEFAULT));
}
/* 以下是根据具体型号的STM32设备进行的额外配置 */
#if defined(D3_SRAM_BASE)
/* 重置D1CFGR, D2CFGR, D3CFGR寄存器 */
RCC->D1CFGR = 0x00000000;
RCC->D2CFGR = 0x00000000;
RCC->D3CFGR = 0x00000000;
#else
/* 重置CDCFGR1, CDCFGR2, SRDCFGR寄存器 */
RCC->CDCFGR1 = 0x00000000;
RCC->CDCFGR2 = 0x00000000;
RCC->SRDCFGR = 0x00000000;
#endif
/* 重置并配置PLL相关寄存器 */
RCC->PLLCKSELR = 0x02020200;
RCC->PLLCFGR = 0x01FF0000;
RCC->PLL1DIVR = 0x01010280;
RCC->PLL1FRACR = 0x00000000;
RCC->PLL2DIVR = 0x01010280;
RCC->PLL2FRACR = 0x00000000;
RCC->PLL3DIVR = 0x01010280;
RCC->PLL3FRACR = 0x00000000;
/* 重置HSEBYP位 */
RCC->CR &= 0xFFFBFFFFU;
/* 禁用所有中断 */
RCC->CIER = 0x00000000;
#if (STM32H7_DEV_ID == 0x450UL)
/* 特定于STM32H7系列的配置 */
if((DBGMCU->IDCODE & 0xFFFF0000U) < 0x20000000U)
{
*((__IO uint32_t*)0x51008108) = 0x000000001U;
}
#endif /* STM32H7_DEV_ID */
#if defined(DATA_IN_D2_SRAM)
/* 如果数据存储在D2 SRAM中,启用D2 SRAM时钟 */
RCC->AHB2ENR |= (RCC_AHB2ENR_D2SRAM1EN | RCC_AHB2ENR_D2SRAM2EN | RCC_AHB2ENR_D2SRAM3EN);
tmpreg = RCC->AHB2ENR;
(void) tmpreg;
#endif /* DATA_IN_D2_SRAM */
#if defined(DUAL_CORE) && defined(CORE_CM4)
/* 双核设备上为Cortex-M4配置向量表 */
SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET;
#else
/* 禁用FMC back1(开机后默认启用)。
* 这防止了CPU在FMC使用期间的推测性访问,这可能会在24us内阻碍FMC的使用。
* 在此时间内,其他FMC主控(如LTDC)不能使用它! */
FMC_Bank1_R->BTCR[0] = 0x000030D2;
/* 配置向量表位置 */
/* 根据定义,将中断向量表定位到特定的地址,可以是内部的D1 AXI-RAM或者内部FLASH */
#if defined(USER_VECT_TAB_ADDRESS)
SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET;
#endif /* USER_VECT_TAB_ADDRESS */
#endif /*DUAL_CORE && CORE_CM4*/
} // SystemInit函数结束
配合开发手册可以知道大部分是在控制RCC的寄存器


CR寄存器







- 复位三个锁相环PLL1,PLL2,PLL3(默认关闭)
- 关闭除开HSI的其他时钟(默认关闭)
- 关闭DI域以及D2域的时钟(默认关闭)
- 开启HSI时钟(默认不分频为64MHZ)
CFGR寄存器

以上寄存器各个位就不展示了,到手册中看
- 将HSI用作系统时钟
各个域时钟配置D1CFGR,D2CFGR,D3CFGR寄存器





D1CFGR:用于控制 D1 域 CPU 时钟分频系数, 用于控制 rcc_pclk3(APB3) 的分频系数,用于控制 rcc_hclk3 和 rcc_aclk (AHB)的分频系数(复位不分频)
D2CFGR:控制 D2 域 APB1、APB2 时钟分频系数(复位不分频)
D3CFGR:用于控制 D3 域 APB4 时钟分频系数(复位不分频)
锁相环寄存器8个
PLLCKSELR,PLLCFGR,PLL1DIVR,PLL1FRACR,PLL2DIVR,PLL2FRACR,PLL3DIVR,PLL3FRACR

/* Reset PLLCKSELR register */
RCC->PLLCKSELR = 0x02020200;
/* Reset PLLCFGR register */
RCC->PLLCFGR = 0x01FF0000;
/* Reset PLL1DIVR register */
RCC->PLL1DIVR = 0x01010280;
/* Reset PLL1FRACR register */
RCC->PLL1FRACR = 0x00000000;
/* Reset PLL2DIVR register */
RCC->PLL2DIVR = 0x01010280;
/* Reset PLL2FRACR register */
RCC->PLL2FRACR = 0x00000000;
/* Reset PLL3DIVR register */
RCC->PLL3DIVR = 0x01010280;
/* Reset PLL3FRACR register */
RCC->PLL3FRACR = 0x00000000;- 以上都是复位值,禁止输出
再次设置源控制寄存器CR
/* Reset HSEBYP bit */
RCC->CR &= 0xFFFBFFFFU;关闭所有中断
/* Disable all interrupts */
RCC->CIER = 0x00000000;禁用FMC bank1
FMC_Bank1_R->BTCR[0] = 0x000030D2;由此看来SystemInitinit()函数使用HSI作用于启动时钟,并禁止了其他时钟系统以及分频器的开启,也就相当于没有打开外设时钟,关闭了中断以防止后面的过程被中断,并设置了中断向量表的地址,禁用FMC bank1(禁止其他内存使用)
HAL_Init();函数分析
/**
* @brief 初始化HAL库,必须是主程序中执行的第一个指令(在调用任何其他HAL函数之前)。
* 它执行以下操作:
* 配置SysTick以每1毫秒生成一次中断,该中断由HSI提供时钟
* (在此阶段,时钟尚未配置,因此系统运行在内部HSI上,频率为16 MHz)。
* 设置NVIC组优先级为4。
* 调用用户文件 "stm32h7xx_hal_msp.c" 中定义的 HAL_MspInit() 回调函数,
* 以执行全局低级硬件初始化。
*
* @note SysTick被用作HAL_Delay()函数的时间基准,应用程序需要确保SysTick时间基准始终设置为1毫秒,
* 以确保正确的HAL操作。
* @retval HAL状态
*/
//以上的注释可能存在错误,只有在配置了系统滴答定时器时候才会出如上情况,我这里使用的是基本定时器作为系统时钟基准
HAL_StatusTypeDef HAL_Init(void)
{
uint32_t common_system_clock;
#if defined(DUAL_CORE) && defined(CORE_CM4)
/* 为Cortex-M4配置指令缓存和ART加速器 */
__HAL_RCC_ART_CLK_ENABLE(); /* 启用Cortex-M4 ART时钟 */
__HAL_ART_CONFIG_BASE_ADDRESS(0x08100000UL); /* 将Cortex-M4 ART基地址配置到Flash Bank 2 */
__HAL_ART_ENABLE(); /* 启用Cortex-M4 ART */
#endif /* DUAL_CORE && CORE_CM4 */
/* 设置中断组优先级 */
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
/* 更新全局变量SystemCoreClock */
#if defined(RCC_D1CFGR_D1CPRE)
common_system_clock = HAL_RCC_GetSysClockFreq() >> ((D1CorePrescTable[(RCC->D1CFGR & RCC_D1CFGR_D1CPRE)>> RCC_D1CFGR_D1CPRE_Pos]) & 0x1FU);
#else
common_system_clock = HAL_RCC_GetSysClockFreq() >> ((D1CorePrescTable[(RCC->CDCFGR1 & RCC_CDCFGR1_CDCPRE)>> RCC_CDCFGR1_CDCPRE_Pos]) & 0x1FU);
#endif
/* 更新全局变量SystemD2Clock */
#if defined(RCC_D1CFGR_HPRE)
SystemD2Clock = (common_system_clock >> ((D1CorePrescTable[(RCC->D1CFGR & RCC_D1CFGR_HPRE)>> RCC_D1CFGR_HPRE_Pos]) & 0x1FU));
#else
SystemD2Clock = (common_system_clock >> ((D1CorePrescTable[(RCC->CDCFGR1 & RCC_CDCFGR1_HPRE)>> RCC_CDCFGR1_HPRE_Pos]) & 0x1FU));
#endif
#if defined(DUAL_CORE) && defined(CORE_CM4)
SystemCoreClock = SystemD2Clock;
#else
SystemCoreClock = common_system_clock;
#endif /* DUAL_CORE && CORE_CM4 */
/* 使用systick作为时间基准源,并配置1ms的tick(复位后默认时钟为HSI) */
if(HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK)
{
return HAL_ERROR;
}
/* 初始化低级硬件 */
HAL_MspInit();
/* 返回函数状态 */
return HAL_OK;
}
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);- 优先级分组为4 (4位全是抢占优先级,16个优先级0-15)

- 初始化定时器7所在的时钟,并查询当前时钟频率,根据频率来配置定时器开启1ms的中断,基于此中断会提供一个系统延时函数
HAL_MspInit为初始化SYSCFG所在的时钟APB4


SystemClock_Config();函数分析
void SystemClock_Config(void)
{
// 定义并初始化RCC的振荡器和时钟配置结构体
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/* 使能电源配置更新 */
HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);
/* 配置主内部电压调节器的输出电压 */
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
// 等待电压调节器准备就绪
while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
// 使能SYSCFG时钟
__HAL_RCC_SYSCFG_CLK_ENABLE();
// 再次配置电压调节器的输出电压
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0);
// 再次等待电压调节器准备就绪
while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
/* 根据RCC_OscInitTypeDef结构体中的参数初始化RCC振荡器 */
// 配置HSE(高速外部)振荡器为开启状态
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
// 配置PLL(锁相环)为开启状态
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
// 设置PLL的各个分频参数
RCC_OscInitStruct.PLL.PLLM = 5;
RCC_OscInitStruct.PLL.PLLN = 192;
RCC_OscInitStruct.PLL.PLLP = 2;
RCC_OscInitStruct.PLL.PLLQ = 2;
RCC_OscInitStruct.PLL.PLLR = 2;
RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;
RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
RCC_OscInitStruct.PLL.PLLFRACN = 0;
// 如果振荡器配置失败,调用错误处理函数
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** 初始化CPU、AHB和APB总线的时钟 */
// 配置各种时钟类型
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
|RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
// 设置系统时钟源为PLL时钟
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
// 设置各种分频器
RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
// 如果时钟配置失败,调用错误处理函数
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
{
Error_Handler();
}
}
HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);
/* Configure the main internal regulator output voltage
*/
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
__HAL_RCC_SYSCFG_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0);
while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
- 有关电源配置(这一块以后再研究吧,配合低功耗一起)
配置微控制器的电压等级,包括在必要时启用或禁用过载保护功能。这些操作对于微控制器的能效和性能至关重要,特别是在改变运行频率或功耗模式时
RCC_OscInitStruct.PLL.PLLM = 5;
RCC_OscInitStruct.PLL.PLLN = 192;
RCC_OscInitStruct.PLL.PLLP = 2;
RCC_OscInitStruct.PLL.PLLQ = 2;
RCC_OscInitStruct.PLL.PLLR = 2;
对应的时钟如下
RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2; 


RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
RCC_OscInitStruct.PLL.PLLFRACN = 0;
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
|RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;- ClockType 指定配置的时钟类型
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
PeriphCommonClock_Config();函数分析
void PeriphCommonClock_Config(void)
{
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
/** Initializes the peripherals clock
*/
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USB|RCC_PERIPHCLK_ADC
|RCC_PERIPHCLK_SDMMC|RCC_PERIPHCLK_SPI1
|RCC_PERIPHCLK_LTDC;
PeriphClkInitStruct.PLL2.PLL2M = 2;
PeriphClkInitStruct.PLL2.PLL2N = 16;
PeriphClkInitStruct.PLL2.PLL2P = 4;
PeriphClkInitStruct.PLL2.PLL2Q = 1;
PeriphClkInitStruct.PLL2.PLL2R = 1;
PeriphClkInitStruct.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_3;
PeriphClkInitStruct.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE;
PeriphClkInitStruct.PLL2.PLL2FRACN = 0;
PeriphClkInitStruct.PLL3.PLL3M = 5;
PeriphClkInitStruct.PLL3.PLL3N = 50;
PeriphClkInitStruct.PLL3.PLL3P = 1;
PeriphClkInitStruct.PLL3.PLL3Q = 5;
PeriphClkInitStruct.PLL3.PLL3R = 10;
PeriphClkInitStruct.PLL3.PLL3RGE = RCC_PLL3VCIRANGE_2;
PeriphClkInitStruct.PLL3.PLL3VCOSEL = RCC_PLL3VCOWIDE;
PeriphClkInitStruct.PLL3.PLL3FRACN = 0;
PeriphClkInitStruct.SdmmcClockSelection = RCC_SDMMCCLKSOURCE_PLL2;
PeriphClkInitStruct.Spi123ClockSelection = RCC_SPI123CLKSOURCE_PLL2;
PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_PLL3;
PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_PLL2;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
}理解SystemClock_Config()函数这个也就差不多了
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USB|RCC_PERIPHCLK_ADC
|RCC_PERIPHCLK_SDMMC|RCC_PERIPHCLK_SPI1
|RCC_PERIPHCLK_LTDC;
PeriphClkInitStruct.PLL2.PLL2M = 2;
PeriphClkInitStruct.PLL2.PLL2N = 16;
PeriphClkInitStruct.PLL2.PLL2P = 4;
PeriphClkInitStruct.PLL2.PLL2Q = 1;
PeriphClkInitStruct.PLL2.PLL2R = 1;
PeriphClkInitStruct.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_3;
PeriphClkInitStruct.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE;
PeriphClkInitStruct.PLL2.PLL2FRACN = 0;
PeriphClkInitStruct.PLL3.PLL3M = 5;
PeriphClkInitStruct.PLL3.PLL3N = 50;
PeriphClkInitStruct.PLL3.PLL3P = 1;
PeriphClkInitStruct.PLL3.PLL3Q = 5;
PeriphClkInitStruct.PLL3.PLL3R = 10;
PeriphClkInitStruct.PLL3.PLL3RGE = RCC_PLL3VCIRANGE_2;
PeriphClkInitStruct.PLL3.PLL3VCOSEL = RCC_PLL3VCOWIDE;
PeriphClkInitStruct.PLL3.PLL3FRACN = 0;- 上面的配置对应着下图的部分

具体寄存器可在如下部分找到

PeriphClkInitStruct.SdmmcClockSelection = RCC_SDMMCCLKSOURCE_PLL2;
PeriphClkInitStruct.Spi123ClockSelection = RCC_SPI123CLKSOURCE_PLL2;
PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_PLL3;
PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_PLL2;此外这里有单独的外设配置对应着经过锁相环输出的频率,只是确定哪一个锁相环输出,并没有确定具体是DIVP,DIVQ,DIVR哪一个,确定是哪一个应该是由后面的配置决定

总结:
SystemInit() 为main函数之前运行的函数,依赖库文件,主要有以下作用
- 确定是否开启FPU
- 启用内部时钟HSI 复位时钟
- 复位时钟,关闭锁相环
- 关闭中断
- 禁用FMC bank1
- 设置中断向量表地址
HAL_Init()
- 设置中断优先级
- 设置定时器提供系统时基
- 开启SYSCFG(系统配置控制器)所在时钟单元
SystemClock_Config() 这一步配置完成就确定了各个总线的时钟
- 电源配置
- 初始化锁相环PLL配置(这里对应着PLL1,H7系列有三个锁相环)
- 初始化分频器(对部分总线进行分频)
PeriphCommonClock_Config() 这一步配置PLL2以及PLL3分频器和时钟在PLL上的外设
- 配置PLL2
- 配置PLL3
- 配置时钟在PLL上的外设