完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
STM32为什么要有复杂的时钟系统
首先STM32 本身非常复杂,外设非常的多,但是并不是所有外设都需要系统时钟这么高的频率,比如看门狗以及 RTC 只需要几十 k 的时钟源即可。同一个电路,时钟越快功耗越大,同时抗电磁干扰能力也会越弱,所以对于较为复杂的 MCU 一般都是采取多时钟源的方法来解决这些问题。 详解STM32时钟系统 下图来自STM32CubeMX工具的时钟配置界面: 下图即是标准库默认8M外部时钟,系统时钟72M的配置情况 下图来自STM32F1的中文版datasheet的时钟系统章节: STM32的时钟系统确实是很复杂,不仅有倍频,分频,还有一系列的外设时钟开关。倍频是考虑到了电磁兼容性,如果外部直接提供一个72MHz的晶振,太高的震荡频率会给电路板的制作带来一定的难度。分频则是因为STM32既有高速外设,也有低速外设,各外设的工作频率不相同,需要分开来管理。最后,每个外设时钟还有自己独立的开关(在图上可以看到,在外设时钟之前需要经过一个与门,这就是它们的开关)在我们不使用该外设时,需要把时钟关闭以减少STM32的功耗。 STM32有几个时钟源 STM32有以下4个时钟源(标号对应上图中的蓝色数字标号): ①高速外部时钟(HSE):以外部晶振作时钟源,晶振频率可取范围为4~16MHz,我们一般采用8MHz的晶振。 ②低速外部时钟(LSE):以外部晶振作时钟源,主要提供给实时时钟模块,所以一般采用32.768KHz。 ③高速内部时钟(HSI): 由内部RC振荡器产生,频率为8MHz,但不稳定。 ④低速内部时钟(LSI):由内部RC振荡器产生,也主要提供给实时时钟模块,频率大约为40KHz。 有些资料把PLL也作为一个时钟源,事实上PLL 为锁相环倍频输出,也是由HSI或者HSE倍频得来的,其时钟输入源可选择为 HSI/2、HSE 或者 HSE/2。倍频可选择为2~16 倍,但是其输出频率最大不得超过 72MHz(仅针对STM32F103) 时钟在STM32内部最终是供给四大块,图中用红色椭圆圈出——USB的48MHz时钟、系统时钟SYSCLK、实时时钟模块RTC、独立看门狗的时钟IWDGCLK。其中最主要的,也是最大头是系统时钟SYSCLK,它可以是内部或外部高速时钟直接接过来,也可以内、外部高速时钟是PLL倍频后提供的,系统时钟再分别供给Cortex内核、SDIO、AHB总线、DMA、APB1、APB2等。 我们通常是采用外部8MHz高速时钟(HSE),所以着重说HSE。我们以前面的GPIO上的时钟为例,由ST的Datasheet可知,GPIO是在APB2高速外设总线上的,图中绿色的线就是时钟的流程,我们一步步地来看。 8MHz外部晶体(或晶振)输入后,先经过一个开关PLLXTPRE(HSE divider for PLL entry),此开关决定对HSE进行2分频再输入到PLL或直接到PLL。我们选择不分频。 这样时钟又到了第二个开关PLLSRC(PLL entry clock source),此开关决定PLL的时钟来源,是内部高速时钟二分频的时钟还是PLLXTPRE的输出。我们选择后者,这时的时钟在进入PLL前还是8MHz,因为在PLLXTPRE我们没有分频。 到了PLL倍频器,由PLLMUL决定倍频系统数,可以选择2~16倍频输出,但记住,PLL输出频率最高72MHz,所以我们选择9倍频,这样PLL输出就是最高72MHz的PLLCLK时钟了。这时的PLLCLK为USB提供时钟。 开关SW来决定SYSCLK的时钟来源,前面已经提到,这里我们由PLLCLK做为SYSCLK的来源,这样系统时钟SYSCLK就是72MHz了。 在供给外设前,先经过AHB预分频,我们选择不分频;在供给GPIO前,还要再经过APB2预分频,因为APB2为高速外设,所以我们选择不分频,这样GPIO的时钟就是72MHz了。注意,低速外设APB1最高频率为36MHz,所以在使用APB1的外设时,要注意设置好分频系统。还要注意,要使用外设,先要对外设时钟进行使能,见图中黄色云形框。这是因为STM32采用了低功耗的设计,对不使用的外设,其时钟不使能,以达到降低功耗的效果。 USB 的时钟USBCLK(图中红色椭圆标出)是来自 PLL 时钟源。 STM32 中有一个全速功能的 USB 模块,其串行接口引擎需要一个频率为 48MHz 的时钟源。该时钟源只能从 PLL 输出端获取,可以选择为 1.5 分频或者 1 分频,也就是,当需要使用 USB模块时,PLL 必须使能,并且时钟频率配置为 48MHz 或 72MHz。 RTC 时钟源RTCCLK(图中红色椭圆标出),从图上可以看出,RTC 的时钟源可以选择 LSI,LSE,以及HSE 的 128 分频。 独立看门狗时钟源只能由40KHz的LSI提供。 SYSCLK 通过 AHB 分频器分频后送给各模块使用。这些模块包括: ①AHB 总线、内核、内存和 DMA 使用的 HCLK 时钟。 ②通过 8 分频后送给 Cortex 的系统定时器时钟,也就是 systick 了。 ③直接送给 Cortex 的空闲运行时钟 FCLK。 ④送给 APB1 分频器。APB1 分频器输出一路供 APB1 外设使用(PCLK1,最大频率 36MHz),另一路送给定时器(Timer)2、3、4 倍频器使用。 ⑤送给 APB2 分频器。APB2 分频器分频输出一路供 APB2 外设使用(PCLK2,最大频率 72MHz),另一路送给定时器(Timer)1 倍频器使用。 其中需要理解的是 APB1 和 APB2 的区别,APB1 上面连接的是低速外设,包括电源接口、备份接口、CAN、USB、I2C1、I2C2、UART2、UART3 等等,APB2 上面连接的是高速外设包括 UART1、SPI1、Timer1、ADC1、ADC2、所有普通 IO 口(PA~PE)、第二功能 IO 口等。 关于时钟输出 MCO 是 microcontroller clock output 的缩写,是微控制器时钟输出引脚,在 STM32 F1系列中 由 PA8 复用所得,主要作用是可以对外提供时钟,相当于一个有源晶振。 如图左下角的咖啡色方框里面,MCO 的时钟来源可以是: PLLCLK/2、 HSI、 HSE、 SYSCLK,具体选哪个由时钟配置寄存器CFGR 的位 26-24: MCO[2:0]决定。 除了对外提供时钟这个作用之外, 我们还可以通过示波器监控 MCO 引脚的时钟输出来验证我们的系统时钟配置是否正确。 软件配置时钟 暂时只针对F1标准库 在stm32的启动文件startup_stm32f10x_hd.s中,会发现有这么一块用汇编写的代码。 Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT __main IMPORT SystemInit LDR R0, =SystemInit BLX R0 LDR R0, =__main BX R0 ENDP 从这里我们可以看到,我们的程序在进入到main函数之前,先要执行systeminit,跳转到这个函数的定义。里面的代码是对寄存器直接进行操作,原因是尽可能提高执行效率,以便尽快使MCU进入正常工作所需的时钟。 下面给出简化了的SystemInit 函数源码,假定预定义了STM32F10X_HD void SystemInit (void) { /* Reset the RCC clock configuration to the default reset state(for debug purpose) */ /* Set HSION bit */ RCC-》CR |= (uint32_t)0x00000001; /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */ RCC-》CFGR &= (uint32_t)0xF8FF0000; /* 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; /* Disable all interrupts and clear pending bits */ RCC-》CIR = 0x009F0000; /* 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) */ } 正常情况下,通常我们都是需要配置成72MHz运行 static void SetSysClockTo72(void) { __IO uint32_t StartUpCounter = 0, HSEStatus = 0; // ① 使能 HSE,并等待 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 ((RCC-》CR & RCC_CR_HSERDY) != RESET) { HSEStatus = (uint32_t)0x01; } else { HSEStatus = (uint32_t)0x00; } // HSE 启动成功,则继续往下处理 if (HSEStatus == (uint32_t)0x01) { //----------------------------------------------------------- // 使能 FLASH 预存取缓冲区 */ FLASH-》ACR |= FLASH_ACR_PRFTBE; // SYSCLK 周期与闪存访问时间的比例设置,这里统一设置成 2 // 设置成 2 的时候, SYSCLK 低于 48M 也可以工作,如果设置成 0 或者 1 的时候, // 如果配置的 SYSCLK 超出了范围的话,则会进入硬件错误,程序就死了 // 0: 0 《 SYSCLK 《= 24M // 1: 24《 SYSCLK 《= 48M // 2: 48《 SYSCLK 《= 72M */ FLASH-》ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY); FLASH-》ACR |= (uint32_t)FLASH_ACR_LATENCY_2; //------------------------------------------------------------ // ② 设置 AHB、 APB2、 APB1 预分频因子 // HCLK = SYSCLK RCC-》CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; //PCLK2 = HCLK RCC-》CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; //PCLK1 = HCLK/2 RCC-》CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; //③ 设置 PLL 时钟来源,设置 PLL 倍频因子, 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 稳定 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; // ⑦ 读取时钟切换状态位,确保 PLLCLK 被选为系统时钟 while ((RCC-》CFGR&(uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08){ } } else {// 如果 HSE 启动失败,用户可以在这里添加错误代码出来 } } SystemInit()函数运行完成后的状态:(注意以上程序默认是8M外部晶振的情况) SYSCLK(系统时钟)=72MHz AHB 总线时钟(使用 SYSCLK) =72MHz APB1 总线时钟(PCLK1) =36MHz APB2 总线时钟(PCLK2) =72MHz PLL 时钟 =72MHz 初始化之后可以通过变量SystemCoreClock获取系统变量。如果 SYSCLK=72MHz,那么变量SystemCoreClock=72000000。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1777 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1621 浏览 1 评论
1080 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
728 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1678 浏览 2 评论
1938浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
731浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
570浏览 3评论
595浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
556浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-23 13:16 , Processed in 0.978629 second(s), Total 78, Slave 61 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号