RCC

RCC 是 Reset and Clock Control 模块的缩写,可用于复位片上外设模块寄存器以及设置时钟树的时钟频率。本篇笔记将探讨 MM32F0140 微控制器上复位寄存器和设置时钟的操作流程,并以 pokt-f0140 开发板作为实际演示平台进行实验,实现 48MHz 开发板系统时钟。

RCC 功能描述

软件复位片上外设模块寄存器

通过设置 RCC_AHBRSTR 寄存器、RCC_APB1RSTR 寄存器以及 RCC_APB2RSTR 寄存器中对应外设模块位,可以实现软件复位片上外设模块寄存器。

设置时钟树频率

“图
图 1. RCC 时钟树框图

如图 1 所示,RCC 时钟树主要分成以下四部分:

  • 系统时钟
  • MCO时钟输出
  • 总线时钟
  • 外设时钟

下文将探讨 RCC 时钟树上系统时钟的配置流程、系统时钟与总线时钟的关系以及部分外设时钟与总线时钟的关系。

配置系统时钟

MM32F0140 支持四个独立时钟源:

  • 高速内部时钟(HSI)
  • 高速外部时钟 (HSE)
  • 锁相环(PLL)
  • 低速内部时钟(LSI)

通过配置 RCC_CFGR[SW] ,可以选择不同的时钟源作为系统时钟源。通过读取RCC_CFGR[SWS] 的值,可以确定系统时钟源选择。值得注意的是,当使能上述四个时钟源后,需要等待 RCC 寄存器中对应的时钟准备就绪标志被拉高,才能确认时钟源被使能完成。( eg. 配置 RCC_CR[HSION] = 1 也即使能高速内部时钟后, 需要等待 RCC_CR[HSIRDY] 被拉高后,才能确认 HSI 准备就绪。)

在上述四个独立时钟源中,PLL 配置相对复杂。PLL 频率取决于 PLL 输入时钟频率 FREFIN、 PLL 时钟倍频因子N 以及PLL 时钟分频因子M和P。PLL 频率 FCLKO 的计算公式为

FCLKO = FREFIN * N / ( M * P )

其中,输入时钟频率 FREFIN, 可以通过配置 RCC_PLLCFGR[PLLXTPRE] 和 RCC_PLLCFGR[PLLSRC] 进行选择 ,可选项有 HSI 输出时钟频率 、HSE 输出时钟频率以及 HSE 输出时钟的 2 分频频率。配置此项前,需要使能对应的时钟输入源。分频因子 M = RCC_PLLCFGR[PLL_DM] + 1, P = RCC_PLLCFGR[PLL_PM] + 1;倍频因子 N = RCC_PLLCFGR[PLL_DN] + 1。这三个系数可以通过配置 PLLCFGR 寄存器的相应位实现。M 值 和 P 值的区别在于,M 值是 PLL 对于输入时钟频率的分频, P 值是 PLL 作为系统时钟输出时的分频。

系统时钟与总线时钟

片上总线时钟由系统时钟一次或多次分频后得到。通过设置 RCC_CFGR[PPRE2] / RCC_CFGR[PPRE1] / RCC_CFGR[HPRE] 可以分别配置APB2,APB1 和 AHB1 总线时钟分频系数。总线时钟频率最高可达 72 MHz。

总线时钟与外设时钟

通过配置 RCC_AHBENR 寄存器、RCC_APB1ENR 寄存器以及 RCC_APB2ENR 寄存器的特定外设位,使能总线上外设时钟,从而可以允许总线对外设寄存器的读写操作。

对于大部分外设而言,外设时钟频率与外设所在总线频率相同。但有以下几个特例。

对于非 TIM1 的定时器而言,如果所在总线时钟分频系数不为 1,则定时器时钟频率为 2 倍频的总线时钟频率。TIM1 的时钟频率取决于 AHB 总线时钟和 APB2 总线时钟。如果 AHB 总线时钟和 APB2 总线时钟都不分频,则 TIM1 时钟频率和 APB2 时钟频率相同;如果 AHB 总线时钟和 APB2 总线时钟仅有一方分频,则 TIM1 时钟频率为 2 倍频的总线分频时钟频率。如果 AHB 总线时钟和 APB2 总线时钟都分频,则 TIM1 时钟频率为 4 倍频的 APB 总线时钟频率。

CPU 系统定时器频率 SysTick ,为 8 分频的 AHB 总线时钟频率。

样例

样例1 pokt-f0140 开发板初始化 48MHz 系统时钟

在 SDK 中已有支持的 pokt-f0140 开发板上,在任一样例工程中的 clock_init.c 中,通过 CLOCK_BootToHSE48MHz() 实现 48MHz 系统时钟和总线时钟。其中,HSE的输出时钟频率为12MHz。

主要代码如下:

void CLOCK_BootToHSE48MHz(void)
{
    /* enable HSE.使能HSE时钟。 */
    RCC->CR |= RCC_CR_HSEON_MASK;
    /* check HSE ready mask.时钟准备就绪。 */
    while ( RCC_CR_HSERDY_MASK != (RCC->CR & RCC_CR_HSERDY_MASK) )
    {
    }

    /* F_clko = F_refin * N/(M*P), F_refin = 12M. HSE输出频率为12MHz,倍频系数为8,分频系数为 1*2 = 2,PLL时钟频率为 12*8/2 = 48MHz。 */
    RCC->PLLCFGR = RCC_PLLCFGR_PLLSRC(1) /* (pllsrc == 1) ? HSE : HSI. */
                 | RCC_PLLCFGR_PLLDN(7) /* N = DN+1. */
                 | RCC_PLLCFGR_PLLDM(1) /* M = DM+1. */
                 | RCC_PLLCFGR_PLLDP(0) /* P = DP+1. */
                 | RCC_PLLCFGR_PLLLDS(1)
                 | RCC_PLLCFGR_PLLICTRL(3) /* 10uA. */
                 ;

    /* Enable PLL. 使能PLL时钟并等待时钟准备就绪。*/
    RCC->CR |= RCC_CR_PLLON_MASK;
    while((RCC->CR & RCC_CR_PLLRDY_MASK) == 0)
    {
    }

    /* Enable the FLASH prefetch. */
    RCC->AHB1ENR |= RCC_AHB1ENR_FLITFEN_MASK; /* enable the access to FLASH. */
    FLASH->ACR = FLASH_ACR_LATENCY(1u) /* setup divider: 1 for 48Mhz, 2 for 72MHz.. */
               | FLASH_ACR_PRFTBE_MASK /* enable flash prefetch. */
               ;

    /* Setup the dividers for each bus. 为各总线设置分频系数。 */
    RCC->CFGR = RCC_CFGR_HPRE(0)   /* div=1 for AHB freq. */
              | RCC_CFGR_PPRE1(0x0)  /* div=1 for APB1 freq. */
              | RCC_CFGR_PPRE2(0x0)  /* div=1 for APB2 freq. */
              | RCC_CFGR_MCO(7)    /* use PLL/2 as output. */
              ;

    /* Switch the system clock source to PLL. 选择系统时钟源为PLL。*/
    RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_SW_MASK) | RCC_CFGR_SW(2); /* use PLL as SYSCLK */


    /* Wait till PLL is used as system clock source.检查SWS标志,确认系统时钟源设置。 */
    while ( (RCC->CFGR & RCC_CFGR_SWS_MASK) != RCC_CFGR_SWS(2) )
    {
    }
}

样例2 pokt-f0140 开发板使能 GPIOA 模块时钟

在 SDK 中已有支持的 pokt-f0140 开发板上,可以通过以下代码使能 GPIOA 模块时钟。

RCC_EnableAHB1Periphs(RCC_AHB1_PERIPH_GPIOA, true);

样例3 pokt-f0140 开发板复位 GPIOA 模块寄存器

在 SDK 中已有支持的 pokt-f0140 开发板上,可以通过以下代码复位 GPIOA 模块寄存器。

RCC_ResetAHB1Periphs(RCC_AHB1_PERIPH_GPIOA);

来源:灵动MM32MCU
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 51

RCC时钟模块并不好理解,初次接触我也是一头雾水,而且我真正掌握它的时候也比较晚,是我在学习uC/os-II,需要分析时钟时才有了深刻认识。但在学习中我却一定要把放在了前列,因为这是整个嵌入式最重要的基础之一,可以说是M3芯片的心脏。初学者理解是比较困难,但是掌握清晰对于嵌入式操作系统特别是Timer定时器以及通讯领域具有重大意义。

下面进入正题,先上一张RCC模块的结构图:

STM32学习笔记——RCC外设的学习和理解

初看此图是不是感觉太复杂了,事实上我第一次看这张图的时候也是的,完全理不清结构,不过不用担心,下面我就分层带你来理解这幅图。

(1)时钟源(4个晶振源,1个中介源)

HSI(RC):内部高速晶振,~8MHz

HSE(Osc):外部高速晶振(与电路设计时选择有关,25MHz)

LSE(Osc):外部低速晶振(默认为32.768KHZ)

LSI(RC):内部低速晶振,~40KHz

PLLCLK:锁向环倍频输出,最大频率小于72MHz,注:PLLCLK来源HSE,HSE/2,HSI/2

(2)系统时钟源

SYSCLK:系统时钟

来源HSI,PLLCLK,HSE,若CSS(时钟监视系统)检测到HSE失效,SYSCLK = HSI;

(3)主要输出时钟源

HCLK:高性能总线时钟(SYSCLK通过AHB Prescaler,最高72MHZ)

PCLK1:外设1区域时钟(通过APB1 Prescaler,最高36MHZ)

PCLK2:外设2区域时钟(通过APB2 Prescaler,最高72MHZ)

此外APB1,APB2外设时钟除了给对应外设区域提供时钟外,还可通过TIMERX Prescaler分配不同的定时器时钟。

ADCCLK:ADC外设时钟(PCLK2通过ADC Prescaler)

USBCLK:通用串行接口时钟(PLLCLK通过USB Prescaler,等于48MHZ)

RTCCLK:实时时钟,来源LSI,LSE,HSE/128

IWDGCLK:独立看门狗时钟,来源LSI

MCO:输出内部时钟

从上面看,我们前面学到的GPIOD外设还有后面的USART等的时钟都没有提到,为什么,其实它们包含在PCLK1,PCLK2这两个外设区域时钟里,也就是说他们的外设时钟来源于该区域的时钟。下面是STM32Fxxx固件函数库中15.2.22以及15.2.23所提到的图,包含所有外设对应的区域:

PCLK1时钟区域:

STM32学习笔记——RCC外设的学习和理解

STM32学习笔记——RCC外设的学习和理解

PCLK2时钟区域:

STM32学习笔记——RCC外设的学习和理解

通过上诉两张图可以清晰的知晓我在第一章节流水灯时时钟使能如此选择APB2外设的原因,当然我是以stm32f10x为例的,如果你使用不同的芯片就要去查相应的寄存器手册。了解了这些其实已经对系统时钟掌握差不多了,下面我就以寄存器控制方式展现嵌入式时钟的配置(库函数操作对于理解时钟配置过程的帮助并不大,特别只是单纯调用而没有理解每个函数内容的情况下)。

(4)系统时钟配置实例(以使用HSE晶振,最后系统时钟为50MHZ为例)

注:RCC寄存器功能可参考《STM32中文参考手册》6.3(互联型产品)

unsigned char PLL = 4;       //PLL为设定放大的倍数
unsigned char temp = 0;

//1.HSE时钟使能                 //时钟控制寄存器 RCC->CR
RCC->CR &= 1<<16;          //使能HSE 
While(!(RCC-CR)>>17));     //判断HSE就绪标志位

//2.配置PLL,APB2,APB1,AHB     //时钟配置寄存器RCC->CFGR
RCC->CFGR |= 0x00000400; 
PLL-=2;            //AHBCLK = SysTick, APB2CLK = AHBCLK,APB1CLK = AHBCLK/2 RCC->CFGR |= PLL<<18;   //参考寄存器功能表,0010~4倍,依次增加,最大9倍 RCC->CFGR |= 3<<16; //HSE/2作为PLL的输入 
//3.FLASH预存取即时钟配置            //FLASH预存取寄存器FLASH->ACR //注:具体参见《STM32闪存flash编程》,但有一点要注意,系统时钟大于30MHZ后一定需要配置,默认24MHZ后就需要配置
FLASH->ACR |= 0x32;        //开启预存取,2个等待周期
 //4.PLL使能                     //时钟控制寄存器   RCC->CR
RCC->CR |= 1<<24;     //PLL使能
while(!(RCC->CR>>25));     //判断PLL使能标志位

//5.PLL作为系统时钟
RCC->CFGR | = 0x00000002;    //PLL作为系统时钟
while(temp!=0x02)     //PLL成功作为系统时钟,标志位
{
  temp= RCC->CFGR>>2;
  temp&= 0x03;
}

通过上述方式就完成了时钟的一般外设的时钟初始化设置,配置系统时钟50MHZ,APB2外设时钟50MHZ,APB1外设时钟25MHZ,理解了这些,延时时间和通讯速率等通过计算即可精确的知晓,这对于整个stm32的学习以及后续理解嵌入式实时操作系统都具有重要意义。

来源:IT点滴

围观 496
订阅 RSS - RCC