HAL库的时钟初始化流程

SystemInit函数

该函数在启动文件中调用,不同的库版本可能不同,以1.8.0的为例,代码如下

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 */
#if !defined(STM32F105xC) && !defined(STM32F107xC)
  RCC->CFGR &= (uint32_t)0xF8FF0000;
#else
  RCC->CFGR &= (uint32_t)0xF0FF0000;
#endif /* STM32F105xC */   

  /* 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;

#if defined(STM32F105xC) || defined(STM32F107xC)
  /* 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(STM32F100xB) || defined(STM32F100xE)
  /* 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 /* STM32F105xC */

#if defined(STM32F100xE) || defined(STM32F101xE) || defined(STM32F101xG) || defined(STM32F103xE) || defined(STM32F103xG)
  #ifdef DATA_IN_ExtSRAM
    SystemInit_ExtMemCtl(); 
  #endif /* DATA_IN_ExtSRAM */
#endif 

#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 
}

这段代码是STM32的系统初始化函数,主要作用是将芯片的各个模块初始化为默认状态,包括时钟配置、中断配置、向量表配置等。具体步骤如下:

  1. 将复位时钟配置恢复为默认状态,开启HSI时钟。

  2. 将时钟分频配置、HSE时钟、PLL时钟、USB/OTG时钟等相关寄存器恢复为默认状态。

  3. 对于一些型号的芯片,还需要将PLL2和PLL3的相关寄存器恢复为默认状态。

  4. 禁用所有中断,并清除挂起中断标志。

  5. 将向量表地址配置到内部SRAM或Flash中。

  6. 如果需要使用外部SRAM,则需要调用SystemInit_ExtMemCtl函数进行外部SRAM的初始化。 此函数通常在芯片启动时被调用,以确保芯片正常运行

注意:此段库函数代码并没有进行实质上的时钟设置,比如选择系统时钟的时钟源,实际上的时钟初始化需要我们自行编写或使用CubeMX图像化设置生成

时钟初始化函数

此函数自行编写,在主函数中手动调用,以正点原子的为例,此函数在sys.c,入口参数PLL倍频系数,若HSE为8M,欲使系统时钟72M,则设置为9

void Stm32_Clock_Init(u32 PLL)
{
    HAL_StatusTypeDef ret = HAL_OK;
    RCC_OscInitTypeDef RCC_OscInitStructure; 
    RCC_ClkInitTypeDef RCC_ClkInitStructure;

    RCC_OscInitStructure.OscillatorType=RCC_OSCILLATORTYPE_HSE;        //时钟源为HSE
    RCC_OscInitStructure.HSEState=RCC_HSE_ON;                          //打开HSE
    RCC_OscInitStructure.HSEPredivValue=RCC_HSE_PREDIV_DIV1;        //HSE预分频
    RCC_OscInitStructure.PLL.PLLState=RCC_PLL_ON;                    //打开PLL
    RCC_OscInitStructure.PLL.PLLSource=RCC_PLLSOURCE_HSE;            //PLL时钟源选择HSE
    RCC_OscInitStructure.PLL.PLLMUL=PLL;                             //主PLL倍频因子
    ret=HAL_RCC_OscConfig(&RCC_OscInitStructure);//初始化

    if(ret!=HAL_OK) while(1);

    //选中PLL作为系统时钟源并且配置HCLK,PCLK1和PCLK2
    RCC_ClkInitStructure.ClockType=(RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2);
    RCC_ClkInitStructure.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK;        //设置系统时钟时钟源为PLL
    RCC_ClkInitStructure.AHBCLKDivider=RCC_SYSCLK_DIV1;                //AHB分频系数为1
    RCC_ClkInitStructure.APB1CLKDivider=RCC_HCLK_DIV2;                 //APB1分频系数为2
    RCC_ClkInitStructure.APB2CLKDivider=RCC_HCLK_DIV1;                 //APB2分频系数为1
    ret=HAL_RCC_ClockConfig(&RCC_ClkInitStructure,FLASH_LATENCY_2);    //同时设置FLASH延时周期为2WS,也就是3个CPU周期。

    if(ret!=HAL_OK) while(1);
}
  1. 定义RCC_OscInitTypeDef结构体,配置时钟源为外部晶体振荡器HSE,打开HSE,设置HSE预分频系数为1,打开PLL,将PLL时钟源选择为HSE,设置主PLL倍频因子为用户提供的PLL值,这里有一个问题是未将未使用的结构体成员设置为0,可能会出问题。

  2. 调用HAL_RCC_OscConfig函数进行时钟初始化,如果初始化失败,则进入死循环。

  3. 定义RCC_ClkInitTypeDef结构体,选中PLL作为系统时钟源并且配置SYSCLK(系统时钟),HCLK(AHB时钟),PCLK1(APB1)和PCLK2(APB2)

  4. 将系统时钟源选择为PLL,设置AHB分频系数为1,这样AHB时钟就达到72M,APB1分频系数为2,也就是使PCLK1达到最大的36M,APB2分频系数为也就是使PCLK2达到最大的72M,

  5. 调用HAL_RCC_ClockConfig函数进行时钟配置,同时设置FLASH延时周期为2WS,也就是3个CPU周期。如果配置失败,则进入死循环。

  6. 函数执行完毕后,系统时钟将被配置为PLL时钟,并根据分频系数得到HCLK,PCLK1,PCLK2时钟。

Last updated