你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】【STM32F0系列学习】之—系统时钟(RCC)

[复制链接]
STMCU小助手 发布时间:2021-11-21 21:00
1、时钟树框图系统时钟的选择在启动时执行,即程序在上电复位时,先运行启动文件startup_stm32f0xx.s,该汇编文件中先进行了系统初始化,在系统初始化的时候进行系统时钟(SYSCLK)的选择与配置,当系统初始化完成后,才运行main函数。在复位后,内部8MHz的RC振荡器(HSI)被选为默认的CPU时钟。用户也可以选择4-32MHz的外部时钟(HSE)作为系统时钟,选择HSE作为系统时钟时,标准库函数需要用户自己进行修改,HAL库的话用户可以直接配置。当 HSE 故障的时候, 且选择 PLL 时钟作为SYSCLK, 那么当 HSE 故障的时候,不仅HSE 不能使用,连 PLL 也会被关闭,这个时候系统会自动切换 HSI 作为系统时钟,此时SYSCLK=HSI=8M,如果没有开启 CSS 和 CSS 中断的话,那么整个系统就只能在低速率运行,这是系统跟瘫痪没什么两样。如果开启了 CSS 功能的话,那么可以当 HSE 故障时,在CSS 中断里面采取补救措施,使用 HSI,并把系统时钟设置为更高的频率。允许应用程序通过几个分频器来配置 AHB 和 APB 的频率,AHB 和 APB 的最高频率为48MHz。
20200616090736181.png
上电复位时,启动文件运行如下程序:
  1. ; Reset handler routine 复位处理程序
  2. Reset_Handler    PROC
  3.                  EXPORT  Reset_Handler                 [WEAK]
  4.         IMPORT  __main
  5.         IMPORT  SystemInit  
  6.                  LDR     R0, =SystemInit
  7.                  BLX     R0
  8.                  LDR     R0, =__main
  9.                  BX      R0
  10.                  ENDP
复制代码
上电启动过程中,先调用了SystemInit()函数,再进入main()函数。SystemInit()函数在文件system_stm32f0xx.c中,它的作用是在单片机上电复位的过程中,会来调用这个函数设置系统时钟SYSCLK,默认STM32F0系列单片机时钟最大设置为48MHz,所以系统时钟默认是已经配置好的,无需用户自己进行配置,除非有特殊需要,用户可以进行修改。2、时钟系统特性系统时钟(SYSCLK)有以下三种选择:HSE(外部高速晶振):晶振范围(4MHz~32MHz)可被外部时钟源旁路,意思就是不经过分频和倍频直接将外部时钟源选为系统时钟 SYSCLK,外部时钟信号范围1MHz~32MHzHSI(内部高速RC振荡器):8MHz,出厂已校准到±1%的精度PLL经过2,3,… 16倍频(所有输出的最小频率16MHz)其他时钟源:LSI(内部低速RC振荡器):~40KHzLSE(外部低速晶振):32.768kHz可悲外部时钟源旁路,可以直接给RTCCLK提供时钟,最高频率1MHz外部晶振的驱动能力可配置(功耗VS起振能力)HSI14(内部高速14MHz RC振荡器):专用于给ADC模块提供时钟3、时钟参数一览
2020061610085323.png
4、时钟配置案例使用标准库进行系统时钟的修改,为什么说是修改,而不是说配置,如上所述,因为程序来到main函数之前,启动文件:startup_stm32f0xx.s 已经调用SystemInit()函数把系统时钟初始化成 48MHz了,SystemInit()在 system_stm32f0xx.c 中定义,如果用户想修改系统时钟,可参照SystemInit()函数中调用的SetSysClock() 函数,自行编写修改程序,官方文件一般不进行直接修改。以下代码为system_stm32f0xx.c原文件中的 SystemInit() 函数和 SetSysClock() 函数,源代码分析:
  1. void SystemInit (void)
  2. {   
  3.   /* Set HSION bit */
  4.   /* HSI时钟使能,开启HSI振荡器 */
  5.   RCC->CR |= (uint32_t)0x00000001;
  6. #if defined(STM32F051)  
  7.   /* Reset SW[1:0], HPRE[3:0], PPRE[2:0], ADCPRE and MCOSEL[2:0] bits */
  8.   /* 复位 系统时钟切换, AHB总线的分频因子, APB总线的分频因子, ADC的时钟频率 和微控制器时钟输出选择 */
  9.   RCC->CFGR &= (uint32_t)0xF8FFB80C;
  10. #else
  11.   /* Reset SW[1:0], HPRE[3:0], PPRE[2:0], ADCPRE, MCOSEL[2:0], MCOPRE[2:0] and PLLNODIV bits */
  12.   /* 复位 系统时钟切换, AHB总线的分频因子, APB总线的分频因子, ADC的时钟频率 ,微控制器时钟输出选择,微控制器预分频选择,
  13.   PLL左时钟输出时是否要分频 */
  14.   RCC->CFGR &= (uint32_t)0x08FFB80C;
  15. #endif /* STM32F051 */
  16.   /* Reset HSEON, CSSON and PLLON bits */
  17.   /* HSE时钟使能位, 时钟安全系统使能位 和PLL使能位清0 */
  18.   RCC->CR &= (uint32_t)0xFEF6FFFF;
  19.   /* Reset HSEBYP bit */
  20.   /* 外部高速时钟旁路清0 */
  21.   RCC->CR &= (uint32_t)0xFFFBFFFF;
  22.   /* Reset PLLSRC, PLLXTPRE and PLLMUL[3:0] bits */
  23.   /* PLL输入时钟源位,分频HSE后作为PLL输入,PLL倍频系数清0 */
  24.   RCC->CFGR &= (uint32_t)0xFFC0FFFF;
  25.   /* Reset PREDIV1[3:0] bits */
  26.   /* PREDIV1分频因子清0 */
  27.   RCC->CFGR2 &= (uint32_t)0xFFFFFFF0;
  28.   /* Reset USARTSW[1:0], I2CSW, CECSW and ADCSW bits */
  29.   /* USART1时钟源选择位,I2C时钟源选择位,HDMI CEC时钟源选择位,ADC时钟源选择位清0 */
  30.   RCC->CFGR3 &= (uint32_t)0xFFFFFEAC;
  31.   /* Reset HSI14 bit */
  32.   /* HSI14时钟使能位清0 */
  33.   RCC->CR2 &= (uint32_t)0xFFFFFFFE;
  34.   /* 禁止所有时钟中断 */
  35.   RCC->CIR = 0x00000000;
  36.   /* Configure the System clock frequency, AHB/APBx prescalers and Flash settings */
  37.   /* 配置系统时钟频率,AHB/APBx分频器和闪存设置 */
  38.   SetSysClock();
  39. }
  40. ===========================================================================================================================
  41. static void SetSysClock(void)
  42. {
  43.   __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  44.   /* SYSCLK, HCLK, PCLK configuration 系统时钟,AHB总线时钟,APB总线时钟配置-----------*/
  45.   /* Enable HSE 使能HSE */   
  46.   RCC->CR |= ((uint32_t)RCC_CR_HSEON);
  47.   /* Wait till HSE is ready and if Time out is reached exit 等待HSE稳定,如果超时,则退出 */
  48.   do
  49.   {
  50.     HSEStatus = RCC->CR & RCC_CR_HSERDY;
  51.     StartUpCounter++;  
  52.   } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
  53.   /* 到这边要么是超时了,要么就是HSE稳定了,准备好了,再次判断HSE是否准备好 */
  54.   if ((RCC->CR & RCC_CR_HSERDY) != RESET)
  55.   {
  56.     HSEStatus = (uint32_t)0x01;                // HSE状态标识置1(稳定了)
  57.   }
  58.   else
  59.   {
  60.     HSEStatus = (uint32_t)0x00;                // HSE状态标识清0(没稳定)
  61.   }  
  62.   if (HSEStatus == (uint32_t)0x01)        //如果HSE稳定了
  63.   {
  64.     /* Enable Prefetch Buffer and set Flash Latency 启用 Flash 预取缓冲区 和 设置Flash延迟*/
  65.     FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;
  66.     /* HCLK = SYSCLK         AHB 预分频因子设置为 1 分频, HCLK = SYSCLK */
  67.     RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
  68.     /* PCLK = HCLK                 APB 预分频因子设置为 1 分频, PCLK = HCLK */
  69.     RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE_DIV1;
  70.     /* PLL configuration = HSE * 6 = 48 MHz  设置 PLL 时钟来源为 HSE(外接了8MHz),设置PLL不分频,设置 PLL 6倍频 */
  71.     RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));
  72.     RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_PREDIV1 | RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLMULL6);
  73.     /* Enable PLL PLL使能 */
  74.     RCC->CR |= RCC_CR_PLLON;
  75.     /* Wait till PLL is ready 等待PLL稳定 */
  76.     while((RCC->CR & RCC_CR_PLLRDY) == 0)
  77.     {
  78.     }
  79.     /* Select PLL as system clock source  选择PLL作为系统时钟源*/
  80.     RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
  81.     RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;   
  82.     /* Wait till PLL is used as system clock source  等待PLL作为系统时钟源稳定 */
  83.     while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)RCC_CFGR_SWS_PLL)
  84.     {
  85.     }
  86.   }
  87.   else
  88.   { /* If HSE fails to start-up, the application will have wrong clock         如果HSE启动失败,应用将使用错误的配置
  89.          configuration. User can add here some code to deal with this error 用户可以在这里添加处理错误的代码 */
  90.   }  
  91. }
复制代码
以上分析了标准库函数中配置系统时钟的程序逻辑,下面介绍当用户需要修改自己的时钟频率时的处理方法,4.1【标准库】的配置方式步骤:1、只要参照 SetSysClock() 函数重新编写一个自己的配置系统时钟的函数2、在main函数中进行调用,调用语句需要放在main函数的函数体第一行,以确保在他初始化前已经重新设置好了自己需要的系统时钟
  1. /*========================================= 自定义 HSE 系统时钟 =========================================*/
  2. /************************************************
  3. 函数名称 : HSE_SetSysClock
  4. 功    能 : 外部高速时钟配置
  5. 参    数 : RCC_PLLMul_x:用来设置PLL的倍频因子,在调用的时候形参可以是: RCC_PLLMul_x , x:[2,3,...16],这些宏来源于
  6.                         库函数的定义,具体功能是配置了时钟配置寄存器 CFGR 的位 21-18 PLLMUL[3:0],预先定义好倍频因子,方便调用
  7. 返 回 值 : 无
  8. 作    者 : JayYang
  9. *************************************************/
  10. void HSE_SetSysClock(uint32_t RCC_PLLMul_x){
  11.         __IO uint32_t StartUpCounter = 0, HSE_StartUpStatus = 0;        //定义启动计数参数,以及外部高速时钟启动状态参数
  12.     //把RCC外设初始化成复位状态,这句是必须的
  13.     RCC_DeInit();
  14.     //使能HSE,开启外部晶振,本人系统中用的是 8MHz
  15.     RCC_HSEConfig(RCC_HSE_ON);
  16.     //等待 HSE 启动稳定
  17.     HSE_StartUpStatus = RCC_WaitForHSEStartUp();
  18.     //如果 HSE 成功启动并且稳定下来
  19.     if(HSE_StartUpStatus==SUCCESS){
  20.     //-----------------------------------------------------------------//
  21.         //使能 FLASH 预存取缓冲区
  22.         FLASH_PrefetchBufferCmd(ENABLE);
  23.         // SYSCLK 周期与闪存访问时间的比例设置,这里统一设置成 1
  24.         // 设置成 1 的时候, SYSCLK 低于 24M 也可以工作,如果设置成 0 的时候,
  25.         // 如果配置的 SYSCLK 超出了范围的话,则会进入硬件错误,程序就死了
  26.         // 0: 0 < SYSCLK <= 24M
  27.         // 1: 24< SYSCLK <= 48M
  28.         FLASH_SetLatency(FLASH_Latency_1);
  29.     //-----------------------------------------------------------------//
  30.         //AHB 预分频因子设置为1分频,HCLK = SYSCLK
  31.         RCC_HCLKConfig(RCC_SYSCLK_Div1);
  32.         //APB 预分频因子设置为1分频,PCLK = HCLK
  33.         RCC_PCLKConfig(RCC_HCLK_Div1);
  34.     //-----------------------------------------------------------------//
  35.     //-----------------设置各种频率主要就是在这里设置-------------------//
  36.         // 设置 PLL 时钟来源为HSE,设置PLL倍频因子
  37.         // PLLCLK = 8MHz * RCC_PLLMul_x
  38.         RCC_PLLConfig(RCC_PLLSource_PREDIV1,RCC_PLLMul_x);
  39.         //开启 PLL
  40.         RCC_PLLCmd(ENABLE);
  41.         //等待 PLL 稳定
  42.         while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
  43.         //当PLL稳定之后,把PLL时钟切换为系统时钟SYSCLK
  44.         RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
  45.     }
  46.     else{
  47.         //当HSE开启失败或者故障的时候,单片机会自动把HSI设置为系统时钟,HSI 是内部的高速时钟,8MHz
  48.         //程序就会来到这里,用户可在这里添加出错的代码处理
  49.         while(1){
  50.         }
  51.     }
  52. }
  53. /*=========================== main.c主函数调用 ===========================*/
  54. int main(void){
  55.         HSE_SetSysClock(RCC_PLLMul_9);                 //设置系统时钟为: 8MHZ * 9 =72MHZ。
  56. }
  57. /*========================================= 自定义 HSI 系统时钟 =========================================*/
  58. /************************************************
  59. 函数名称 : HSI_SetSysClk
  60. 功    能 : 外部高速时钟配置
  61. 参    数 : RCC_PLLMul_x:用来设置PLL的倍频因子,在调用的时候形参可以是: RCC_PLLMul_x , x:[2,3,...16],这些宏来源于
  62.                         库函数的定义,具体功能是配置了时钟配置寄存器 CFGR 的位 21-18 PLLMUL[3:0],预先定义好倍频因子,方便调用
  63. 返 回 值 : 无
  64. 作    者 : JayYang
  65. *************************************************/
  66. void HSI_SetSysClk( uint32_t RCC_PLLMul_x)
  67. {
  68.         __IO uint32_t HSI_StartUpStatus = 0;                //定义内部高速时钟启动状态
  69.         
  70.         // 把 RCC 外设初始化成复位状态,这句是必须的
  71.         RCC_DeInit();        
  72.         // 使能 HSI
  73.         RCC_HSICmd(ENABLE);
  74.         
  75.         // 等待 HSI 就绪
  76.         HSI_StartUpStatus = RCC->CR & RCC_CR_HSIRDY;
  77.         
  78.         // 只有 HSI 就绪之后则继续往下执行
  79.         if( HSI_StartUpStatus== RCC_CR_HSIRDY )
  80.         {
  81.         //-----------------------------------------------------------------//
  82.         //使能 FLASH 预存取缓冲区
  83.                 FLASH_PrefetchBufferCmd(ENABLE);
  84.         // SYSCLK 周期与闪存访问时间的比例设置,这里统一设置成 1
  85.         // 设置成 1 的时候, SYSCLK 低于 24M 也可以工作,如果设置成 0 的时候,
  86.         // 如果配置的 SYSCLK 超出了范围的话,则会进入硬件错误,程序就死了
  87.         // 0: 0 < SYSCLK <= 24M
  88.         // 1: 24< SYSCLK <= 48M
  89.                 FLASH_SetLatency(FLASH_Latency_1);
  90.                
  91.         //AHB 预分频因子设置为1分频,HCLK = SYSCLK
  92.                 RCC_HCLKConfig(RCC_SYSCLK_Div1);
  93.         //APB 预分频因子设置为1分频,PCLK = HCLK
  94.                 RCC_PCLKConfig(RCC_HCLK_Div1);
  95.     //-----------------------------------------------------------------//
  96.     //-----------------设置各种频率主要就是在这里设置-------------------//
  97.                 // 配置 PLLCLK = HSI * RCC_PLLMul_x
  98.         RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_x);
  99.                
  100.         // 使能PLL
  101.                 RCC_PLLCmd(ENABLE);
  102.                
  103.                 // 等待PLL稳定
  104.                 while( RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET );
  105.                
  106.         //当PLL稳定之后,把PLL时钟切换为系统时钟SYSCLK
  107.                 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
  108.                
  109.         //读取时钟切换状态位,确保 PLLCLK 被选为系统时钟
  110.         while( RCC_GetSYSCLKSource() != 0x08 );
  111.         }
  112.         else
  113.     {
  114.                 //当HSI开启失败或者故障的时候,程序就会来到这里,用户可在这里添加出错的代码处理
  115.         while(1){
  116.         }
  117.         }
  118. }
  119. /*=========================== main.c主函数调用 ===========================*/
  120. int main(void){
  121.         HSI_SetSysClock(RCC_PLLMul_12);         //设置系统时钟为: 4MHZ * 12 =48MHZ。
  122. }
复制代码
4.2【HAL库】的配置方式步骤:1、配置HSE系统时钟:在 STM32CubeMX 软件中选择 【System Core】→【RCC】→【选择HSE高速时钟】→【点击下拉菜单选择“晶体/陶瓷谐振器”,即电路中外接晶振的】
20200616161516116.png
2020061616172011.png
20200616161903673.png
20200616170009152.png
STM32CubeMX软件可以直接生成MDK项目代码,双击打开项目工程,先进行项目编译,然后打开 main.c 查看源代码,找到 **SystemClock_Config();**函数,这个就是STM32CubeMX生成的系统时钟配置函数,代码已全部自动生成,无需用户再次修改任何内容,生成的系统时钟,就是上文配置的外部8MHz的晶振,经过6倍频后,最终生成了48MHz的系统时钟频率。
20200616163602359.png
进入函数体内查看源代码
2020061621485766.png
2、配置HSI系统时钟,按下图设置CubeMX软件,并生成代码
20200616165217582.png
同样打开项目中的main文件
进入函数体内查看源代码
20200616215530797.png
至此,关于STM32F0系列的系统时钟的配置及修改方法如上所述,本人刚开始写博客,有不好或者错误的地方希望各路大神指教。此博文的主要目的是,作为自己的一个学习笔记,在自己学习的过程中做好相应的记录,方便以后回顾和巩固,也方便有需要的朋友。因为我发现只看一些教学视频的讲解,和动手练习操作一遍是很难学会的,还是要通过做笔记的方式来对知识进行巩固和累积才能帮助我真正的消化和学懂相关知识,谢谢!
20200616165527848.png
收藏 评论0 发布时间:2021-11-21 21:00

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版