当前位置: 代码网 > it编程>编程语言>C/C++ > stm32 hal库 RCC初始化函数SystemClock_Config()梳理分析、初步细致学习(一)

stm32 hal库 RCC初始化函数SystemClock_Config()梳理分析、初步细致学习(一)

2024年08月01日 C/C++ 我要评论
首先是时钟就绪标志位的检测,就比如我要PLL作为主时钟的时钟源。那么在开启主时钟之前必须要保证PLL是打开的,软件通过CR寄存器下PLLRDY判断。通过函数if(== RESET)#define1U 5U) 看成 b;就是if(b == 1)= 0u)return 1;

目录

一、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 时钟使能

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

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

代码很长,只留下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)分析: 

1.3.2 给pll相关寄存器赋值:

主要赋值语句

二、外设时钟初始化

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

函数主体如下: 

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等待周期的验证和写入;

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库参数

三、总结

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

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

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com