STM32 时钟系统
STM32 时钟树
STM32 系统复位后首先进入 SystemInit 函数进行时钟的设置,将 STM32F1 系统时钟设 置为 72MHz(我们开发板上使用的 STM32F103ZET6 最大可达到 72M(超频除外)), 然后进入主函数。那么这个系统时钟大小如何得来,其他外设的时钟又如何划分, 这些问题都可以通过一张时钟树图找到答案,只要理解好时钟树,STM32 一切时 钟的来龙去脉就会非常清楚。
在 STM32 时钟系统中,有 5 个重要的时钟源,分别是 LSI、LSE、HSI、HSE、
PLL。按照时钟频率分可分为高速时钟源和低速时钟源,在这 5 个中 HSI,HSE 以 及 PLL 属于高速时钟,LSI 和 LSE 属于低速时钟。按照时钟来源可分为外部时 钟源和内部时钟源,外部时钟源就是在 STM32 晶振管脚处接入外部晶振的方式获 取时钟源,其中 HSE 和 LSE 是外部时钟源,其他的是内部时钟源。下面我们就按 照上图中数字顺序来介绍。
(1)图标 1 HSI 是内部高速时钟,RC 振荡器,频率为 8MHz。可作为系统时钟或 PLL 锁相环的输入。
(2)图标 2 HSE 是外部高速时钟,芯片的 23 和 24 引脚即为外部高速晶振管脚。可通过外接一个频率范围是 4-16MHz 的时钟或者晶振,我们开发板上接的 是一个 8MHz 的外部晶振。HSE 可以作为系统时钟和 PLL 锁相环输入,还可以经 过 128 分频后输入给 RTC。
(3)图标 3 LSI 是内部低速时钟,RC 振荡器,频率大约为 40K,可供独立看门狗和 RTC 使用,并且独立看门狗只能使用 LSI 时钟。
(4)图标 4 LSE 是外部低速时钟,我们开发板上 STM32 芯片的 PC14 和 PC15即为外部低速时钟管脚。通常在此管脚上外接一个 32.768KHz 的晶振,供 RTC 使用。我们开发板上已经外接了一个 32.768K 的晶振。
(5)图标 5 PLL 是锁相环,用于倍频输出,因为开发板外部高速晶振也只有 8M,而我们这块芯片的最大时钟频率是 72M,因此可通过 PLL 锁相环来倍频。从图标 5 中可以看到,PLL 时钟输入源可选择为 HSI/2、HSE 或者 HSE/2,时钟源经过 2-16 倍频后输入给 PLLCLK,如果系统时钟选择由 PLLCLK 提供,则 PLLCLK 最大值不 要超过 72M。 那么它是怎么倍频产生72MHz系统时钟的呢?我们看到在主PLL内有倍频器 和分频器,如图 11.1.2 所示。
从图11.1.2可以看出,PLL时钟源的输入信号要先经过一个PLLMUL倍频器,
将 HSE 或 HSI 倍频(2-16)后输入给 PLLCLK,如果系统时钟源 SYSCLK 选择 PLLCLK 作为它的来源,则最大值不能超过 72M。虽然可以做超频处理,但会打破系统的 稳定性,这个是不划算的。假如 PLLSRC 的时钟来源由 HSE 提供,我们开发板使 用的 HSE 是 8M 晶振,经过 PLLMUL 9 倍频后可以输出 72M 时钟频率给 PLLCLK。 总结:如果我们选择 HSE 是 PLL 的时钟源,PLL 是 SYSCLK 的时钟源,即 SYSCLK 为 72MHz,这个也是我们库函数模板中 SystemInit 所配置的最终系统时钟。
上面我们简单介绍了下 STM32 的 5 个时钟源,那么它们是怎么给其他外设和
系统提供时钟的呢?在上图 11.1.1 时钟树图中我们把常用的时钟用字母框起 来,按照它们顺序依次介绍。 (A)MCO 是 STM32 的一个时钟输出 IO(PA8),它可以选择一个时钟信号输
出,可以选择为 PLL 输出的 2 分频、 HSI、 HSE 或者系统时钟。这个时钟可 以用来给外部其他系统提供时钟源。 (B)RTC 时钟。从图中线的流向可知,RTC 时钟来源可以是内部低速的 LSI
时钟,外部低速 LSE 时钟(32.768K),还可以通过 HSE 128 分频后得到。 (C)USB 时钟。STM32 中有一个全速功能的 USB 模块,其串行接口引擎需
要一个频率为 48MHz 的时钟源,该时钟源只能从 PLL 输出端获取,可以选择为 1.5 分频或者 1 分频,也就是当需要使用 USB 模块时,PLL 必须使能,并且 PLLCLK 时钟频率配置为 48MHz 或 72MHz。 (D)SYSCLK 系统时钟。它是 STM32 中绝大部分部件工作的时钟源。它的
时钟来源可以由 HSI、HSE、PLLCLK 提供,相信大家选择 STM32F1 这种高级芯片, 都希望有一个比较大的时钟频率,因此选择 PLLCLK 作为系统时钟。PLLCLK 又是从 HSE 或 HSI 经过 PLL 倍频得到。根据前面 PLL 计算关系大家就可以算出系统时 钟是多少。 (E)其他所有外设。从时钟图上可以看出,其他所有外设的时钟最终来源
都是 SYSCLK。SYSCLK 通过 AHB 分频器分频后送给各模块使用。这些模块包括: ①、 AHB 总线、内核、内存和 DMA 使用的 HCLK 时钟。 ②、通过 8 分频后送给 Cortex 系统定时器时钟,即 SysTick。 ③、直接送给 Cortex 的空闲运行时钟 FCLK。 ④、送给 APB1 分频器。 APB1 分频器输出一路供 APB1 外设使用(PCLK1,
最大频率 36MHz),另一路送给定时器(Timer)1、2 倍频使用。 ⑤、送给 APB2 分频器。 APB2 分频器分频输出一路供 APB2 外设使用
(PCLK2,最大频率 72MHz),另一路送给定时器(Timer)1 倍频器使用。 ⑥、送给 ADC 分频器。ADC 分频器经过 2、4、6、8 分频后送给 ADC1/2/3 使
用,ADC 最大频率为 14M。 ⑦、二分频后送给 SDIO 使用。
其中需要理解的是 APB1 和 APB2 的区别,APB1 上面连接的是低速外设,
包括电源接口、备份接口、CAN、USB、I2C1、I2C2、UART2、UART3 等,APB2 上面连接的是高速外设包括 UART1、 SPI1、 Timer1、 ADC1、 ADC2、GPIO 等。 大家可以简单这样记忆:2>1,所以 APB2 的速度大于 APB1 的速度。 在时钟树图中我们还可以得到一个重要信息,大多数有关时钟输出部分都有
一个使能控制,比如 AHB 总线、APB1 外设、APB2 外设、内核时钟等。当需要使 用某个时钟的时候一定要开启它的使能,否则将不工作。
时钟配置函数
时钟初始化配置函数
在前面章节的介绍中,我们知道 STM32 系统复位后首先进入 SystemInit 函
数进行时钟的设置,然后进入主函数 main。那么我们就来看下 SystemInit()函 121普中 STM32F1xx 开发攻略 www.prechin.cn
数到底做了哪些操作,首先打开我们前面使用库函数编写的 LED 程序,在 system_stm32f10x.c 文件中可以找到 SystemInit()函数,如果不想找的可以直 接打开其头文件,通过前面教大家的快速进入函数的方法进入到 SystemInit() 内。
SystemInit 函数开始通过条件编译,先复位 RCC 寄存器,同时通过设置 CR
寄存器的 HSI 时钟使能位来打开 HSI 时钟。默认情况下如果 CR 寄存器复位, 是选择 HSI 作为系统时钟,这点大家可以查看 RCC->CR 寄存器相关位描述可以 得知,当低两位配置为 00 的时候(复位之后),会选择 HSI 振荡器为系统时 钟。也就是说,调用 SystemInit 函数之后,首先是选择 HSI 作为系统时钟。 在设置完相关寄存器后才换成 HSE 作为系统时钟,接下来 SystemInit 函数内部 会调用 SetSysClock()函数。这个函数内部是根据宏定义设置系统时钟频率。
SystemInit 内实现过程 看不懂没有关系,大家只要知道 SystemInit 函数执行完,时钟大小设置如下: SYSCLK(系统时钟) =72MHz AHB 总线时钟(HCLK=SYSCLK) =72MHz APB1 总线时钟(PCLK1=SYSCLK/2) =36MHz APB2 总线时钟(PCLK2=SYSCLK/1) =72MHz PLL 主时钟 =72MHz 这些时钟值大家要记住。
RCC_APB2Periph_GPIOC 宏,第二个传递的参数是 ENABLE 使 能。从第一个参数名来看也非常好理解,RCC 表示复位和时钟控制器,APB2 表示 GPIOC 是挂接在 APB2 总线上,Periph 表示外设,后面的 GPIOC 表示我们使能的 是 GPIOC 端口。第二个参数 ENABLE 表示使能。
自定义系统时钟
1 void RCC_HSE_Config(u32 div,u32 pllm) //自定义系统时间(可以修改时钟)
2 {
3 RCC_DeInit(); //将外设RCC寄存器重设为缺省值
4 RCC_HSEConfig(RCC_HSE_ON);//设置外部高速晶振(HSE)
5 if(RCC_WaitForHSEStartUp()==SUCCESS) //等待HSE起振
6 {
7 RCC_HCLKConfig(RCC_SYSCLK_Div1);//设置AHB时钟(HCLK)
8 RCC_PCLK1Config(RCC_HCLK_Div2);//设置低速AHB时钟(PCLK1)
9 RCC_PCLK2Config(RCC_HCLK_Div1);//设置高速AHB时钟(PCLK2)
10 RCC_PLLConfig(div,pllm);//设置PLL时钟源及倍频系数
11 RCC_PLLCmd(ENABLE); //使能或者失能PLL
12 while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET);//检查指定的RCC标志位设置与否,PLL就绪
13 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);//设置系统时钟(SYSCLK)
14 while(RCC_GetSYSCLKSource()!=0x08);//返回用作系统时钟的时钟源,0x08:PLL作为系统时钟
15 }
16 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗