深入学习STM32 HAL库中RCC初始化函数SystemClock_Config()的梳理与分析(一)

目录

一、PLL主时钟初始化

1.1 时钟使能

 1.2 配置好主时钟配置结构体

1.3 将配置好的值写入到对应的寄存器、初始化PLL主时钟;

1.3.1 __HAL_RCC_HSE_CONFIG(RCC_OscInitStruct->HSEState)分析: 

1.3.2 给PLL相关寄存器赋值:

二、外设时钟初始化

2.1等待周期的验证和写入;

2.2 HCLK配置

2.3 SYSCLK配置、时钟源选择

2.3.1  PLL时钟就绪检测和__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY)宏定义的分析

2.3.2 系统时钟源选择

2.4PCLK1和PCLK2配置

2.5 更新hal库参数

三、总结


本人使用的单片机stm32f407vg,代码来源stm32CubeMx。

时钟配置如下

 主函数调用SystemClock_Config();,这也是RCC初始化函数;

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

  /** Configure the main internal regulator output voltage 
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 25;
  RCC_OscInitStruct.PLL.PLLN = 144;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses 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_DIV2;

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

一、PLL主时钟初始化

1.1 时钟使能

__HAL_RCC_PWR_CLK_ENABLE();

#define __HAL_RCC_PWR_CLK_ENABLE()     do { \
                                    __IO uint32_t tmpreg = 0x00U; \
                                    SET_BIT(RCC->APB1ENR, RCC_APB1ENR_PWREN);\
                                    /* Delay after an RCC peripheral clock enabling */ \
                                    tmpreg = READ_BIT(RCC->APB1ENR, RCC_APB1ENR_PWREN);\
                                    UNUSED(tmpreg); \
                                      } while(0U)

#define SET_BIT(REG, BIT)     ((REG) |= (BIT))

 #define READ_BIT(REG, BIT)    ((REG) & (BIT))

#define RCC_APB1ENR_PWREN_Pos              (28U)                               
#define RCC_APB1ENR_PWREN_Msk              (0x1UL<<RCC_APB1ENR_PWREN_Pos) 
#define RCC_APB1ENR_PWREN                  RCC_APB1ENR_PWREN_Msk    

把0x1左移28位,即使能电源接口时钟;

__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

这个函数是调压器输出电压级别选择,选择级别1模式(默认的模式),暂时不了解有什么作用;

 1.2 配置好主时钟配置结构体

 32的时钟树,暂时用到这些;

 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
 RCC_OscInitStruct.HSEState = RCC_HSE_ON;

#define RCC_OSCILLATORTYPE_NONE            0x00000000U
#define RCC_OSCILLATORTYPE_HSE             0x00000001U
#define RCC_OSCILLATORTYPE_HSI             0x00000002U
#define RCC_OSCILLATORTYPE_LSE             0x00000004U
#define RCC_OSCILLATORTYPE_LSI             0x00000008U

#define RCC_HSE_OFF                      0x00000000U
#define RCC_HSE_ON                       RCC_CR_HSEON
#define RCC_HSE_BYPASS               ((uint32_t)(RCC_CR_HSEBYP | RCC_CR_HSEON))

分别是配置时钟源为(HSE\HSI\LSE\LSI); 选择HSE时钟状态(使能、关闭、旁路)

  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 25;
  RCC_OscInitStruct.PLL.PLLN = 144;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;

#define RCC_PLL_NONE                      ((uint8_t)0x00)
#define RCC_PLL_OFF                       ((uint8_t)0x01)
#define RCC_PLL_ON                        ((uint8_t)0x02)

#define RCC_PLLSOURCE_HSI                RCC_PLLCFGR_PLLSRC_HSI
#define RCC_PLLSOURCE_HSE                RCC_PLLCFGR_PLLSRC_HSE

#define RCC_PLLCFGR_PLLSRC_HSE_Pos         (22U)                               
#define RCC_PLLCFGR_PLLSRC_HSE_Msk   

                                                                 (0x1UL <<RCC_PLLCFGR_PLLSRC_HSE_Pos) 
#define RCC_PLLCFGR_PLLSRC_HSE             RCC_PLLCFGR_PLLSRC_HSE_Msk

 #define RCC_PLLP_DIV2                  0x00000002U
#define RCC_PLLP_DIV4                  0x00000004U
#define RCC_PLLP_DIV6                  0x00000006U
#define RCC_PLLP_DIV8                  0x00000008U

分别为PLL时钟(使能、关闭);PLL时钟源选择(HSE\HSI);PLL M\N\P,Q分频器赋值;

1.3 将配置好的值写入到对应的寄存器、初始化PLL主时钟;

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

调用这个函数进行赋值,并通过返回值判断是否初始化成功;

代码很长,只留下HSE和PLL配置部分;

__weak HAL_StatusTypeDef HAL_RCC_OscConfig(RCC_OscInitTypeDef  *RCC_OscInitStruct)
{
  uint32_t tickstart, pll_config;

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

  /* Check the parameters */
  assert_param(IS_RCC_OSCILLATORTYPE(RCC_OscInitStruct->OscillatorType));
  /*------------------------------- HSE Configuration ------------------------*/
  if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_HSE) == RCC_OSCILLATORTYPE_HSE)
  {
    /* Check the parameters */
    assert_param(IS_RCC_HSE(RCC_OscInitStruct->HSEState));
    /* When the HSE is used as system clock or clock source for PLL in these cases HSE will not disabled */
    if((__HAL_RCC_GET_SYSCLK_SOURCE() == RCC_CFGR_SWS_HSE) ||\
      ((__HAL_RCC_GET_SYSCLK_SOURCE() == RCC_CFGR_SWS_PLL) && ((RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) == RCC_PLLCFGR_PLLSRC_HSE)))
    {
      if((__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) != RESET) && (RCC_OscInitStruct->HSEState == RCC_HSE_OFF))
      {
        return HAL_ERROR;
      }
    }
    else
    {
      /* Set the new HSE configuration ---------------------------------------*/
      __HAL_RCC_HSE_CONFIG(RCC_OscInitStruct->HSEState);

      /* Check the HSE State */
      if((RCC_OscInitStruct->HSEState) != RCC_HSE_OFF)
      {
        /* Get Start Tick */
        tickstart = HAL_GetTick();

        /* Wait till HSE is ready */
        while(__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) == RESET)
        {
          if((HAL_GetTick() - tickstart ) > HSE_TIMEOUT_VALUE)
          {
            return HAL_TIMEOUT;
          }
        }
      }
      else
      {
        /* Get Start Tick */
        tickstart = HAL_GetTick();

        /* Wait till HSE is bypassed or disabled */
        while(__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) != RESET)
        {
          if((HAL_GetTick() - tickstart ) > HSE_TIMEOUT_VALUE)
          {
            return HAL_TIMEOUT;
          }
        }
      }
    }
  }
/*-------------------------------- PLL Configuration -----------------------*/
  /* Check the parameters */
  assert_param(IS_RCC_PLL(RCC_OscInitStruct->PLL.PLLState));
  if ((RCC_OscInitStruct->PLL.PLLState) != RCC_PLL_NONE)
  {
    /* Check if the PLL is used as system clock or not */
    if(__HAL_RCC_GET_SYSCLK_SOURCE() != RCC_CFGR_SWS_PLL)
    {
      if((RCC_OscInitStruct->PLL.PLLState) == RCC_PLL_ON)
      {
        /* Check the parameters */
        assert_param(IS_RCC_PLLSOURCE(RCC_OscInitStruct->PLL.PLLSource));
        assert_param(IS_RCC_PLLM_VALUE(RCC_OscInitStruct->PLL.PLLM));
        assert_param(IS_RCC_PLLN_VALUE(RCC_OscInitStruct->PLL.PLLN));
        assert_param(IS_RCC_PLLP_VALUE(RCC_OscInitStruct->PLL.PLLP));
        assert_param(IS_RCC_PLLQ_VALUE(RCC_OscInitStruct->PLL.PLLQ));

        /* Disable the main PLL. */
        __HAL_RCC_PLL_DISABLE();

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

        /* Wait till PLL is ready */
        while(__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) != RESET)
        {
          if((HAL_GetTick() - tickstart ) > PLL_TIMEOUT_VALUE)
          {
            return HAL_TIMEOUT;
          }
        }

        /* Configure the main PLL clock source, multiplication and division factors. */
        WRITE_REG(RCC->PLLCFGR, (RCC_OscInitStruct->PLL.PLLSource                                            | \
                                 RCC_OscInitStruct->PLL.PLLM                                                 | \
                                 (RCC_OscInitStruct->PLL.PLLN << RCC_PLLCFGR_PLLN_Pos)             | \
                                 (((RCC_OscInitStruct->PLL.PLLP >> 1U) - 1U) << RCC_PLLCFGR_PLLP_Pos) | \
                                 (RCC_OscInitStruct->PLL.PLLQ << RCC_PLLCFGR_PLLQ_Pos)));
        /* Enable the main PLL. */
        __HAL_RCC_PLL_ENABLE();

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

        /* Wait till PLL is ready */
        while(__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) == RESET)
        {
          if((HAL_GetTick() - tickstart ) > PLL_TIMEOUT_VALUE)
          {
            return HAL_TIMEOUT;
          }
        }
      }
      else
      {
        /* Disable the main PLL. */
        __HAL_RCC_PLL_DISABLE();

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

        /* Wait till PLL is ready */
        while(__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) != RESET)
        {
          if((HAL_GetTick() - tickstart ) > PLL_TIMEOUT_VALUE)
          {
            return HAL_TIMEOUT;
          }
        }
      }
    }
    else
    {
      /* Check if there is a request to disable the PLL used as System clock source */
      if((RCC_OscInitStruct->PLL.PLLState) == RCC_PLL_OFF)
      {
        return HAL_ERROR;
      }
      else
      {
        /* Do not return HAL_ERROR if request repeats the current configuration */
        pll_config = RCC->CFGR;
        if((READ_BIT(pll_config, RCC_PLLCFGR_PLLSRC) != RCC_OscInitStruct->PLL.PLLSource) ||
           (READ_BIT(pll_config, RCC_PLLCFGR_PLLM) != RCC_OscInitStruct->PLL.PLLM) ||
           (READ_BIT(pll_config, RCC_PLLCFGR_PLLN) != RCC_OscInitStruct->PLL.PLLN) ||
           (READ_BIT(pll_config, RCC_PLLCFGR_PLLP) != RCC_OscInitStruct->PLL.PLLP) ||
           (READ_BIT(pll_config, RCC_PLLCFGR_PLLQ) != RCC_OscInitStruct->PLL.PLLQ))
        {
          return HAL_ERROR;
        }
      }
    }
  }
  return HAL_OK;
}

首先是很长一堆判断是否出错的语句,不用管;

1.3.1 __HAL_RCC_HSE_CONFIG(RCC_OscInitStruct->HSEState)分析: 

      /* Set the new HSE configuration —————————————*/
      __HAL_RCC_HSE_CONFIG(RCC_OscInitStruct->HSEState); 

而函数本身如下

#define __HAL_RCC_HSE_CONFIG(__STATE__)                         \
                    do { if ((__STATE__) == RCC_HSE_ON)  

                                      SET_BIT(RCC->CR, RCC_CR_HSEON);                                       \
                           else if ((__STATE__) == RCC_HSE_BYPASS)   \
                           {         SET_BIT(RCC->CR, RCC_CR_HSEBYP);        \
                                     SET_BIT(RCC->CR, RCC_CR_HSEON);    }                                         \                      else                                      \
                          {           CLEAR_BIT(RCC->CR, RCC_CR_HSEON);       \
                                       CLEAR_BIT(RCC->CR, RCC_CR_HSEBYP);  }                                                   } while(0U)

简单看就是判断HSE使能还是旁路。然后给RCC_CR对应位赋值;

SET_BIT(RCC->CR, RCC_CR_HSEON);  

#define SET_BIT(REG, BIT)     ((REG) |= (BIT))

之前初始化的语句是:

RCC_OscInitStruct.HSEState = RCC_HSE_ON;

对应的值如下

#define RCC_CR_HSEON_Pos                   (16U)                               
#define RCC_CR_HSEON_Msk                   (0x1UL << RCC_CR_HSEON_Pos)          
#define RCC_CR_HSEON                       RCC_CR_HSEON_Msk  

即1左移16位;

而,SET_BIT(RCC->CR, RCC_CR_HSEBYP);

#define RCC_CR_HSEBYP_Pos                  (18U)                               
#define RCC_CR_HSEBYP_Msk                  (0x1UL << RCC_CR_HSEBYP_Pos)         
#define RCC_CR_HSEBYP                      RCC_CR_HSEBYP_Msk 

即1左移18位;

暂时没用过HSE旁路,好像是不用晶振、用外部信号输入代替晶振。

1.3.2 给PLL相关寄存器赋值:

先关闭PLL时钟:

__HAL_RCC_PLL_DISABLE();

#define __HAL_RCC_PLL_DISABLE() (*(__IO uint32_t *) RCC_CR_PLLON_BB = DISABLE)

#define RCC_PLLON_BIT_NUMBER       0x18U
#define RCC_CR_PLLON_BB            (PERIPH_BB_BASE + (RCC_CR_OFFSET * 32U) + (RCC_PLLON_BIT_NUMBER * 4U))
#define PERIPH_BB_BASE        0x42000000UL
/* — CR Register — */
#define RCC_CR_OFFSET              (RCC_OFFSET + 0x00U)

#define RCC_OFFSET                 (RCC_BASE – PERIPH_BASE)

简单点看就是在RCC_CR寄存器里。

0x18转换为10进制就是24,即bit24;

 赋0就是关闭PLL;使能PLL同理

主要赋值语句

WRITE_REG(RCC->PLLCFGR, (RCC_OscInitStruct->PLL.PLLSource | \
RCC_OscInitStruct->PLL.PLLM | \
                                 (RCC_OscInitStruct->PLL.PLLN << RCC_PLLCFGR_PLLN_Pos)             | \       (((RCC_OscInitStruct->PLL.PLLP >> 1U) – 1U) << RCC_PLLCFGR_PLLP_Pos) | \
                                 (RCC_OscInitStruct->PLL.PLLQ << RCC_PLLCFGR_PLLQ_Pos)));

#define WRITE_REG(REG, VAL)   ((REG) = (VAL))

首先是RCC_PLLSOURCE_HSE之前写结构体如下:

RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;

#define RCC_PLLSOURCE_HSE                RCC_PLLCFGR_PLLSRC_HSE

#define RCC_PLLCFGR_PLLSRC_HSE_Pos         (22U)                               

#define RCC_PLLCFGR_PLLSRC_HSE_Msk         (0x1UL << RCC_PLLCFGR_PLLSRC_HSE_Pos) /*!< 0x00400000 */
#define RCC_PLLCFGR_PLLSRC_HSE             RCC_PLLCFGR_PLLSRC_HSE_Msk  

即1左移22位,在RCC_PLLCFGR寄存器中作用选择HSE;

 寄存器其他位和分频器计算如下:

#define RCC_PLLCFGR_PLLN_Pos               (6U) 

#define RCC_PLLP_DIV2                  0x00000002U
#define RCC_PLLP_DIV4                  0x00000004U
#define RCC_PLLP_DIV6                  0x00000006U
#define RCC_PLLP_DIV8                  0x00000008U

#define RCC_PLLCFGR_PLLP_Pos               (16U) 

#define RCC_PLLCFGR_PLLQ_Pos               (24U)   

PLLM不移动,PLLN左移6位,PLLQ左移24位

PLLP分频的值右移1位、即除以2再减1,2~8分频对应00~11两位,PLLP左移16位;

最后把各个数相或,数值写到寄存器。

完毕后使能PLL时钟。

这儿使用write,是因为操作的位比较多,直接相与再写入比较方便。

二、外设时钟初始化

 

时钟树如上,结构体成员赋值如下:

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_DIV2;

#define RCC_CLOCKTYPE_SYSCLK             0x00000001U
#define RCC_CLOCKTYPE_HCLK               0x00000002U
#define RCC_CLOCKTYPE_PCLK1              0x00000004U
#define RCC_CLOCKTYPE_PCLK2              0x00000008U

#define RCC_SYSCLKSOURCE_HSI             RCC_CFGR_SW_HSI
#define RCC_SYSCLKSOURCE_HSE             RCC_CFGR_SW_HSE
#define RCC_SYSCLKSOURCE_PLLCLK          RCC_CFGR_SW_PLL

#define RCC_SYSCLK_DIV1                  RCC_CFGR_HPRE_DIV1
#define RCC_SYSCLK_DIV2                  RCC_CFGR_HPRE_DIV2


#define RCC_SYSCLK_DIV512                RCC_CFGR_HPRE_DIV512

#define RCC_HCLK_DIV1                    RCC_CFGR_PPRE1_DIV1
#define RCC_HCLK_DIV2                    RCC_CFGR_PPRE1_DIV2

#define RCC_HCLK_DIV16                   RCC_CFGR_PPRE1_DIV16

分别是要使能的外设时钟(HCLK\SYSCLK\PCLK1\PCLK2);

系统时钟源选择(HSI\HSE\PLLCLK);AHB分频器,APB1分频器,APB2分频器。

将配置好的结构体成员写到对应的寄存器内;

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

调用这个函数并且判断是否出错;

函数主体如下: 

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) and the supply voltage of the device. */

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

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

    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)   ||
            (RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_PLLRCLK))
    {
      /* 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;
      }
    }
  }

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

  /*-------------------------- 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) << 3U));
  }

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

2.1等待周期的验证和写入;

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

初步看是判断等待周期的,

#define FLASH_LATENCY_2                FLASH_ACR_LATENCY_2WS  

                                                                 /*!< FLASH Two Latency cycles      */

#define FLASH_ACR_LATENCY_2WS          0x00000002U   

#define __HAL_FLASH_GET_LATENCY()     (READ_BIT((FLASH->ACR), FLASH_ACR_LATENCY))

 #define __HAL_FLASH_SET_LATENCY(__LATENCY__) (*(__IO uint8_t *)ACR_BYTE0_ADDRESS = (uint8_t)(__LATENCY__))

#define ACR_BYTE0_ADDRESS           0x40023C00U 

我们主要操作RCC_CFGR寄存器,所以在切换时钟源时要插入1、2个等待周期。

所以做法是调用函数__HAL_FLASH_GET_LATENCY()

读取FLASH_ACR寄存器LATENCY位。当等待周期小于2时要进行处理。

方法就是给LATENCY地址写值,写的值就是FLASH_LATENCY_2。

至于为什么没有用很多基地址组合,因为FLASH下的寄存器很重要,能自主读写的很少。

2.2 HCLK配置

  /*————————– 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));
    }

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

之前结构体HCLK和其他三个相或,所以这儿的if判断都要进入里面;

后面两个if语句都是判断欲开启的外设时钟有没有PCLK1、PCLK2,都有,然后赋值16分频,具体的后面详解;

这部分主要是

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

#define MODIFY_REG(REG, CLEARMASK, SETMASK)  WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK)))

这儿括号很多,一开始看错了,看成前面的寄存器和后面一堆相与,怎么也不对,其实是寄存器和中间那部分相与,再和后面那部分相或;这个宏定义作用是给这个寄存器写数值,后面一堆按位运算的作用是只写给想修改的位,其他的保持不变。

READ_REG(REG)就是读RCC_CFGR寄存器的值;

#define RCC_CFGR_HPRE_Pos                  (4U)                                
#define RCC_CFGR_HPRE_Msk                  (0xFUL << RCC_CFGR_HPRE_Pos)        

                                                                                /*!< 0x000000F0 */
#define RCC_CFGR_HPRE                      RCC_CFGR_HPRE_Msk  

RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;

#define RCC_SYSCLK_DIV1                  RCC_CFGR_HPRE_DIV1

#define RCC_CFGR_HPRE_DIV1                 0x00000000U                         
#define RCC_CFGR_HPRE_DIV2                 0x00000080U                       
#define RCC_CFGR_HPRE_DIV4                 0x00000090U                        
#define RCC_CFGR_HPRE_DIV8                 0x000000A0U                      
…                     
#define RCC_CFGR_HPRE_DIV512               0x000000F0U 

这个宏定义的逻辑就是寄存器本来的值和(0x000000f0取反)也就是0xffffff0f相与,得到的值是bit[5:8]清零,其他位不变,这个值再和分频的值相或,0的位置变为想写入的值。

2.3 SYSCLK配置、时钟源选择

if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_SYSCLK) == RCC_CLOCKTYPE_SYSCLK)
  {
    assert_param(IS_RCC_SYSCLKSOURCE(RCC_ClkInitStruct->SYSCLKSource));

    /* PLL is selected as System Clock Source */
    else if((RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_PLLCLK)   ||
            (RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_PLLRCLK))
    {
      /* Check the PLL ready flag */
      if(__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) == 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;
      }
    }
  }

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

代码很长,去掉一部分HSE、HSI作为时钟源的代码原理和PLL的一致。

2.3.1  PLL时钟就绪检测和__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY)宏定义的分析

首先是时钟就绪标志位的检测,就比如我要PLL作为主时钟的时钟源。那么在开启主时钟之前必须要保证PLL是打开的,软件通过CR寄存器下PLLRDY判断。

 通过函数     

if(__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) == RESET)
      {
        return HAL_ERROR;
      }

#define __HAL_RCC_GET_FLAG(__FLAG__) (((((((__FLAG__) >> 5U) == 1U)? RCC->CR :((((__FLAG__) >> 5U) == 2U) ? RCC->BDCR :((((__FLAG__) >> 5U) == 3U)? RCC->CSR :RCC->CIR))) & (1U << ((__FLAG__) & RCC_FLAG_MASK)))!= 0U)? 1U : 0U)
这个宏定义很长,下面是__FLAG__可以选的值

/* Flags in the CR register */
#define RCC_FLAG_HSIRDY                  ((uint8_t)0x21)
#define RCC_FLAG_HSERDY                  ((uint8_t)0x31)
#define RCC_FLAG_PLLRDY                  ((uint8_t)0x39)
#define RCC_FLAG_PLLI2SRDY               ((uint8_t)0x3B)

/* Flags in the BDCR register */
#define RCC_FLAG_LSERDY                  ((uint8_t)0x41)

/* Flags in the CSR register */
#define RCC_FLAG_LSIRDY                  ((uint8_t)0x61)
#define RCC_FLAG_BORRST                  ((uint8_t)0x79)
#define RCC_FLAG_PINRST                  ((uint8_t)0x7A)
#define RCC_FLAG_PORRST                  ((uint8_t)0x7B)
#define RCC_FLAG_SFTRST                  ((uint8_t)0x7C)
#define RCC_FLAG_IWDGRST                 ((uint8_t)0x7D)
#define RCC_FLAG_WWDGRST                 ((uint8_t)0x7E)
#define RCC_FLAG_LPWRRST                 ((uint8_t)0x7F)

#define RCC_FLAG_MASK  ((uint8_t)0x1FU)

将宏定义拆开来看:

if((((((__FLAG__) >> 5U) == 1U)? RCC->CR :((((__FLAG__) >> 5U) == 2U) ? RCC->BDCR :((((__FLAG__) >> 5U) == 3U)? RCC->CSR :RCC->CIR))) & (1U << ((__FLAG__) & RCC_FLAG_MASK)))!= 0U)   return 1;

else       return 0;

把((__FLAG__) >> 5U) 看成 b;就是

if(b == 1)

{

        if(RCC_CR & (1U << ((__FLAG__) & RCC_FLAG_MASK)) != 0u)

                return 1;

        else

                return 0;

}

else if (b == 2)

{

        

        if(RCC_BDCR & (1U << ((__FLAG__) & RCC_FLAG_MASK)) != 0u)

                return 1;

        else

                return 0;

}

#define RCC_FLAG_PLLRDY                  ((uint8_t)0x39)

对于PLL标准位,0x39右移5位等于1,0x39和0x1f相与,也就是取后5位,得到0x19;1左移0x19位是1左移25位,也就是PLLRDY位,其他位都是0。再和RCC_CR相与,就是取第25位是0,还是1.

当是1时,也就是PLL开启时得到的结果不等于0;当未硬件置一时,0和1相与也是0,得到的结果就是0;

2.3.2 系统时钟源选择

__HAL_RCC_SYSCLK_CONFIG(RCC_ClkInitStruct->SYSCLKSource);

#define __HAL_RCC_SYSCLK_CONFIG(__RCC_SYSCLKSOURCE__) MODIFY_REG(RCC->CFGR, RCC_CFGR_SW, (__RCC_SYSCLKSOURCE__))
 

#define RCC_CFGR_SW_Pos                    (0U)                                
#define RCC_CFGR_SW_Msk                    (0x3UL << RCC_CFGR_SW_Pos)          

#define RCC_CFGR_SW                        RCC_CFGR_SW_Msk          /*!< 0x00000003 */

#define RCC_SYSCLKSOURCE_PLLCLK          RCC_CFGR_SW_PLL

#define RCC_CFGR_SW_HSI                    0x00000000U                         
#define RCC_CFGR_SW_HSE                    0x00000001U                         
#define RCC_CFGR_SW_PLL                    0x00000002U                         
函数逻辑和之前一样,给最后两位赋值,其他位保持不变。

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

这部分是减少等待周期;大于2时,写入到FLASH_ACR_LATENCY,和之前一样。

2.4PCLK1和PCLK2配置

 /*————————– 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) << 3U));
  }

RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;

#define RCC_CFGR_PPRE1_Pos                 (10U)                               
#define RCC_CFGR_PPRE1_Msk                 (0x7UL << RCC_CFGR_PPRE1_Pos)      

                                                                                         /*!< 0x00001C00 */
#define RCC_CFGR_PPRE1                     RCC_CFGR_PPRE1_Msk 

#define RCC_CFGR_PPRE2_Pos                 (13U)                               
#define RCC_CFGR_PPRE2_Msk                 (0x7UL << RCC_CFGR_PPRE2_Pos)      

                                                                                         /*!< 0x0000E000 */
#define RCC_CFGR_PPRE2                     RCC_CFGR_PPRE2_Msk

#define RCC_HCLK_DIV1                    RCC_CFGR_PPRE1_DIV1
#define RCC_HCLK_DIV2                    RCC_CFGR_PPRE1_DIV2

#define RCC_HCLK_DIV16                   RCC_CFGR_PPRE1_DIV16

#define RCC_CFGR_PPRE1_DIV1                0x00000000U                        
#define RCC_CFGR_PPRE1_DIV2                0x00001000U                        
…                       
#define RCC_CFGR_PPRE1_DIV16               0x00001C00U 

分别是给RCC_CFGR,bit[10:12]和bit[13:15]写入数据,数据内容是分频值。

对于PCLK2分频值用到PPRE1的值,所以要左移三位。

在这儿可以直接用PPER2的分频值,但不知道为什么没有用;

#define RCC_CFGR_PPRE2_DIV1                0x00000000U                       
#define RCC_CFGR_PPRE2_DIV2                0x00008000U                       
…                     
#define RCC_CFGR_PPRE2_DIV16               0x0000E000U 

2.5 更新hal库参数

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

翻译下来的意思是:更新SystemCoreClock全局变量;

                                鉴于新的系统时钟设置来配置时基。

这儿涉及到hal库,目前还不太懂,希望有大佬补充。

三、总结

时钟的配置最核心的还是看懂时钟树,知道时钟源怎么选,时钟这个路怎么走的,到哪个外设。至于配置完全可以利用软件的便利,节省大量的工作,对于函数的分析其实是笔者较真了。但是一点点梳理下来,还是学习到很多函数的写法,宏定义的运用。同时,以后可能面对的时钟配置远超过CubeMX软件使用的复杂度,掌握配置时钟整体的思路至关重要。

参考资料:stm32f4xx中文参考手册

物联沃分享整理
物联沃-IOTWORD物联网 » 深入学习STM32 HAL库中RCC初始化函数SystemClock_Config()的梳理与分析(一)

发表评论