观前提醒:本章以基于f105的点灯项目为例,时钟树配置如下图。分析了void SystemClock_Config(void)以及HAL_RCC_ClockConfig()。
仅配置PC0为推挽输出
需要注意的是105与103系列的时钟树有些差异。
105系列时钟树

103系列时钟树

void SystemClock_Config(void)

先赏析一下源码,SystemClock_Config函数是由CubeMX根据时钟树配置自动生成的,包括配置系统时钟,配置RCC振荡器和时钟初始化。将系统时钟设置为使用HSE振荡器作为输入,使用PLL进行倍频,最终得到一个稳定的系统时钟。

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.Prediv1Source = RCC_PREDIV1_SOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  RCC_OscInitStruct.PLL2.PLL2State = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure the Systick interrupt time
  */
  __HAL_RCC_PLLI2S_ENABLE();
}

下面是对代码逐句的分析:

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

定义了SystemClock_Config函数,同时声明了RCC_OscInitStructRCC_ClkInitStruct两个结构体变量。

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.Prediv1Source = RCC_PREDIV1_SOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  RCC_OscInitStruct.PLL2.PLL2State = RCC_PLL_NONE;

设置RCC_OscInitStruct结构体的各个成员变量,用于配置RCC振荡器的参数。这些参数包括振荡器类型、HSE状态、HSE预分频值、HSI状态、预分频源、PLL状态、PLL时钟源、PLL倍频因子和PLL2状态。

  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

通过调用HAL_RCC_OscConfig函数配置RCC振荡器,并检查配置操作是否成功。如果配置失败,将调用Error_Handler函数进行错误处理。

  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

设置RCC_ClkInitStruct结构体的各个成员变量,用于配置CPU、AHB和APB总线的时钟。这些参数包括时钟类型、SYSCLK源、AHB时钟分频系数、APB1时钟分频系数和APB2时钟分频系数。

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }

通过调用HAL_RCC_ClockConfig函数配置CPU、AHB和APB总线的时钟,并指定FLASH访问延迟为FLASH_LATENCY_2。如果配置失败,将调用Error_Handler函数进行错误处理。

  __HAL_RCC_PLLI2S_ENABLE();
}

调用__HAL_RCC_PLLI2S_ENABLE函数使能PLLI2S。

HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, uint32_t FLatency)

此函数有150行左右,所以这里将源码放在文末,分析时将逐句分析重点代码。(建议在阅读分析内容前,大致看一遍完整的函数源码)
从功能性上,函数可以分为以下几个部分:

  1. 参数检查和初始化:对输入参数进行检查,包括检查是否为空指针以及参数的合法性。

  2. HCLK配置:根据输入的时钟类型,设置高级外设总线(APB1、APB2)的分频器,然后设置新的HCLK时钟分频器。

  3. SYSCLK配置:根据输入的时钟类型,选择合适的时钟源作为系统时钟源,检查所选的时钟源是否就绪,并设置系统时钟源。然后等待系统时钟源切换完成。

  4. FLASH_ACR_LATENCY配置:根据输入的时钟延迟值,调整Flash存储器的等待状态,以确保正确读取Flash数据。

  5. PCLK1配置:根据输入的时钟类型,设置APB1总线的分频器。

  6. PCLK2配置:根据输入的时钟类型,设置APB2总线的分频器。

  7. 更新SystemCoreClock变量:根据新的时钟配置更新SystemCoreClock全局变量。

  8. 配置时间基准源:根据新的系统时钟配置,重新初始化时间基准。

  9. 返回状态:返回函数执行的状态,表明是否成功配置时钟。

下面我们按照顺序来开始分析此函数:

当分析这段代码时,我们可以逐行解释其功能和操作。

uint32_t tickstart;

这是一个名为tickstartuint32_t类型的变量。

if (RCC_ClkInitStruct == NULL)
{
  return HAL_ERROR;
}

这个条件语句检查指针RCC_ClkInitStruct是否为空。如果为空,表示传入的指针无效,函数会返回HAL_ERROR

assert_param(IS_RCC_CLOCKTYPE(RCC_ClkInitStruct->ClockType));
assert_param(IS_FLASH_LATENCY(FLatency));

这两行使用assert_param宏来检查输入参数的有效性。IS_RCC_CLOCKTYPEIS_FLASH_LATENCY是用于验证参数的宏,如果参数无效,则会触发一个断言错误。

#if defined(FLASH_ACR_LATENCY)

这是一个条件编译指令。它检查是否定义了符号FLASH_ACR_LATENCY,如果定义了,则执行接下来的代码块。

if (FLatency > __HAL_FLASH_GET_LATENCY())
{
  __HAL_FLASH_SET_LATENCY(FLatency);
  if (__HAL_FLASH_GET_LATENCY() != FLatency)
  {
    return HAL_ERROR;
  }
}

这个条件语句检查传入的延迟参数FLatency是否大于当前的FLASH访问延迟。如果是,表示需要增加等待状态以适应更高的CPU频率。函数使用__HAL_FLASH_SET_LATENCY宏将新的等待状态数值写入FLASH_ACR寄存器的LATENCY位。然后,它读取FLASH_ACR寄存器的LATENCY位,以确保新的等待状态数值生效。如果读取到的值与设置的值不相等,表示设置失败,函数会返回HAL_ERROR

#endif /* FLASH_ACR_LATENCY */

这是一个条件编译指令的结束标记。它表示前面的代码块只在定义了FLASH_ACR_LATENCY时执行。


if (((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_HCLK) == RCC_CLOCKTYPE_HCLK)
  {
    /* Set the highest APBx dividers in order to ensure that we do not go through
    a non-spec phase whatever we decrease or increase HCLK. */
    if (((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK1) == RCC_CLOCKTYPE_PCLK1)
    {
      MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, RCC_HCLK_DIV16);
    }

    if (((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK2) == RCC_CLOCKTYPE_PCLK2)
    {
      MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE2, (RCC_HCLK_DIV16 << 3));
    }

    /* Set the new HCLK clock divider */
    assert_param(IS_RCC_HCLK(RCC_ClkInitStruct->AHBCLKDivider));
    MODIFY_REG(RCC->CFGR, RCC_CFGR_HPRE, RCC_ClkInitStruct->AHBCLKDivider);
  }

这段代码使用了条件语句来判断是否需要配置HCLK(AHB总线时钟)的分频系数。

if (((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_HCLK) == RCC_CLOCKTYPE_HCLK)
{
  // 执行以下操作
}

这个条件语句检查传入的RCC_ClkInitStruct->ClockType是否包含RCC_CLOCKTYPE_HCLK位,即判断是否需要配置HCLK。如果条件成立,表示需要配置HCLK分频系数,将执行以下操作:

if (((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK1) == RCC_CLOCKTYPE_PCLK1)
{
  MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, RCC_HCLK_DIV16);
}

if (((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK2) == RCC_CLOCKTYPE_PCLK2)
{
  MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE2, (RCC_HCLK_DIV16 << 3));
}

assert_param(IS_RCC_HCLK(RCC_ClkInitStruct->AHBCLKDivider));
MODIFY_REG(RCC->CFGR, RCC_CFGR_HPRE, RCC_ClkInitStruct->AHBCLKDivider);

首先,它检查传入的RCC_ClkInitStruct->ClockType是否包含RCC_CLOCKTYPE_PCLK1位,即判断是否需要配置PCLK1(APB1总线时钟)的分频系数。如果条件成立,将使用MODIFY_REG宏将RCC_CFGR_PPRE1寄存器的值修改为RCC_HCLK_DIV16,即将PCLK1的分频系数设置为16。

接下来,它检查传入的RCC_ClkInitStruct->ClockType是否包含RCC_CLOCKTYPE_PCLK2位,即判断是否需要配置PCLK2(APB2总线时钟)的分频系数。如果条件成立,将使用MODIFY_REG宏将RCC_CFGR_PPRE2寄存器的值修改为(RCC_HCLK_DIV16 << 3),即将PCLK2的分频系数设置为16。

最后,它使用assert_param宏检查RCC_ClkInitStruct->AHBCLKDivider的有效性,然后使用MODIFY_REG宏将RCC_CFGR_HPRE寄存器的值修改为RCC_ClkInitStruct->AHBCLKDivider,即将HCLK的分频系数设置为传入的值。

这段代码的功能是根据传入的时钟类型判断是否需要配置HCLK的分频系数,并根据需要修改PCLK1和PCLK2的分频系数以及HCLK的分频系数。


继续看下一段代码,
这段代码使用条件语句来判断是否需要配置SYSCLK(系统时钟)以及相关的操作。

if (((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_SYSCLK) == RCC_CLOCKTYPE_SYSCLK)
{
  // 执行以下操作
}

这个条件语句检查传入的RCC_ClkInitStruct->ClockType是否包含RCC_CLOCKTYPE_SYSCLK位,即判断是否需要配置SYSCLK。如果条件成立,表示需要配置SYSCLK,将执行以下操作:

assert_param(IS_RCC_SYSCLKSOURCE(RCC_ClkInitStruct->SYSCLKSource));

if (RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_HSE)
{
  if (__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) == RESET)
  {
    return HAL_ERROR;
  }
}
else if (RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_PLLCLK)
{
  if (__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) == RESET)
  {
    return HAL_ERROR;
  }
}
else
{
  if (__HAL_RCC_GET_FLAG(RCC_FLAG_HSIRDY) == RESET)
  {
    return HAL_ERROR;
  }
}

__HAL_RCC_SYSCLK_CONFIG(RCC_ClkInitStruct->SYSCLKSource);

tickstart = HAL_GetTick();

while (__HAL_RCC_GET_SYSCLK_SOURCE() != (RCC_ClkInitStruct->SYSCLKSource << RCC_CFGR_SWS_Pos))
{
  if ((HAL_GetTick() - tickstart) > CLOCKSWITCH_TIMEOUT_VALUE)
  {
    return HAL_TIMEOUT;
  }
}

首先,它使用assert_param宏检查RCC_ClkInitStruct->SYSCLKSource的有效性。

然后,根据RCC_ClkInitStruct->SYSCLKSource的值进行不同的操作。如果SYSCLKSourceRCC_SYSCLKSOURCE_HSE,即选择HSE作为系统时钟源,它会检查HSE就绪标志位RCC_FLAG_HSERDY是否被置位,如果未就绪,函数返回HAL_ERROR

如果SYSCLKSourceRCC_SYSCLKSOURCE_PLLCLK,即选择PLL作为系统时钟源,它会检查PLL就绪标志位RCC_FLAG_PLLRDY是否被置位,如果未就绪,函数返回HAL_ERROR

如果SYSCLKSource为其他值,即选择HSI作为系统时钟源,它会检查HSI就绪标志位RCC_FLAG_HSIRDY是否被置位,如果未就绪,函数返回HAL_ERROR

接下来,它使用__HAL_RCC_SYSCLK_CONFIG宏将系统时钟源配置为RCC_ClkInitStruct->SYSCLKSource

然后,它获取当前的启动时间戳(tickstart)。

在一个循环中,它检查当前的系统时钟源是否与目标源相匹配,如果不匹配,继续等待。如果超过了设定的超时时间(CLOCKSWITCH_TIMEOUT_VALUE),函数返回HAL_TIMEOUT

最后,这段代码使用条件编译指令结束,如果定义了符号FLASH_ACR_LATENCY,则执行下面的代码块。

#if defined(FLASH_ACR_LATENCY)
if (FLatency < __HAL_FLASH_GET_LATENCY())
{
  __HAL_FLASH_SET_LATENCY(FLatency);
  if (__HAL_FLASH_GET_LATENCY() != FLatency)
  {
    return HAL_ERROR;
  }
}
#endif /* FLASH_ACR_LATENCY */

这个条件语句检查传入的延迟参数FLatency是否小于当前的FLASH访问延迟。如果是,表示需要减少等待状态以适应较低的CPU频率。函数使用__HAL_FLASH_SET_LATENCY宏将新的等待状态数值写入FLASH_ACR寄存器的LATENCY位。然后,它读取FLASH_ACR寄存器的LATENCY位,以确保新的等待状态数值生效。如果读取到的值与设置的值不相等,表示设置失败,函数会返回HAL_ERROR


if (((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK1) == RCC_CLOCKTYPE_PCLK1)
  {
    assert_param(IS_RCC_PCLK(RCC_ClkInitStruct->APB1CLKDivider));
    MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, RCC_ClkInitStruct->APB1CLKDivider);
  }

  /*-------------------------- PCLK2 Configuration ---------------------------*/
  if (((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK2) == RCC_CLOCKTYPE_PCLK2)
  {
    assert_param(IS_RCC_PCLK(RCC_ClkInitStruct->APB2CLKDivider));
    MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE2, ((RCC_ClkInitStruct->APB2CLKDivider) << 3));
  }

  /* Update the SystemCoreClock global variable */
  SystemCoreClock = HAL_RCC_GetSysClockFreq() >> AHBPrescTable[(RCC->CFGR & RCC_CFGR_HPRE) >> RCC_CFGR_HPRE_Pos];

  /* Configure the source of time base considering new system clocks settings*/
  HAL_InitTick(uwTickPrio);

  return HAL_OK;
}

该代码段根据给定的时钟配置参数RCC_ClkInitStruct来配置PCLK1和PCLK2时钟。以下是代码的详细分析:

  1. 首先,通过按位与操作符(&)将RCC_ClkInitStruct的ClockType字段与RCC_CLOCKTYPE_PCLK1进行比较,判断是否需要配置PCLK1时钟。如果两者相等,则进入if语句块。
  2. 在if语句块中,使用assert_param宏来验证RCC_ClkInitStruct的APB1CLKDivider字段是否是有效的PCLK1分频器值。如果验证通过,则使用MODIFY_REG宏来修改RCC->CFGR寄存器的RCC_CFGR_PPRE1位域,将其值设置为RCC_ClkInitStruct的APB1CLKDivider字段的值,从而配置PCLK1时钟。
  3. 然后,通过类似的方式,检查是否需要配置PCLK2时钟。如果需要配置PCLK2时钟,则执行与PCLK1时钟配置类似的操作,但是需要将RCC_CFGR_PPRE2位域的值左移3位。
  4. 接下来,通过调用HAL_RCC_GetSysClockFreq函数获取系统时钟频率,并根据RCC->CFGR寄存器的RCC_CFGR_HPRE位域的值选择合适的AHB预分频器,然后将结果右移,得到新的SystemCoreClock值。
  5. 然后,根据新的系统时钟设置,调用HAL_InitTick函数来配置时间基准。
  6. 最后,返回HAL_OK表示函数执行成功。

总结:该代码段根据给定的时钟配置参数来配置PCLK1和PCLK2时钟,并更新SystemCoreClock变量。

HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, uint32_t FLatency)源码

HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef  *RCC_ClkInitStruct, uint32_t FLatency)
{
  uint32_t tickstart;

  /* Check Null pointer */
  if (RCC_ClkInitStruct == NULL)
  {
    return HAL_ERROR;
  }

  /* Check the parameters */
  assert_param(IS_RCC_CLOCKTYPE(RCC_ClkInitStruct->ClockType));
  assert_param(IS_FLASH_LATENCY(FLatency));

  /* To correctly read data from FLASH memory, the number of wait states (LATENCY)
  must be correctly programmed according to the frequency of the CPU clock
    (HCLK) of the device. */

#if defined(FLASH_ACR_LATENCY)
  /* Increasing the number of wait states because of higher CPU frequency */
  if (FLatency > __HAL_FLASH_GET_LATENCY())
  {
    /* Program the new number of wait states to the LATENCY bits in the FLASH_ACR register */
    __HAL_FLASH_SET_LATENCY(FLatency);

    /* Check that the new number of wait states is taken into account to access the Flash
    memory by reading the FLASH_ACR register */
    if (__HAL_FLASH_GET_LATENCY() != FLatency)
  {
    return HAL_ERROR;
  }
}

#endif /* FLASH_ACR_LATENCY */
/*-------------------------- HCLK Configuration --------------------------*/
if (((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_HCLK) == RCC_CLOCKTYPE_HCLK)
  {
    /* Set the highest APBx dividers in order to ensure that we do not go through
    a non-spec phase whatever we decrease or increase HCLK. */
    if (((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK1) == RCC_CLOCKTYPE_PCLK1)
    {
      MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, RCC_HCLK_DIV16);
    }

    if (((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK2) == RCC_CLOCKTYPE_PCLK2)
    {
      MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE2, (RCC_HCLK_DIV16 << 3));
    }

    /* Set the new HCLK clock divider */
    assert_param(IS_RCC_HCLK(RCC_ClkInitStruct->AHBCLKDivider));
    MODIFY_REG(RCC->CFGR, RCC_CFGR_HPRE, RCC_ClkInitStruct->AHBCLKDivider);
  }

  /*------------------------- SYSCLK Configuration ---------------------------*/
  if (((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_SYSCLK) == RCC_CLOCKTYPE_SYSCLK)
  {
    assert_param(IS_RCC_SYSCLKSOURCE(RCC_ClkInitStruct->SYSCLKSource));

    /* HSE is selected as System Clock Source */
    if (RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_HSE)
    {
      /* Check the HSE ready flag */
      if (__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) == RESET)
      {
        return HAL_ERROR;
      }
    }
    /* PLL is selected as System Clock Source */
    else if (RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_PLLCLK)
    {
      /* Check the PLL ready flag */
      if (__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) == RESET)
      {
        return HAL_ERROR;
      }
    }
    /* HSI is selected as System Clock Source */
    else
    {
      /* Check the HSI ready flag */
      if (__HAL_RCC_GET_FLAG(RCC_FLAG_HSIRDY) == RESET)
      {
        return HAL_ERROR;
      }
    }
    __HAL_RCC_SYSCLK_CONFIG(RCC_ClkInitStruct->SYSCLKSource);

    /* Get Start Tick */
    tickstart = HAL_GetTick();

    while (__HAL_RCC_GET_SYSCLK_SOURCE() != (RCC_ClkInitStruct->SYSCLKSource << RCC_CFGR_SWS_Pos))
    {
      if ((HAL_GetTick() - tickstart) > CLOCKSWITCH_TIMEOUT_VALUE)
      {
        return HAL_TIMEOUT;
      }
    }
  }

#if defined(FLASH_ACR_LATENCY)
  /* Decreasing the number of wait states because of lower CPU frequency */
  if (FLatency < __HAL_FLASH_GET_LATENCY())
  {
    /* Program the new number of wait states to the LATENCY bits in the FLASH_ACR register */
    __HAL_FLASH_SET_LATENCY(FLatency);

    /* Check that the new number of wait states is taken into account to access the Flash
    memory by reading the FLASH_ACR register */
    if (__HAL_FLASH_GET_LATENCY() != FLatency)
  {
    return HAL_ERROR;
  }
}
#endif /* FLASH_ACR_LATENCY */

/*-------------------------- PCLK1 Configuration ---------------------------*/
if (((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK1) == RCC_CLOCKTYPE_PCLK1)
  {
    assert_param(IS_RCC_PCLK(RCC_ClkInitStruct->APB1CLKDivider));
    MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, RCC_ClkInitStruct->APB1CLKDivider);
  }

  /*-------------------------- PCLK2 Configuration ---------------------------*/
  if (((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK2) == RCC_CLOCKTYPE_PCLK2)
  {
    assert_param(IS_RCC_PCLK(RCC_ClkInitStruct->APB2CLKDivider));
    MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE2, ((RCC_ClkInitStruct->APB2CLKDivider) << 3));
  }

  /* Update the SystemCoreClock global variable */
  SystemCoreClock = HAL_RCC_GetSysClockFreq() >> AHBPrescTable[(RCC->CFGR & RCC_CFGR_HPRE) >> RCC_CFGR_HPRE_Pos];

  /* Configure the source of time base considering new system clocks settings*/
  HAL_InitTick(uwTickPrio);

  return HAL_OK;
}