单片机
返回首页

Systemlnit时钟系统初始化函数剖析

2022-06-27 来源:eefocus

在工程文件的system_stm32f10x.c中有如下代码:

这里将为大家逐个注释,让大家了解。


void SystemInit (void)

{

 

  RCC->CR |= (uint32_t)0x00000001;//把HSI时钟打开

   //HSION:内部高速时钟使能 (Internal high-speed clock enable) 由软件置’1’或清零。 

   //当从待机和停止模式返回或用作系统时钟的外部4-16MHz振荡器发生故障时,该位由硬件置’1’ 来启动内部8MHz的RC振荡器。

   //当内部8MHz振荡器被直接或间接地用作或被选择将要作为系 统时钟时,该位不能被清零。 

   //0:内部8MHz振荡器关闭; 

   //1:内部8MHz振荡器开启。

 

 //该部分是把相关位设置为0

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

 

#ifdef STM32F10X_CL  //判断宏定义标识符(如果是_HD,这段不执行)

  /* Reset PLL2ON and PLL3ON bits */

  RCC->CR &= (uint32_t)0xEBFFFFFF;

  /* Disable all interrupts and clear pending bits  */

  RCC->CIR = 0x00FF0000;

  /* Reset CFGR2 register */

  RCC->CFGR2 = 0x00000000;

#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)

  /* Disable all interrupts and clear pending bits  */

  RCC->CIR = 0x009F0000;

  /* Reset CFGR2 register */

  RCC->CFGR2 = 0x00000000;      

#else  //_HD执行该段

  /* Disable all interrupts and clear pending bits  */

  RCC->CIR = 0x009F0000;

#endif /* STM32F10X_CL */

 

//hd同样不执行该段    

#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)

  #ifdef DATA_IN_ExtSRAM

    SystemInit_ExtMemCtl(); 

  #endif /* DATA_IN_ExtSRAM */

#endif 

 

//

  /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */

  /* Configure the Flash Latency cycles and enable prefetch buffer */

  SetSysClock();//重点※

 

#ifdef VECT_TAB_SRAM

  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */

#else

  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */

#endif 

}

 

//SetSysClock()的定义

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

}


SetSysClock():

这里选择讲解72MHz为例,该函数的步骤为:


打开HSE时钟(3-8行)

等待HSE时钟就绪(9-24行)

设置flash和三个时钟,确定关系(26-51行)

设置CFGR寄存器确定PLL指针来源和倍频系数(75-80行)

把PLL时钟作为系统时钟的来源(84-97行)

最后有所使用寄存器和系统时钟图参考.


static void SetSysClockTo72(void)

{

  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;

 

/* 使能 HSE 时钟*/    

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

  //在头文件中宏定义了RCC_CR_HSEON对应地址为0x000100000  即位十六——外部高速时钟使能

 

  /* 等待HSE时钟稳定 */

  do

  {

      //通过判断RCC_CR_HSERDY(外部高速时钟就绪标志,地址对应0x000200000,位17)= 1,则表示稳定

    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;//如果就绪了,则让HSEStatus=0x01

  }

  else

  {

    HSEStatus = (uint32_t)0x00;

  }  

 

//判断就绪,然后继续执行

  if (HSEStatus == (uint32_t)0x01)

  {

  //由于cpu速度快于芯片flash很多,所以操作前要等待几个时钟

  //ACR寄存器:LATENCY:时延

    //这些位表示SYSCLK(系统时钟)周期与闪存访问时间的比例000:零等待状态,当0    //001:一个等待状态,当24MHz< sYSCLK ≤48MHz

    //010:两个等待状态,当48MHzACR |= FLASH_ACR_PRFTBE;

    /* 两个等待状态 */

    FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);

    FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2; 

 

 //根据图解可知频率关系

    /* HCLK = SYSCLK 72MHz*/

    //实现则需要AHB预分频器要设置为1

    RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;

    //RCC_CFGR_HPRE_DIV1对应地址0x00000000  不分频的位7:4为0xxx

 

    /* PCLK2 = HCLK 72MHz*/

    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;

    //RCC_CFGR_PPRE2_DIV1对应地址0x00000000  不分频的位13:11为0xx

 

    /*36MHz PCLK1 = HCLK/2 72MHz*/

    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;

    //RCC_CFGR_PPRE2_DIV1对应地址0x00000400  二分频的位10:8为100

 

#ifdef STM32F10X_CL   //判断为_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   //非_CL 执行 

    /*  PLL configuration: PLLCLK = HSE * 9 = 72 MHz */

    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |

                                        RCC_CFGR_PLLMULL));

                                        //直接将HSE作为PLL时钟的来源

    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);//设置PLL 9倍频

 

#endif /* STM32F10X_CL */

 

    /* 使能 PLL时钟 */

    RCC->CR |= RCC_CR_PLLON;

    //RCC_CR_PLLON地址对应0x010000000  位24为1:使能PLL

 

    /* 等待 PLL时钟 稳定 */

    while((RCC->CR & RCC_CR_PLLRDY) == 0)

    {

    }

 

    /* 选择 PLL时针作为系统时针来源 */

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

  }

}


寄存器和系统时钟图参考:


42行:位7:4

HPRE[3:0]:AHB预分频(AHB Prescaler)

由软件置’1’或清0’来控制AHB时钟的预分频系数。

0xxx: sYSCLK不分频

1000: sYSCLK 2分频 1100: sYSCLK64分频

1001: sYSCLK4分频 1101: sYSCLK 128分频

1010: sYSCLK8分频 1110: sYSCLK 256分频

1011:sYSCLK 16分频 1111:sYSCLK 512分频

注意:当AHB时钟的预分频系数大于1时,必须开启预取缓冲器。详见闪存读取(第2.3.3节).


46行:位13:11

PPRE2[2:0]:高速APB预分频(APB2)(APB high-speed prescaler (APB2))由软件置’1’或清’0’来控制高速APB2时钟(PCLK2)的预分频系数。

0xx:HCLK不分频

100:HCLK 2分频

101:HCLK 4分频

110:HCLK 8分频

111:HCLK 16分频


50行:位10:8

PPRE1[2:0]低速APB预分频(APB1) (APB low-speed prescaler (APB1))

由软件置’1’或清’0’来控制低速APB1时钟(PCLK1)的预分频系数。

警告:软件必须保证APB1时钟频率不超过36MHz。

0xx:HCLK不分频

100:HCLK 2分频

101:HCLK 4分频

110:HCLK 8分频

111:HCLK 16分频


进入单片机查看更多内容>>
相关视频
  • RISC-V嵌入式系统开发

  • SOC系统级芯片设计实验

  • 云龙51单片机实训视频教程(王云,字幕版)

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

精选电路图
  • PIC单片机控制的遥控防盗报警器电路

  • 使用ESP8266从NTP服务器获取时间并在OLED显示器上显示

  • 用NE555制作定时器

  • 如何构建一个触摸传感器电路

  • 基于ICL296的大电流开关稳压器电源电路

  • 基于TDA2003的简单低功耗汽车立体声放大器电路

    相关电子头条文章