一、概述
系统时钟,是整个芯片的心脏,如果没有了它,就等于人没有了心跳;在实际工程应用中,每当使用一个外设时,首先需要做的就是打开该外设对应的时钟;这样的好处就是,如果不使用一个外设的时候,就把它的时钟关掉,从而可以降低系统的功耗,达到节能,实现低功耗的效果(低功耗);
寄存器是由D触发器组成的,只有送来了时钟,触发器才能被改写值。任何MCU的任何外设都需要有时钟,8051也是如此;STM32为了让用户更好地掌握功耗,对每个外设的时钟都设置了开关,让用户可以精确地控制,
关闭不需要的设备,达到节省供电的目的。51单片机不用配置IO时钟,只是因为默认使用同一个时钟,这样是方便,但是这样的话功耗就降低不了。51中某个功能不需要,
但是它还是一直运行。stm32中当你想关闭某个IO的时候,关闭它相对应的时钟使能就是了;ARM的芯片都是这样,外设通常都是给了时钟后,才能设置它的寄存器(即才能使用这个外设),
这么做的目的是为了省电,使用了所谓时钟门控的技术。
二、关于时钟
1.时钟分类(stm32所有型号的时钟分为4类)
①、HSI 是高速内部时钟(High Speed Internal Clock Signal)
②、HSE是高速外部时钟(High Speed External Clock Signal)
③、LSI 是低速内部时钟(Low Speed Internal Clock Signal)
④、LSE是低速外部时钟(Low Speed External Clock Signal)
2.时钟源(stm32)
①、HSI内部高速时钟,RC振荡器,频率为8MHz,当HSE故障时,系统时钟会自动切换到HSI,直到HSE启动成功,相对HSE精度小,受温度影响较大,会有温漂。
②、HSE外部高速时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz,多使用8MHz/12MHz。
③、PLL锁相环倍频时钟,其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。
STM32具有以下两个次级时钟源:
①、LSI内部低速时钟,RC振荡器,频率为30~60kHz不等,一般取40kHz,该 RC 用于驱动独立看门狗,也可选择提供给 RTC 用于停机/待机模式下的自动唤醒。②、LSE外部低速时钟,接频率为32.768kHz的石英晶体,主要做RTC时钟源;
(使用32.768kHz是因为2的15次方为32768,32.768kHz的晶振产生的时钟信号经过15次分频后,便会产生频率为1Hz的信号,即为秒脉冲信号)
3.时钟树(stm32)
注:
1)对于不同系列的芯片,时钟树之间会存在或大或小的差异,具体需要查看手册中RCC的章节;
2)MCO接口的两个作用:其一,可以观察波形是否正常;其二,可以作为其他部件的时钟;
三、实验分析
keil软件版本:V5.35.00st官网:STM32 固件 - 意法半导体STMicroelectronics
单片机型号:STM32F103VET6
1、标准库中配置系统时钟分析
1)系统时钟配置的相关代码在这里哦
2)配置详情及描述(stm32)
1 /** 2 * @brief Sets System clock frequency to 72MHz and configure HCLK, PCLK2 3 * and PCLK1 prescalers. 4 * @note This function should be used only after reset. 5 * @param None 6 * @retval None 7 */ 8 static void SetSysClockTo72(void) 9 { 10 __IO uint32_t StartUpCounter = 0, HSEStatus = 0; 11 12 /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/ 13 /* Enable HSE */ 14 RCC->CR |= ((uint32_t)RCC_CR_HSEON); //使能HSE,等待HSE稳定 15 16 /* Wait till HSE is ready and if Time out is reached exit */ 17 do //等待HSE启动稳定,并做超时处理 18 { 19 HSEStatus = RCC->CR & RCC_CR_HSERDY; 20 StartUpCounter++; 21 } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); 22 23 if ((RCC->CR & RCC_CR_HSERDY) != RESET) //判断HSE启动是否成功,置位标志位 24 { 25 HSEStatus = (uint32_t)0x01; 26 } 27 else 28 { 29 HSEStatus = (uint32_t)0x00; 30 } 31 32 if (HSEStatus == (uint32_t)0x01) //如果HSE启动成功 33 { 34 /* Enable Prefetch Buffer */ 35 FLASH->ACR |= FLASH_ACR_PRFTBE; //使能FLASH预存取缓冲区 36 37 /* Flash 2 wait state */ //SYSCLK 周期与闪存访问时间的比例设置,这里统一设置成 2 38 FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY); //设置成 2 的时候, SYSCLK 低于 48M 也可以工作,如果设置成 0 或者 1 的时候, 39 FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2; //如果配置的 SYSCLK 超出了范围的话,则会进入硬件错误,程序就死了 40 //0: 0 < SYSCLK <= 24M 41 //1: 24< SYSCLK <= 48M 42 //2: 48< SYSCLK <= 72M 43 44 //设置AHB、APB2、APB1预分频因子 45 /* HCLK = SYSCLK */ 46 RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; 47 48 /* PCLK2 = HCLK */ 49 RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; 50 51 /* PCLK1 = HCLK/2 */ 52 RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; 53 54 #ifdef STM32F10X_CL 55 /* Configure PLLs ------------------------------------------------------*/ 56 /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */ 57 /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */ 58 59 RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL | 60 RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC); 61 RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 | 62 RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5); 63 64 /* Enable PLL2 */ 65 RCC->CR |= RCC_CR_PLL2ON; 66 /* Wait till PLL2 is ready */ 67 while((RCC->CR & RCC_CR_PLL2RDY) == 0) 68 { 69 } 70 71 72 /* PLL configuration: PLLCLK = PREDIV1 * 9 = 72 MHz */ 73 RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL); 74 RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 | 75 RCC_CFGR_PLLMULL9); 76 #else 77 /* PLL configuration: PLLCLK = HSE * 9 = 72 MHz */ 78 RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC //设置PLL时钟来源,设置PLL倍频因子,PLLCLK = HSE * 9 = 72MHz 79 80 | RCC_CFGR_PLLXTPRE 81 82 |RCC_CFGR_PLLMULL)); 83 84 RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE 85 86 | RCC_CFGR_PLLMULL9); 87 #endif /* STM32F10X_CL */ 88 89 /* Enable PLL */ 90 RCC->CR |= RCC_CR_PLLON; //使能PLL 91 92 /* Wait till PLL is ready */ 93 while((RCC->CR & RCC_CR_PLLRDY) == 0) //等待PLL稳定 94 { 95 } 96 97 /* Select PLL as system clock source */ 98 RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); //选择PLL作为系统时钟来源 99 RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; 100 101 /* Wait till PLL is used as system clock source */ //读取时钟切换状态位,确保PLLCLK被选为系统时钟 102 while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) 103 { 104 } 105 } 106 else //如果HSE启动失败,可以在这里添加错误代码 107 { /* If HSE fails to start-up, the application will have wrong clock 108 configuration. User can add here some code to deal with this error */ 109 } 110 }system_stm32f10x.c
3)另外补充一个例子(gd32)
1 /*! 2 \brief configure the system clock to 72M by PLL which selects HXTAL as its clock source 3 \param[in] none 4 \param[out] none 5 \retval none 6 */ 7 static void system_clock_72m_hxtal(void) 8 { 9 uint32_t timeout = 0U; 10 uint32_t stab_flag = 0U; 11 12 /* enable HXTAL */ 13 /*开启外部高速时钟*/ 14 RCU_CTL0 |= RCU_CTL0_HXTALEN; 15 16 /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */ 17 /*等待外部高速时钟稳定,(当外部晶振稳定后,芯片将自动设置相关标志位,软件只需要不断读取这个标志位就可以知道时钟是否稳定)*/ 18 do { 19 timeout++; 20 stab_flag = (RCU_CTL0 & RCU_CTL0_HXTALSTB); 21 } while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout)); 22 /* if fail 若外部高速时钟异常,上面等待超时,进入这里*/ 23 if(0U == (RCU_CTL0 & RCU_CTL0_HXTALSTB)) { 24 return; 25 } 26 27 28 /*运行到这里,说明外部高速时钟正常启动,下面按照时钟树,来配置系统和各个模块的时钟*/ 29 /* HXTAL is stable */ 30 /* AHB = SYSCLK */ 31 RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; //系统时钟到AHB总线时钟不进行分频 32 /* APB2 = AHB/2 */ 33 RCU_CFG0 |= RCU_APB2_CKAHB_DIV2; //APB2是AHB的2分频 34 /* APB1 = AHB/2 */ 35 RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; //APB1是AHB的2分频 36 37 38 /* PLL = HXTAL * 6 = 72 MHz */ 39 /*现在外部晶振是12M,通过PLL倍频为72M*/ 40 RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PLLMF | RCU_CFG0_PLLMF4 | RCU_CFG0_PLLPREDV); 41 RCU_CFG1 &= ~(RCU_CFG1_PLLPRESEL | RCU_CFG1_PLLMF5 | RCU_CFG1_PREDV); 42 43 RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | (RCU_PLL_MUL6 & (~RCU_CFG1_PLLMF5))); //PLLSEL设置为1,PLL设置为6 44 // RCU_CFG0 |= (RCU_CFG0_PLLPREDV); //HXTAL或CK_IRC48M时钟二分频 (CFG0第17位,与CFG1中的PREDV[0]位是一样的) 45 RCU_CFG1 |= (RCU_PLLPRESEL_HXTAL); //HXTAL选为PLL时钟源 PREDV 选择1分频 46 RCU_CFG1 |= (RCU_PLL_MUL6 & RCU_CFG1_PLLMF5); //PLLMF的第五位设置为0 47 48 49 /* enable PLL */ 50 /*上面的配置完成,使能PLL*/ 51 RCU_CTL0 |= RCU_CTL0_PLLEN; 52 53 /* wait until PLL is stable */ 54 while(0U == (RCU_CTL0 & RCU_CTL0_PLLSTB)) { 55 } 56 57 /* select PLL as system clock */ 58 /*设置SCS[1:0],将时钟切换到刚配置好的PLL这条系统时钟线路*/ 59 RCU_CFG0 &= ~RCU_CFG0_SCS; 60 RCU_CFG0 |= RCU_CKSYSSRC_PLL; 61 62 /* wait until PLL is selected as system clock */ 63 /*等待PLL这条系统时钟配置能稳定给系统提供时钟*/ 64 while(0U == (RCU_CFG0 & RCU_SCSS_PLL)) { 65 } 66 67 /*到此处,系统完成了从8M到200M的切换*/ 68 }system_gd32f3x0.c
2、重新定义并初始化函数
由上文代码可知,程序运行起来会首先执行启动文件,调用systeminit()函数,最终初始化系统时钟;如需改变时钟配置,可以在系统时钟配置文件中修改;但是为了不破坏库函数的完整性,可重新定义并调用初始化函数;
一般情况下,使用HSE经分频倍频后来配置系统时钟;
1)以HSE配置系统时钟为例,按照时钟树流程来完成函数:
1 /** 2 * @brief HSE_SetSysClk program. 3 * @param RCC_PLLMul_x: specifies the PLL multiplication factor[2~16] 4 * @retval None 5 */ 6 void HSE_SetSysClk(uint32_t RCC_PLLMul_x) 7 { 8 ErrorStatus HSEStatus; 9 10 11 RCC_DeInit(); //把RCC寄存器复位 12 13 RCC_HSEConfig(RCC_HSE_ON); //使能HSE 14 15 HSEStatus = RCC_WaitForHSEStartUp(); //等待HSE稳定,返回HSE状态 16 17 if(HSEStatus == SUCCESS) 18 { 19 /*预取缓冲器包含两个数据块,每个数据块有8个字节; 预取指令(数据)块直接映像到闪存中, 20 因为数据块的大小与闪存的宽度相同, 所以读取预取指令块可以在一个读周期完成; 21 设置预取缓冲器可以使CPU更快地执行, CPU读取一个字的同时下一个字已经在预取缓冲器中等候, 22 即当代码跳转的边界为8字节的倍数时, 闪存的加速比例为2; 23 flash等待周期数要系统时钟频率对应, 《stm32f10xxx闪存编程手册》中可以进行了解 */ 24 FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //使能预取指 25 FLASH_SetLatency(FLASH_Latency_2); //2个等待周期 26 27 RCC_HCLKConfig(RCC_SYSCLK_Div1); //配置AHB总线时钟HCLK 28 RCC_PCLK1Config(RCC_HCLK_Div2); //配置APB1总线时钟PCLK1,PCLK1 = HCLK/2 29 RCC_PCLK2Config(RCC_HCLK_Div1); //配置APB2总线时钟PCLK2,PCLK2 = HCLK 30 31 RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_x); //配置锁相环时钟(PLLCLK = HSE * RCC_PLLMul_x = 72MHz),RCC_PLLMul_x为倍频因子,PLL需要先配置再使能 32 RCC_PLLCmd(ENABLE); //使能PLL 33 while(RESET == RCC_GetFlagStatus(RCC_FLAG_PLLRDY)); //等待PLL稳定 34 35 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //选择PLLCLK为系统时钟 36 while(0x08 != RCC_GetSYSCLKSource()); //等待PLLCLK置位系统时钟完成 37 } 38 else 39 { 40 /*如果HSE启动失败,可以在这里添加处理错误的代码*/ 41 } 42 } 43 44 /*在main中调用即可*/ 45 /** 46 * @brief Main program. 47 * @param None 48 * @retval None 49 */ 50 int main(void) 51 { 52 LED_GPIO_Config(); 53 HSE_SetSysClk(RCC_PLLMul_9); 54 // HSI_SetSysClk(RCC_PLLMul_16); 55 MCO_GPIO_Config(); 56 RCC_MCOConfig(RCC_MCO_SYSCLK); 57 58 for(;;) 59 { 60 LED1_TOGGLE; 61 Delay(0xFFFFF); 62 LED1_TOGGLE; 63 Delay(0xFFFFF); 64 } 65 }rcc_clk_config_operation.c 使能MCO,使用示波器抓取波形
1 /** 2 * @brief MCO_GPIO_Config program. 3 * @param void 4 * @retval enable mco for see clock 5 */ 6 void MCO_GPIO_Config() 7 { 8 GPIO_InitTypeDef GPIO_InitStructure; 9 10 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); 11 12 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; 13 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 14 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 15 16 GPIO_Init(GPIOA,&GPIO_InitStructure); 17 18 } 19 20 21 RCC_MCOConfig(RCC_MCO_SYSCLK);mco_gpio_config.c 波形如下:(使用HSE 8*9 = 72MHz; 使用HSE(超频) 8*16 = 128MHz)
2)以HSI作为系统时钟
1 /** 2 * @brief HSI_SetSysClk program. 3 * @param RCC_PLLMul_x: specifies the PLL multiplication factor[2~16] 4 * @retval None 5 */ 6 void HSI_SetSysClk(uint32_t RCC_PLLMul_x) 7 { 8 __IO uint32_t HSIStatus = 0; //加 __IO 防止编译器优化 9 10 11 RCC_DeInit(); //把RCC寄存器复位 12 13 // RCC_HSICmd(ENABLE); //使能HSI,复位时候已经使能了HSI,这里可以不再使能 14 15 HSIStatus = RCC->CR & RCC_CR_HSIRDY; //等待HSI稳定,读取hsirdy寄存器 16 17 if(HSIStatus == RCC_CR_HSIRDY) 18 { 19 /*预取缓冲器包含两个数据块,每个数据块有8个字节; 预取指令(数据)块直接映像到闪存中, 20 因为数据块的大小与闪存的宽度相同, 所以读取预取指令块可以在一个读周期完成; 21 设置预取缓冲器可以使CPU更快地执行, CPU读取一个字的同时下一个字已经在预取缓冲器中等候, 22 即当代码跳转的边界为8字节的倍数时, 闪存的加速比例为2; 23 flash等待周期数要系统时钟频率对应, 《stm32f10xxx闪存编程手册》中可以进行了解 */ 24 FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //使能预取指 25 FLASH_SetLatency(FLASH_Latency_2); //2个等待周期 26 27 RCC_HCLKConfig(RCC_SYSCLK_Div1); //配置AHB总线时钟HCLK 28 RCC_PCLK1Config(RCC_HCLK_Div2); //配置APB1总线时钟PCLK1,PCLK1 = HCLK/2 29 RCC_PCLK2Config(RCC_HCLK_Div1); //配置APB2总线时钟PCLK2,PCLK2 = HCLK 30 31 RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_x); //配置锁相环时钟(PLLCLK = HSI * RCC_PLLMul_x = 64MHz),RCC_PLLMul_x为倍频因子,PLL需要先配置再使能 32 RCC_PLLCmd(ENABLE); //使能PLL 33 while(RESET == RCC_GetFlagStatus(RCC_FLAG_PLLRDY)); //等待PLL稳定 34 35 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //选择PLLCLK为系统时钟 36 while(0x08 != RCC_GetSYSCLKSource()); //等待PLLCLK置位系统时钟完成 37 } 38 else 39 { 40 /*如果HSI启动失败,可以在这里添加处理错误的代码*/ 41 } 42 }rcc_clk_config_operation_hsi.c 波形如下:(使用HSI 8/2 * 16 = 64MHz; 使用HSI 8/2*8 = 32MHz)
四、总结
系统时钟配置需要根据具体的时钟框图,先设置好时钟源,AHB、APB1、APB2的分频系数;再设置好PLL倍频和分频;最后使用SW切换选择系统时钟来源即可。
参考:
1、《STM32F10X-中文参考手册》2、[野火EmbedFire]《STM32库开发实战指南——基于野火指南者开发板》;
3、野火F103-指南者bilibili教程视频【150集-野火F103霸道/指南者视频教程】-中级篇_哔哩哔哩_bilibili;
4、32系统时钟配置 - Darren_pty - 博客园 (cnblogs.com)
5、STM32F4_RCC系统时钟配置及描述 - strongerHuang - 博客园 (cnblogs.com)
6、STM32F2系列系统时钟默认配置 - MyBooks - 博客园 (cnblogs.com)
7、STM32入门系列-STM32时钟系统,时钟使能配置函数 - STM32嵌入式开发 - 博客园 (cnblogs.com)
小弟才疏学浅,如有错误或不足之处,还请大佬批评指正,深表感谢!
标签:HSE,RCU,时钟,stm32,------,PLL,uint32,RCC From: https://www.cnblogs.com/xugdawn666/p/17301374.html