一、时钟系统

  1. 概述

    时钟是单片机运行的基础,时钟信号推动单片机内各个部分执行相应的指令,时钟系统就是CPU的脉搏,决定cpu速率。 STM32有多个时钟来源的选择,为什么 STM32 要有多个时钟源呢?因为首先 STM32 本身非常复杂,外设非常的多,而使用任何外设都需要时钟才能启动,但是并不是所有外设都需要系统时钟这么高的频率,比如看门狗以及 RTC 只需要几十 k 的时钟即可。同一个电路,时钟越快功耗越大,同时抗电磁干扰能力也会越弱,所以对于较为复杂的 MCU 一般都是采取多时钟源的方法来解决这些问题。如图所示:

  2. STM32F10x时钟系统图



    从图中蓝色部分可以看出STM32有5个时钟源:HSI、HSE、LSE、LSI、PLL。

  • HSI时钟:高速内部时钟,RC振荡器,频率约为8MHz,精度不高。直接作为 8MHz 的系统时钟或者用作 4MHz 的PLL时钟输入。
  • HSE时钟:高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为3MHz~25MHz。(一般是8MHZ),外部振荡器可为系统提供非常精确的主时钟。
  • LSI时钟:低速内部时钟,RC振荡器,频率为32kHz,提供低功耗时钟。主要供独立看门狗和自动唤醒单元使用。
  • LSE时钟:低速外部时钟,接频率为32.768kHz的石英晶体,它为实时时钟或者其他定时功能提供一个低功耗且精确的时钟源。
  • PLL时钟:产生倍频的输出
  1. 系统时钟(SYSCLK)选择

    从时钟系统图中可以看出,系统复位后,HSI振荡器被选为系统时钟。此时系统时钟的来源有三种选择,可以选择HSI、PLL、HSE/2,而PLL又有两种时钟源可以选择HSE、HSI/2。

    注意:切换时钟源时需要等待新的时钟源就绪,否则系统时钟源不会切换。在时空控制寄存器(RCC_CR)里的状态指示可以看到已经准备好的时钟已经被当前系统使用的时钟。

  2. 时钟安全系统(CSS)

    从时钟系统图中可以看出,当HSE振荡器被直接或间接地作为系统时钟时(直接指的是系统时钟源为HSE/2,间接指的是HSE通过PLL产生的倍频时钟作为系统时钟源),时钟故障将导致系统时钟自动切换到HSI振荡器,同时外部HSE振荡器被关闭。在时钟失效时,如果HSE振荡器时钟是作为PLL的输入时钟,PLL也将被关闭。

    注意:一旦CSS被激活,并且HSE时钟出现故障,CSS中断就产生,并且NMI也自动产生。NMI将被不断执行,直到CSS中断挂起位被清除。因此,在NMI的处理程序中必须通过设置时钟中断寄存器(RCC_CIR)里的CSSC位来清除CSS中断

# 如果需要72MHz的系统时钟,我们可以选择8MHz的外部时钟(HSE),PLL的倍频设置为9
SYSCLK = 8(HSE) * 9(PLL) = 72MHz
  1. RTC时钟(RTCCLK)

    通过设置 备份域控制寄存器(RCC_BDCR)里的RTCSEL[1:0]位,RTCCLK时钟源可以由HSE/128、LSE或LSI时钟提供。

  2. 看门狗时钟

    如果独立看门狗已经由硬件选项或软件启动,LSI振荡器将被强制在打开状态,并且不能被关

    闭。在LSI振荡器稳定后,时钟供应给IWDG。

  3. 时钟输出

    微控制器允许输出时钟信号到外部MCO引脚。通过MCO引脚输出时钟时,GPIO端口寄存器必须被配置为相应功能,时钟信号有四种来源SYSCLK、HSI、HSE、PLL/2。

    注意:在MCO上输出的时钟必须小于50MHz(这是I/O端口的最大速度),时钟的选择由时钟配置寄存器(RCC_CFGR)中的MCO[3:0]位控制。

二、寄存器

  1. 时钟控制寄存器(RCC_CR)

  2. 时钟配置寄存器(RCC_CFGR)

  3. 时钟中断寄存器(RCC_CIR)

  4. APB2 外设复位寄存器 (RCC_APB2RSTR)

  5. APB1 外设复位寄存器 (RCC_APB1RSTR)

  6. AHB外设时钟使能寄存器 (RCC_AHBENR)

  7. APB2 外设时钟使能寄存器(RCC_APB2ENR)

  8. APB1 外设时钟使能寄存器(RCC_APB1ENR)

  9. 备份域控制寄存器 (RCC_BDCR)

  10. 控制/状态寄存器 (RCC_CSR)

  11. RCC寄存器地址映像

三、程序分析

从上面可以看出配置系统时钟有10个寄存器,分别的作用就不用过多介绍了,文档已经很详细。接下来看STM32提供的库函数是怎么配置的。

  1. RCC相关寄存器的结构体

typedef struct
{
__IO uint32_t CR;
__IO uint32_t CFGR;
__IO uint32_t CIR;
__IO uint32_t APB2RSTR;
__IO uint32_t APB1RSTR;
__IO uint32_t AHBENR;
__IO uint32_t APB2ENR;
__IO uint32_t APB1ENR;
__IO uint32_t BDCR;
__IO uint32_t CSR; #ifdef STM32F10X_CL
__IO uint32_t AHBRSTR;
__IO uint32_t CFGR2;
#endif /* STM32F10X_CL */ #if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL)
uint32_t RESERVED0;
__IO uint32_t CFGR2;
#endif /* STM32F10X_LD_VL || STM32F10X_MD_VL || STM32F10X_HD_VL */
} RCC_TypeDef;
  1. 系统时钟的初始化函数

    现在应知道,系统时钟是CPU的心脏,所以在运行程序之前,首先需要进行时钟的配置,也就是说在执行main函数之前已经执行了系统时钟的配置。系统时钟的初始函数SystemInit(),当然这个函数名是可以更改的,所以需要了解STM32运行时,是在哪里调用SystemInit()这个函数进行初始化的。

  2. SystemInit()函数

void SystemInit (void)
{
/* 将RCC时钟配置重置为默认重置状态(用于调试目的) */
RCC->CR |= (uint32_t)0x00000001; // 打开HSION位(内部高速时钟使能) /* 复位SW、HPRE、PPRE1、PPRE2、ADCPRE和MCO位 */
RCC->CFGR &= (uint32_t)0xF8FF0000; /* 复位 HSEON, CSSON 和 PLLON 位 */
RCC->CR &= (uint32_t)0xFEF6FFFF; /* 复位 HSEBYP 位 */
RCC->CR &= (uint32_t)0xFFFBFFFF; /* 复位 PLLSRC, PLLXTPRE, PLLMUL 和 USBPRE/OTGFSPRE 位 */
RCC->CFGR &= (uint32_t)0xFF80FFFF; /* 禁用所有中断并清除挂起位 */
RCC->CIR = 0x009F0000; #if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)
#ifdef DATA_IN_ExtSRAM
SystemInit_ExtMemCtl(); // 设置的是STM32F10X_HD,所以不执行此函数
#endif /* DATA_IN_ExtSRAM */
#endif /* 配置系统时钟频率、HCLK、PCLK2和PCLK1预分频器 */
/* 配置闪存延迟周期并启用预取缓冲区 */
SetSysClock(); #ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* 内部SRAM中的向量表重定位. */
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* 内部FLASH中的向量表重定位. */
#endif
}
  1. SetSysClock()函数

    配置系统时钟频率、HCLK、PCLK2和PCLK1预分频器。这里配置的系统时钟频率是72MHz

static void SetSysClock(void)
{
#ifdef SYSCLK_FREQ_HSE
SetSysClockToHSE();
#elif defined SYSCLK_FREQ_24MHz
SetSysClockTo24();
#elif defined SYSCLK_FREQ_36MHz
SetSysClockTo36();
#elif defined SYSCLK_FREQ_48MHz
SetSysClockTo48();
#elif defined SYSCLK_FREQ_56MHz
SetSysClockTo56();
#elif defined SYSCLK_FREQ_72MHz
SetSysClockTo72();
#endif /* If none of the define above is enabled, the HSI is used as System clock
source (default after reset) */
}
  1. SetSysClockTo72()函数

    将系统时钟频率设置为72MHz并配置HCLK、PCLK2和PCLK1预分频器。
/**
* @brief Sets System clock frequency to 72MHz and configure HCLK, PCLK2
* and PCLK1 prescalers.
* @note This function should be used only after reset.
* @param None
* @retval None
*/
static void SetSysClockTo72(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0; /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/
/* Enable HSE */
RCC->CR |= ((uint32_t)RCC_CR_HSEON); /* Wait till HSE is ready and if Time out is reached exit */
do
{
HSEStatus = RCC->CR & RCC_CR_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); if ((RCC->CR & RCC_CR_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
} if (HSEStatus == (uint32_t)0x01)
{
/* Enable Prefetch Buffer */
FLASH->ACR |= FLASH_ACR_PRFTBE; /* Flash 2 wait state */
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2; /* HCLK = SYSCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; /* PCLK2 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; /* PCLK1 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; #ifdef STM32F10X_CL
/* Configure PLLs ------------------------------------------------------*/
/* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */
/* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */ RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL |
RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC);
RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 |
RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5); /* Enable PLL2 */
RCC->CR |= RCC_CR_PLL2ON;
/* Wait till PLL2 is ready */
while((RCC->CR & RCC_CR_PLL2RDY) == 0)
{
} /* PLL configuration: PLLCLK = PREDIV1 * 9 = 72 MHz */
RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL);
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 |
RCC_CFGR_PLLMULL9);
#else
/* PLL configuration: PLLCLK = HSE * 9 = 72 MHz */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
RCC_CFGR_PLLMULL));
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
#endif /* STM32F10X_CL */ /* Enable PLL */
RCC->CR |= RCC_CR_PLLON; /* Wait till PLL is ready */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
} /* Select PLL as system clock source */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; /* Wait till PLL is used as system clock source */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
{
}
}
else
{ /* If HSE fails to start-up, the application will have wrong clock
configuration. User can add here some code to deal with this error */
}
}

从这里看来系统时钟的配置就比较简单了,只需要花一些时间对一下相应的寄存器,希望感兴趣的小伙伴自己按照上面的寄存器表格与程序进行对比一下,很容易就能看懂了。当然也可以跟着上面的思路自己写一个SystemInit()函数,如果不考虑各种芯片的适配,那么程序相对就比较简单,程序量也比较少。

参考文献

STM32时钟系统以及配置及源码分析:https://blog.csdn.net/qq_36243942/article/details/83655339

【STM32】系统时钟RCC详解(超详细,超全面):https://blog.csdn.net/as480133937/article/details/98845509

《STM32中文参考手册》

STM32时钟系统的配置寄存器和源码分析的更多相关文章

  1. STM32入门系列-STM32时钟系统,STM32时钟树

    时钟对于单片机来说是非常重要的,它为单片机工作提供一个稳定的机器周期从而使系统能够正常运行.时钟系统犹如人的心脏,一旦有问题整个系统就崩溃.我们知道STM32属于高级单片机,其内部有很多的外设,但不是 ...

  2. Android Debuggerd 简要介绍和源码分析(转载)

    转载: http://dylangao.com/2014/05/16/android-debuggerd-%E7%AE%80%E8%A6%81%E4%BB%8B%E7%BB%8D%E5%92%8C%E ...

  3. Quartz学习--二 Hello Quartz! 和源码分析

    Quartz学习--二  Hello Quartz! 和源码分析 三.  Hello Quartz! 我会跟着 第一章 6.2 的图来 进行同步代码编写 简单入门示例: 创建一个新的java普通工程 ...

  4. Kubernetes Job Controller 原理和源码分析(一)

    概述什么是 JobJob 入门示例Job 的 specPod Template并发问题其他属性 概述 Job 是主要的 Kubernetes 原生 Workload 资源之一,是在 Kubernete ...

  5. Kubernetes Job Controller 原理和源码分析(二)

    概述程序入口Job controller 的创建Controller 对象NewController()podControlEventHandlerJob AddFunc DeleteFuncJob ...

  6. Kubernetes Job Controller 原理和源码分析(三)

    概述Job controller 的启动processNextWorkItem()核心调谐逻辑入口 - syncJob()Pod 数量管理 - manageJob()小结 概述 源码版本:kubern ...

  7. Apollo配置中心源码分析

    Apollo配置中心源码分析 1. apollo的核心代码分享 SpringApplication启动的关键步骤 在SpringApplication中,会加载所有实现了Init方法的类 protec ...

  8. Unity时钟定时器插件——Vision Timer源码分析之二

      Unity时钟定时器插件——Vision Timer源码分析之二 By D.S.Qiu 尊重他人的劳动,支持原创,转载请注明出处:http.dsqiu.iteye.com 前面的已经介绍了vp_T ...

  9. Java并发编程(七)ConcurrentLinkedQueue的实现原理和源码分析

    相关文章 Java并发编程(一)线程定义.状态和属性 Java并发编程(二)同步 Java并发编程(三)volatile域 Java并发编程(四)Java内存模型 Java并发编程(五)Concurr ...

随机推荐

  1. my41_主从延迟大排查

    半同步复制 主库执行 INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so'; SET GLOBAL rpl_semi_sync ...

  2. 关系型数据库和非关系型数据库区别、oracle与mysql的区别

    一.关系型数据库 关系型数据库,是指采用了关系模型来组织数据的数据库.    关系模型是在1970年由IBM的研究员E.F.Codd博士首先提出的,在之后的几十年中,关系模型的概念得到了充分的发展并逐 ...

  3. C# ASP.NET MVC/WebApi 或者 ASP.NET CORE 最简单高效的跨域设置

    概述 前面写了一篇:<C# ASP.NET WebApi 跨域设置>的文章,主要针对 ASP.NET WebApi 项目. 今天遇到 ASP.NET MVC 项目也需要设置跨域,否则浏览器 ...

  4. 统计函数(Excel函数集团)

    此处文章均为本妖原创,供下载.学习.探讨! 文章下载源是Office365国内版1Driver,如有链接问题请联系我. 请勿用于商业! 谢谢 下载地址:https://officecommunity- ...

  5. C语言程序设计:模拟简单运算器的工作

    目录 C语言程序设计:模拟简单运算器的工作 1.题目 2.分析 3.代码实现 4.结尾 C语言程序设计:模拟简单运算器的工作 1.题目 ​ 模拟简单运算器的工作,输入一个算式(没有空格),遇等号&qu ...

  6. Java 中接口和抽象类的 7 大区别!

    本文已收录<Java常见面试题>:https://gitee.com/mydb/interview ​ Java 是一门面向对象的编程语言,面向对象的编程语言有四大特征:抽象.封装.继承和 ...

  7. linux服务器加入AD域(sssd)~ 通过域用户ssh登录加域的linux服务器

    搭建域控:参考 https://www.cnblogs.com/taosiyu/p/12009120.html 域控计算机全名: WIN-3PLKM2PLE6E.zhihu.test.com 域:zh ...

  8. libevent 源码分析

    1,前言 Libevent是一个轻量级的开源高性能网络库,使用者众多,研究者更甚,相关文章也不少.写这一系列文章的用意在于,一则分享心得:二则对libevent代码和设计思想做系统的.更深层次的分析, ...

  9. num_duilib之TabBox用法(21)

    介绍 本文将介绍 使用 TabBox的用法 更多用法,请参考源码 TabBox.h 文件 文件中定义了公有接口,其中包括,添加tab页下的控件,删除.设置TabBox的属性 其中,我常用的有:GetC ...

  10. c++内存分布之虚析构函数

    关于 本文代码演示环境: VS2017+32程序 虚析构函数是一种特殊的虚函数,可以知道,虚函数影响的内存分布规律应该也适用虚析构函数.看看实际结果. Note,一个类中,虚析构函数只能有一个. 本文 ...