STM32时钟系统

一、复位

复位也就是重启,使系统回到初始状态;

复位的三种形式:上电复位,系统复位和备份区复位;

系统复位;除了时钟控制器RCC-CSR寄存器中的复位标志位和备份区域中的寄存器以外,系统复位将复位所以的寄存器至他们复位状态;

当触发以下事件时,将会产生一个系统复位:

1.NRST引脚上的低电平(外部复位);

2.窗口看门狗计数终止(WWDG复位);

3.独立看门狗计数终止(IWDG复位);

4.软件复位(SW复位);

5.低功耗管理复位;

二、时钟CLK

2.1时钟的简介

对于单片机来说,时钟是具有周期性的脉冲信号,最常用的是占空比为50%的方波;

时钟相当于单片机的心脏,想要使用单片机的外设,必须开启相应的时钟,驱动外设的本质是操作寄存器,而寄存器是由D触发器构成的,而触发器需要时钟才能改写值,所以要想操作寄存器必须开启对应外设的时钟;

对于cpu来说,假设 cpu在一个时钟周期内执行一条指令(二进制代码),若时钟频率越高,而时钟等于1/f为频率的倒数,则时钟周期更短,在相同的时间内CPU可以执行更多的指令,CPU运行速度更快;

时钟的走向及关系很重要;

STM32时钟系统主要的目的是给相对独立的外设模块提供时钟,也是为了降低整个芯片的功耗,所以外设的时钟默认都是关闭状态,当我们要使用某个外设就要开启这个外设的时钟,不同外设所需的时钟频率也不同,没必要所有外设都用高速时钟造成浪费,而且有的外设也接受不了这么高的频率,这也就是为什么STM32有四个时钟源的原因,就是兼容不同速度的外设,STM32的四个时钟源:HSE,HSI,LSE.LSI

2.2时钟分类

2.2.1 HSI振荡器时钟(内部高速时钟);

来源:HSI时钟信号由内部8MHz的RC振荡器产生;
作用:可以直接作为系统时钟或在二分频后作为PLL的输入,HSI RC振荡器能够在不需要任何外部器件的条件下提供系统时钟。它的启动时间比HSE晶体振器短。然而,即使在校准之后它的时钟频率精度仍较差
控制:时钟寄存器(参考手册);

2.2.2HSE振荡器时钟(外部高速时钟);

来源:HSE外部高速时钟信号由俩种方式产生:

1.HSE外部晶体/陶瓷谐振器;

2.HSE外部用户晶体

作用:可不分频或2分频作为PLL锁相环的输入,还可以直接不分频作为系统时钟输入,128分频作为外设RTC时钟的输入为了减少时钟输出的失真和缩短启动稳定时间,晶体/陶瓷谐振器和负载电容器必须尽可能地靠近振荡器引脚。负载电容值必须根据所选择的振荡器来调整。

控制:参考手册;

2.2.3PLL时钟(锁相环)

来源:PLL的设置(选择HSI振荡器除2或者HSE振荡器为PLL的输入时钟,和选择倍频因子)必须在其被激活前完成,一旦PLL被激活,这些参数就不能被改动;

作用:内部PLL可以用来倍频HSI RC的输出时钟或者HSE晶体输出时钟,倍频后的PLLCLK可以作为系统时钟源;

如果PLL中断在时钟中断寄存器里被允许,当PLL这准备就绪时,可产生中断申请;

*PLL选择HIS二分频时候,PLLMUL最大只能是16,这是PLLCLK最大为64MKHZ,小于官方推荐的72MKZ,如果需要使用USB接口,PLL必须被设置为48或者72MHZ,用于提供48MHZ的USBCLK时钟;

2.2.4 40KHZ的内部低速RC(RC振荡器)

来源:内部芯片,LSI RC担任了一个低功耗时钟源的角色,它可以在停机和待机模式下保存运行,为独立看门狗和自动唤醒单元提供时钟

作用:可用于驱动独立看门狗和通过程序选择驱动RTC;

控制:参考手册;

*RTC用于从停机或者休眠模式下自动唤醒系统;

2.2.5 32.768KHZ低速外部晶体

来源:LSE晶体是一个32.768Khz的低速外部晶体或者陶瓷谐振器,它为实时时钟或者其他定时功能提供了一个低功耗且精确的时钟源;

作用:用来通过程序选择驱动RTC;

控制:参考手册;

当不被使用的时候任何时钟源可被独立的关闭或者启动;

2.2.6系统时钟

系统时钟:SYSCLK,最高位72M;

来源:HSI,HSE,PLLCLK;

控制:SW,CFGR;

*通常配置:SYSCLK=PLLCLK=72M;

系统复位后,HSI振荡器被选为系统时钟。当时钟源被直接或通过PLL间接作为系统时钟时,它
将不能被停止。 只有当目标时钟源准备就绪了(经过启动稳定阶段的延迟或PLL稳定),从一个时钟源到另一个时 钟源的切换才会发生。在被选择时钟源没有就绪时,系统时钟的切换不会发生。直至目标时钟源就绪,才发生切换。

SYSCLK时钟源:

通常配置:

2.2.7HCLK时钟

HCLK:AHB高速总线时钟,速度最高位72M;

作用:为AHB总线的外设提供时钟,为Cortex系统定时器提供时钟(Systick)、为内核提供时钟(fclk);

来源:系统时钟分频得到,一般设置HCLK=SYSCKL=72M;

控制:寄存器控制(参考手册);

2.2.8PCLK1

PCLK1:APB1低速总线时钟,最高位36M;

作用:为APB1总线的外设提供时钟。1或2倍频后为APB1总线的定时器2-7提供时钟,最大位72M;

来源:HCLK分频的来,一般配置位PCLK1=HCLK/2=36M;

 

2.2.9PCLK2

PCLK2:APB2高速总线时钟,最高为72MHZ;

作用:为APB2总线的外设提供时钟,为APB2总线定时器1和8提供时钟,最大为72M,还有一路输出供给ADC使用;

来源:HCLK分频的来,一般配置为PCLK2=HCKL=72M;

控制:参考手册;

2.2.10RTC时钟

RTC时钟:为内部的RTC外设提供时钟;

来源:HSE_RTC分频的来,LSE,LSI;

控制:RCC备份控制寄存器(参考手册);

2.2.11独立看门狗时钟:

IWDGCLK,LSI提供;

2.2.12时钟安全系统CSS

时钟安全系统可以通过软件被激活,一旦其被激活,时钟监视器将在HSE振荡器启动延迟后被使能,并在HSE时钟关闭后关闭。

2.2.13MOC时钟

MOC:微控制器时钟输出引脚;由PA复用所得,可以把时钟信号输出供外部使用,也可以用示波器检测时钟信号的参数(峰峰值,频率等)

来源:PLLCLK/2,HSE,HSI,SYSYCLK

控制:MCO;

2.2.14时钟树

具体以什么作为时钟源以如何分频:主要看系统的初始化,通过软件的方式进行配置;

3.时钟配置函数

3.1系统时钟初始化过程

打开之前创建的工程,系统启动.s的汇编文件后;

启动上电后,执行启动文件(汇编代码编写)中的复位程序;

执行复位程序:1.调用Systemlnit系统初始化函数完成系统时钟的配置;

2.调用_main函数初始化堆栈指针,然后在调用C库函数main函数,去到C语言的世界;

所以跳转到C语言的main函数时,已经完成了系统时钟SYSCLK的配置;

首先执行的是SystemInit系统初始化函数;

SystemInit系统初始化的功能:设置微控制器系统
  * 初始化嵌入式闪存接口和 PLL,并更新 
  * 系统核心时钟变量。

查看SystemInit内容,右击选择go to 前往

*

对寄存器进行操作,官方固件库提供的函数,实现的时候通过寄存器进行操作,把寄存器操作封装好,提供给用户使用,我们使用时候,只需要调动函数就行了,一般我们不会修改时钟,我们想要修改,就通过修改初始化函数中,该寄存器设置的一些值,来修改它的时钟源和分配系数;

分析:

RCC->CR |= (uint32_t)0x00000001;
在系统初始化前期:使能了内部高速时钟HSI

将RCC指向的寄存器CR或等(|=)于0x00000001;,把CR寄存器第0位置1,复位RCC时钟,使能了内部高速时钟;

/* 将 RCC 时钟配置重置为默认重置状态(用于调试) */
  /* 设置 HSION 位 */

 /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
#ifndef STM32F10X_CL
  RCC->CFGR &= (uint32_t)0xF8FF0000;
#else
  RCC->CFGR &= (uint32_t)0xF0FF0000;
#endif /* STM32F10X_CL */   
  
  /* Reset HSEON, CSSON and PLLON bits */
  RCC->CR &= (uint32_t)0xFEF6FFFF;

  /* Reset HSEBYP bit */
  RCC->CR &= (uint32_t)0xFFFBFFFF;

  /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
  RCC->CFGR &= (uint32_t)0xFF80FFFF;

#ifnde通过条件编译的写法,如果定义了CL(互联型产品)就执行CL后面的代码,否则就执行后面的代码,CL代表互联型成品,我们选择的不是互联型所以执行

 RCC->CFGR &= (uint32_t)0xF0FF0000;

&= (把后面的清0)   

0XF0FF0000(一个十六进制位等于四个二进制位)=11110000111111110000000000000000

位10:00即选择HS1作为系统时钟;位32即判断时钟状态

第一位SW相对时钟树种SW选择时钟源对应:

第二位十六进制0对应二进制0000,对应 10987654

代表AHB的预分频选择SYSCLK不分频;

低速APB1预分配:0XX HCLK也不分频

26:24:001

系统sysclk设置:
上面对时钟设置进行了简单分析,其他的部分可以参照上面方法,查询手册进行分析:
下面是系统时钟设置:
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
 

通过条件编译的方式来确定时钟源的:如果定义了SYSCLK_FREQ_HSE则执行这个 SetSysClockToHSE();

如何确定走的是那条分支,通过鼠标右击跳转的方式来判断是否定义了;

最后经过尝试发现定义了是72KHZ的;

所以我们go to跳转到这个函数:

分析:在这个函数中设置了系统时钟;

开启了外部的高速时钟,使能了HSE;

RCC->CR |= ((uint32_t)RCC_CR_HSEON);

等待外部高速时钟已经准备好退出,等待HSE就绪并作超时处理;

do
  {
    HSEStatus = RCC->CR & RCC_CR_HSERDY;
    StartUpCounter++;  
  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

如何使能:

 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;

使能预取指CPu在FLASH取代码

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设置没有分频

PCLK2设置等于HCLK也没有分频

PCLK1,2分频

条件编译如果定义了是互联网产品则执行前面代码,如果不是执行后面代码;我们这里选择的不是互联网产品,所以执行后面的代码

#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 */
  }
}
#endif

/**
  * @}
  */

/**
  * @}
  */
  
/**
  * @}
  */    
/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/

锁相环配置:PLLCLK=HSE*9=72MHZ;

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

使能PLL;

  RCC->CR |= RCC_CR_PLLON;

   

等待PLL稳定;


    /* Wait till PLL is ready */
    while((RCC->CR & RCC_CR_PLLRDY) == 0)
    {
    }
 

选择PLL作为系统时钟

/* Select PLL as system clock source */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;    

 

等待PLLCLK切换为系统时钟

  /* Wait till PLL is used as system clock source */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
    {
    }
  

接的外部晶振是8KHZ,经过PLL九倍频后位72KHZ给到SYSCLK;

系统时钟源:PLL
选择外部高速晶振:8MHz
经过PLL倍频器9倍频后提供给sysclk = 8*9 = 72MHz
HCLK没有对sysclk进行分频,所以HCLK = 72MHz
PCLK1对HCLK进行2分频,所以PCLK1 = 36MHz
PCLK2对HCLK没有进行分频,所以PCLK2 = 72MHz
TIM2——7:72MHz
TIM1和TIM8:72MHz
  • 25
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值