一、时钟树概述
目前用到的STM32的许多片上外设都需要先打开相应的时钟,告诉当前这个器件是以什么频率在运行,而每个片上外设的时钟频率其实在出厂时已经固化。
二、时钟树框架
25M / 25 * 336 / 2 = 168M
系统时钟计算公式:SYSCLK=PLLCLK = HSE / M *N / P = 168M
系统时钟 (SYSCLK==168MHZ)可以来自以下三种不同的时钟源。
● HSI 振荡器时钟
● HSE 振荡器时钟
● 主 PLL (PLL) 时钟
留心:芯片内部未使用时默认都是关闭的,目的是降低功耗,可以单独打开或者关闭。
3概念补充:
(1)MCO1/2: 微处理器的时钟输出,可以向外提供时钟源而且也可以测量芯片内部时钟是否稳定正确。
(2)芯片内部的工作时钟可以由以下四个时钟提供:(重点)
①LSI(low speed inner):内部低速时钟:该时钟是芯片内部的一个32KHZ的RC振荡器,该时钟非常不稳定,一般很少使用。
作用对象:独立看门狗和RTC
②LSE(外部低速时钟):该时钟是由芯片外部提供的的一个32.768KHZ的有源晶振,该时钟要求要很稳定,是RTC专用时钟。
作用对象:RTC也可以向外提供一个时钟源。
③HSI(内部高速时钟):该时钟是芯片内部的一个16MHZ的RC振荡器,该时钟用途很多。
作用对象:向外输出一个时钟源、给系统提供时钟、作为PLL时钟输入源。
④HSE(外部高速时钟):该时钟是由芯片外部提供的的一个25MHZ的有源晶振,该时钟非常稳定.
作用对象:向外输出一个时钟源、给系统提供时钟、作为PLL时钟输入源,作为RTC的时钟输入源。
(3)锁相环(PPL):使得输入和输出两个频率同步即具有稳定频率的作用。能使输出频率是输入频率的倍数,可以实现对输入输出信号的进行调制。
在APB1总线上的外设时钟频率为42M,但是定时器的时钟频率是84M
在APB2总线上的外设时钟频率为84M,但是定时器的时钟频率是168M
框架分析思路:
芯片要想工作需要一个时钟输入,对于该芯片(40x)时钟频率最高168Mhz,所以最终我们需要获得一个168MHZ(系统要求的) 的时钟作为系统时钟,再由该系统时钟经过分频得到各个模块的时钟。
经分析:系统时钟可以来自以下三个输入源:即HIS/HSE/PLL(锁相环)
由上图可知HIS(16MHZ)和HSE(25MHZ)都不能直接选为系统时钟(要求是168MHZ),经分析虽然HIS(16MHZ)和HSE(25MHZ)都不能直得到系统时钟(168MHZ),但是可以选择PLLCLK作为系统时钟,而PLLCLK的时钟要经过锁相环(PLL)处理才能得到。
三、时钟树程序设计
时钟设置步骤如下:
1使能HSI 。
2等待HSI打开成功。
3设置电压调节器输出级别默认就行)。
4打开电源开关(默认就行)
5设置AHB/APB1/APB2的分频值。
6关闭主锁相环锁 (可以不要)
7等待关闭成功(可以不要)
8配置PLL(M/N/P包括选择HSI作为锁相环输入时钟源)。
9开启主PLL。
10等待主锁相环稳定。
11设置flash的相关参数(缓存,存取指令数据缓存,等待周期等 默认就可以)
12切换PLL为系统时钟源。
13 等待PLL作为系统时钟切换成功。
void SystemInit(void)
{
/* Reset the RCC clock configuration to the default reset state ------------*/
/* Set HSION bit */
RCC->CR |= (uint32_t)0x00000001;//把RCC的第0位置1
/* Reset CFGR register */
RCC->CFGR = 0x00000000; //CFGR寄存器全部复位
/* Reset HSEON, CSSON and PLLON bits */
RCC->CR &= (uint32_t)0xFEF6FFFF; //复位HSE,CSS,PLL
/* Reset PLLCFGR register */
RCC->PLLCFGR = 0x24003010;//对于后面需要配置的PLL相应的时钟复位成原本的值
/* Reset HSEBYP bit */
RCC->CR &= (uint32_t)0xFFFBFFFF;//复位HSEYP的位
/* Disable all interrupts */
RCC->CIR = 0x00000000; //复位中断
}
static void SetSysClock(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0; //StartUpCounter 超时计数变量,HSEStatus 标志位
/* Enable HSE */
RCC->CR |= ((uint32_t)RCC_CR_HSEON);//使能HSE的振荡器
do
{
HSEStatus = RCC->CR & RCC_CR_HSERDY;//要不就是0,要不就是非零值
StartUpCounter++;//累加
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
//上面的do while出来的条件有两个:一个是HSE就绪,一个是等待的时间到了
//判断HSE有没有就绪
if ((RCC->CR & RCC_CR_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
/* Select regulator voltage output Scale 1 mode */
RCC->APB1ENR |= RCC_APB1ENR_PWREN;//使能电源接口时钟
PWR->CR |= PWR_CR_VOS;
/* HCLK = SYSCLK / 1*/
RCC->CFGR |= RCC_CFGR_HPRE_DIV1;//AHB为1分频
/* PCLK2 = HCLK / 2*/
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;//APB2为2分频
/* PCLK1 = HCLK / 4*/
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;//APB1为4分频
RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
(RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
//M分频是25,N倍频是336,P分频是2,选择 HSE 振荡器时钟作为 PLL 和 PLLI2S 时钟输入
/* Enable the main PLL */
RCC->CR |= RCC_CR_PLLON;//开启PLL时钟源
/* Wait till the main PLL is ready */
while((RCC->CR & RCC_CR_PLLRDY) == 0)//死等PLL就绪
{
}
/* Select the main PLL as system clock source */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= RCC_CFGR_SW_PLL; //选择PLL作为系统时钟源168M
}
HSE改为HSI最后还是168M