STM32工作笔记045---SystemInit时钟系统初始化函数剖析

技术交流QQ群【JAVA,C++,Python,.NET,BigData,AI】:170933152

systeminit这个函数对,时钟系统有配置,是通过配置寄存器实现的.

需要看上一讲之后再看这一讲

复习一下.

这里CR寄存器,CFGR寄存器,AHBENR寄存器,APB2ENR寄存器,APB1ENR寄存器,对应的寄存器

用法可以在上讲或者文档中找到.

然后打开,之前的跑马灯实验,库函数版,来看一下代码

找到系统初始化函数

看之前先要准备,时钟框图和寄存器文档.

可以看到,这里

RCC->CR|=(uint32_t)0x00000001

可以看到,这里首先把时钟寄存器的CR寄存器的,最低位设置为1,其他位不变.

可以看到他的注释是SET HSION BIT,也就是打开内部高速时钟寄存器,

去看一下文档,是不是说,这么配置的

可以去看一下,这里HSION,这里,只要是把位0设置为1,就是把内部8MHZ的振荡器打开.

HSI RC打开.

再看一下,这里,#ifndef STM32F10X_CL

因为咱们这里,设置的是大容量的HD

所以,这里走的时候,因为设置了大容量STM32F10X_HD

所以这里会走下面的分支

  /* 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 SW...这些设置一个初始值,设置一个默认值.

注意这里的这个STM32F10X_CL这里的这个意思是,类似于系统定义的一个宏定义,这个每个系列的芯片

这个值也不一样.具体对于STM32F10X_CL的讲解,有个博文对他说了一下.

这部分也是把相应的位设置为0

这些都是把对应的位设置为0,也就是可以看到8的地方,只要不是F的地方,就有0的位置,也就是说,会把

对应0的位置,进行与运算从而设置成了0.

&=这个与运算

这里可以对照看看PLLON这个寄存器,就是把位24设置为了0

这里的判断跟上面的判断是一样的,然后,这里的意思是初始化的时候把所有的

中断和相应的位都关掉设置为0

这部分代码也是上面这个意思,做初始化用的.

这里的SetSysClock,这个是很重要的.

这次说的重点.

去看看定义的位置

系统时钟结构体定义了不同频率的时钟.

去看看.c文件中,可以看到下面的文件中,开始的时候

可以看到定义初始化的频率了已经定义了72mhz的.

因为这里设置的是72mhz的,所以上面的代码会调用

SetSysClockTo72(void);

这个代码会重点看一下.一行一行的看

去找到RCC_CR_HSEON这个定义.

可以看到,这里,有个第

#define  RCC_CR_HSEON                        ((uint32_t)0x00010000)        /*!< External High Speed clock enable */

4*4=16,也就是这里的1是第16位.

可以看到使能的是外部高速时钟使能.打开外部高速时钟.

然后上面的部分是,等待高速外部时钟稳定.

打开时钟源以后,都需要等待他稳定以后,再去使用才行.

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

//这里这个do循环是用来等待时钟稳定,这里有个标志位可以判断时钟是否稳定
//这里可以看到通过获取RCC->CR 的RCC_CR_HSERDY这个ready位,来判断
//这个时钟是否已经稳定的.

去这个rdy的定义可以看到

#define  RCC_CR_HSERDY                       ((uint32_t)0x00020000)        /*!< External High Speed clock ready flag */.

这个第17位是CR时钟的就绪位,

如果这位是1的话,那么就代表外部的时钟已经就绪了.

接下来再看:

 if ((RCC->CR & RCC_CR_HSERDY) != RESET)
  {
    HSEStatus = (uint32_t)0x01;
  }
  else
  {
    HSEStatus = (uint32_t)0x00;
  }  

//这里如果就绪了就把HSEStatus设置为就绪0x01
//如果没有就绪就设置为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;    
//看这几个对flash的ACR寄存器操作的代码.
//关于这个flash的配置,可以参考下面的文档.

去看

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;    
//这部分代码其实就是
//因为内部CPU的速度要比这个flash的速度快很多
//所以这里要等待flash时钟.

可以看弹道当时钟达到48mhz,和72mhz的频率的时候,时钟有两个等待状态.

可以看到这里:

 FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;  

有两个等待状态.

接下来:

 /* HCLK = SYSCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
//这个HCLK =SYSCLK可以看到

HCLK=SYSCLK这个意思是,把这个AHB 这个分频器设置为1

之前已经说了这个:

 /* HCLK = SYSCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
//这个CFGR这寄存器,实际上是可以用来配置,选择哪个时钟源
//以及给时钟源设置分频系数

去看一下CFGR寄存器

这里可以看到,这个不分频,要设置位0XXX;不分频

去看一下代码

然后去看看这个:

RCC_CFGR_HPRE_DIV1

定义

可以看到设置的就是不分频.也就是上面文档中标注的.

这里可以看到这里设置PCLK2=HCLK可以看到,

意思就是下面图中的情况,当APB2的,分频系数设置为1的时候

这个时候PCLK2=HCLK

去看文档的话,

可以看到APB2分频器,也需要设置为HCLK的时候,不分频的时候,也是设置为0xxx

去看一下.

看到定义的地方,可以看到

对应的,寄存器,确实也设置为了0

再看下一句:

PCLK1=HCLK

这里的

PCLK1=HCLK

这里是备注,写错了

实际上应该是两分频

去具体看一下代码就知道.

也可以看到这里的DIV2这个就是两分频的意思.

去看一下代码,这里是0X00000400

第10位

从第0位开始数,就是0100 0000 0000

1的位置是第10位.

也可以看到设置为100也就是4的时候,其实是HCLK2分频,可以看到

这个是官方提供的库的备注的一个错误.

HCLK=SYSCLK=PLK2=72mhz的话

PCLK1=36mhz

这个部分是设置时钟的初始化状态.

再往下看:

#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 */
//1.STM32F10X_CL 这一部分,因为这里我们用的是HD,大容量,其实这个地方的
//STM32F10X_CL是对应的某个系列的,而这里咱们STM32F103
//这个系列的是走的else
//这里else可以看到
//RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
//比如这个HSE,外部晶振产生8MHZ的频率,这个*9 这样就达到72MHZ的一个频率
//

这个图中也能看到,这里HSE产生一个频率他可以产生4-16mhz的频率,

然后PLL经过9倍,倍频,会给SYSCLK这个系统时钟一个72MHZ的时钟频率,

然后然后72mhz的时钟频率,经过SYSCLK系统时钟,会产生一个HCLK 72MHZ的时钟

然后经过AHB  1倍频的,频率,再经过APB1产生一个36mhz的频率,

然后经过AHB 1倍频的时钟频率,再经过APB2的 1 倍频产生一个 72mhz的PCLK2 的频率

这个就是上面那段代码的意思.

在上面的代码,做完,对PLL的时钟选择,对系统时钟的时钟源选择,对分频,频率都设置好以后.

就可以打开PLL时钟

    /* Enable PLL */
    RCC->CR |= RCC_CR_PLLON;
//就是操作CR寄存器.

而且文档中可以看到,打开PLL时钟,要使能CR时钟的位24

也可以看定义

这里可以看到是第24位

使能PLL以后,再等待PLL时钟就绪.

可以看到时钟就绪,这个是第25位.

然后再把PLL作为一个系统时钟的来源.

系统时钟的选择,这里也有配置.

去看看定义这里:

这里2就代表10

上面文档中的,选择PLL输出作为系统时钟.

这个函数的代码,就已经都说了

再从头到尾的看一下这个函数:

/**
  * @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 */  
  //1.首先打开HSE,  把外部高速时钟打开.
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);
 
  /* Wait till HSE is ready and if Time out is reached exit */
  do
  {
  //2.然后等待HSE时钟就绪
    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;
   //3.然后设置flash
    /* Flash 2 wait state */
    FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
    FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;    

    
   //4.然后设置三个时钟.
   //设置分频系数,确定各个时钟HCLK PCLK1 PCLK2 跟系统SYSCLK 时钟的关系
    /* 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    
    //5.然后把PLL的倍频系数和时钟来源设置好.
    /*  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 */

   //6.然后先把PLL时钟打开
    /* Enable PLL */
    RCC->CR |= RCC_CR_PLLON;

    /* Wait till PLL is ready */
    while((RCC->CR & RCC_CR_PLLRDY) == 0)
    {
    }
    
    //7.然后把系统时钟切换到PLL上面,就是这样的过程
    /* 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****/

这里再去搜索SystemCoreClock

可以看到这里,确定了系统时钟为多少了以后,把确定的系统时钟就

设置给SystemCoreClock这个变量了.

也就是说,后面咱们可以通过这个变量来获取到系统时钟的频率是多少的

SystemCoreClock

这样的话,就可以判断系统时钟的大小了.

这里面还有个问题就是

这个每个库函数的实验,这里main函数里,都没有调用systeminit这个函数.

那么他是怎么实现执行main函数之前的时候,会自动去执行systeminit函数的呢

这段代码不知道具体什么原理,但是大体意思是

当系统复位的时候,先执行Systeminit,再去执行main函数.

这里如果你想写自己的系统初始化函数,是可以这样的,把自己写的系统初始化函数的名字写在这里就可以了.

替换掉Systeminit

猜你喜欢

转载自blog.csdn.net/lidew521/article/details/108147209
今日推荐