RCC详解_day7

RCC详解

今天回顾了直播课程后,自己去阅读芯片手册学习了stm32的RCC模块,针对学习的内容做一些梳理和总结。

1什么是时钟

时钟是单片机运行的基础,时钟信号推动单片机内各个部分执行相应的指令。时钟系统就是CPU的脉搏,决定cpu速率,像人的心跳一样 只有有了心跳,人才能做其他的事情,而单片机有了时钟,才能够运行执行指令,才能够做其他的处理 (点灯,串口,ADC),时钟的重要性不言而喻。

为什么 STM32 要有多个时钟源呢?

STM32本身十分复杂,外设非常多 但我们实际使用的时候只会用到有限的几个外设,使用任何外设都需要时钟才能启动,但并不是所有的外设都需要系统时钟那么高的频率,为了兼容不同速度的设备,有些高速,有些低速,如果都用高速时钟,势必造成浪费 并且,同一个电路,时钟越快功耗越快,同时抗电磁干扰能力也就越弱,所以较为复杂的MCU都是采用多时钟源的方法来解决这些问题。所以便有了STM32的时钟系统和时钟树

总结:
STM32时钟系统主要的目的就是给相对独立的外设模块提供时钟,也是为了降低整个芯片的耗能。
系统时钟,是处理器运行时间基准(每一条机器指令一个时钟周期)
时钟是单片机运行的基础,时钟信号推动单片机内各个部分执行相应的指令。
一个单片机内提供多个不同的系统时钟,可以适应更多的应用场合。
不同的功能模块会有不同的时钟上限,因此提供不同的时钟,也能在一个单片机内放置更多的功能模块。
对不同模块的时钟增加开启和关闭功能,可以降低单片机的功耗
STM32为了低功耗,他将所有的外设时钟都设置为disable(不使能),用到什么外设,只要打开对应外设的时钟就可以, 其他的没用到的可以还是disable(不使能),这样耗能就会减少。 这就是为什么不管你配置什么功能都需要先打开对应的时钟的原因。

在这里插入图片描述

系统时钟SYSCLK 的右边,则是系统时钟通过AHB预分频器,给相对应的外设设置相对应的时钟频率。从左到右可以简单理解为 各个时钟源—>系统时钟来源的设置—>各个外设时钟的设置

时钟系统
1各个时钟源 (左边的部分)
STM32 有4个独立时钟源:HSI、HSE、LSI、LSE。
①、HSI是高速内部时钟,RC振荡器,频率为8MHz,精度不高。
②、HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz。
③、LSI是低速内部时钟,RC振荡器,频率为40kHz,提供低功耗时钟。 
④、LSE是低速外部时钟,接频率为32.768kHz的石英晶体。

其中LSI是作为IWDGCLK(独立看门狗)时钟源和RTC时钟源 而独立使用

而HSI高速内部时钟 HSE高速外部时钟 PLL锁相环时钟 这三个经过分频或者倍频 作为系统时钟来使用。

PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。 通过倍频之后作为系统时钟的时钟源

举个例子:Keil编写程序是默认的时钟为72Mhz,其实是这么来的:外部晶振(HSE)提供的8MHz(与电路板上的晶振的相关)通过PLLXTPRE分频器后,进入PLLSRC选择开关,进而通过PLLMUL锁相环进行倍频(x9)后,为系统提供72MHz的系统时钟(SYSCLK)。之后是AHB预分频器对时钟信号进行分频,然后为低速外设提供时钟。

或者内部RC振荡器(HSI) 为8MHz /2 为4MHz 进入PLLSRC选择开关,通过PLLMUL锁相环进行倍频(x18)后 为72MHz

PS: 网上有很多人说是5个时钟源,这种说法有点问题,学习之后就会发现PLL并不是自己产生的时钟源,而是通过其他三个时钟源倍频得到的时钟

2系统时钟SYSCLK
系统时钟SYSCLK可来源于三个时钟源:
①、HSI振荡器时钟
②、HSE振荡器时钟
③、PLL时钟
最大为72Mhz
在这里插入图片描述

3USB时钟

STM32中有一个全速功能的USB模块,其串行接口引擎需要一个频率为48MHz的时钟源。该时钟源只能从PLL输出端获取(唯一的),,可以选择为1.5分频或者1分频,也就是,当需要使用USB模块时,PLL必须使能,并且时钟频率配置为48MHz或72MHz。

在这里插入图片描述

4把时钟信号输出到外部

STM32可以选择一个时钟信号输出到MCO脚(PA8)上,可以选择为PLL输出的2分频、HSI、HSE、或者系统时钟。可以把时钟信号输出供外部使用

5系统时钟通过AHB分频器给外设提供时钟(右边的部分) 重点

从左到右可以简单理解为 系统时钟—>AHB分频器—>各个外设分频倍频器 —> 外设时钟的设置
在这里插入图片描述

右边部分为:系统时钟SYSCLK通过AHB分频器分频后送给各模块使用,AHB分频器可选择1、2、4、8、16、64、128、256、512分频。其中AHB分频器输出的时钟送给5大模块使用:

①内核总线:送给AHB总线、内核、内存和DMA使用的HCLK时钟。

②Tick定时器:通过8分频后送给Cortex的系统定时器时钟。

③I2S总线:直接送给Cortex的空闲运行时钟FCLK。

④APB1外设:送给APB1分频器。APB1分频器可选择1、2、4、8、16分频,其输出一路供APB1外设使用(PCLK1,最大频率36MHz),另一路送给通用定时器使用。该倍频器可选择1或者2倍频,时钟输出供定时器2-7使用。

⑤APB2外设:送给APB2分频器。APB2分频器可选择1、2、4、8、16分频,其输出一路供APB2外设使用(PCLK2,最大频率72MHz),另一路送给高级定时器。该倍频器可选择1或者2倍频,时钟输出供定时器1和定时器8使用。

、8、16分频,其输出一路供APB2外设使用(PCLK2,最大频率72MHz),另一路送给高级定时器。该倍频器可选择1或者2倍频,时钟输出供定时器1和定时器8使用。

在这里插入图片描述

#include "key.h" int main(void) { // HAL ????(??????) HAL_Init(); // ??????(?? CubeMX ????,??????) SystemClock_Config(); // ??? GPIO(????????????) GPIO_Init(); while (1) { // ????????(K1-K4 ?????,?????) if (Get_Key_State() == 1) { Light_State light = Get_Light_State(); // ??????????? if (light == DAY) { Beep_Trigger(500); // ??:?? 500ms } else { Beep_Trigger(1500); // ??:?? 1500ms } // ?? + ??????,?????? while (Get_Key_State() == 1) { HAL_Delay(10); } } // ?????,?? CPU ??(???,??????) HAL_Delay(50); } } // ??? GPIO(???????????????) void GPIO_Init(void) { // ?? GPIOA ??(??,?? GPIO ????) __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct = {0}; // ???:?????? GPIO_InitStruct.Pin = BEEP_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; // ?????????? GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(BEEP_PORT, &GPIO_InitStruct); // ????? DO:????(??????/???? Pull) GPIO_InitStruct.Pin = LIGHT_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; // ??????????,?? GPIO_PULLUP/GPIO_PULLDOWN HAL_GPIO_Init(LIGHT_PORT, &GPIO_InitStruct); // ?? K1-K4:????(?????? GPIO_PULLUP,??? GPIO_PULLDOWN) GPIO_InitStruct.Pin = KEY1_PIN | KEY2_PIN | KEY3_PIN | KEY4_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; // ???????,??????/?? HAL_GPIO_Init(KEY_PORT, &GPIO_InitStruct); } // ??????(?? DO ????) Light_State Get_Light_State(void) { return (Light_State)HAL_GPIO_ReadPin(LIGHT_PORT, LIGHT_PIN); } // ?????(duration: ???? ms) void Beep_Trigger(uint32_t duration) { HAL_GPIO_WritePin(BEEP_PORT, BEEP_PIN, GPIO_PIN_SET); // ???? HAL_Delay(duration); // ???? HAL_GPIO_WritePin(BEEP_PORT, BEEP_PIN, GPIO_PIN_RESET); // ???? } // ??????(???????? 1,?? 0) uint8_t Get_Key_State(void) { // ???????(? GPIO_PULLUP ??,???????) return (HAL_GPIO_ReadPin(KEY_PORT, KEY1_PIN) == GPIO_PIN_RESET) || (HAL_GPIO_ReadPin(KEY_PORT, KEY2_PIN) == GPIO_PIN_RESET) || (HAL_GPIO_ReadPin(KEY_PORT, KEY3_PIN) == GPIO_PIN_RESET) || (HAL_GPIO_ReadPin(KEY_PORT, KEY4_PIN) == GPIO_PIN_RESET); } // ??????(STM32F103C8T6 ????,????????) void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; // ?? HSE(??????)+ PLL(???) RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; // ??????(? 8MHz) RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; // 8MHz * 9 = 72MHz(F103 ???? 72MHz) if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); // ??????,?????? } // ???????AHB?APB ?? RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // ?????? PLL RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB = 72MHz RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; // APB1 = 36MHz(F1 ???? 36MHz) RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; // APB2 = 72MHz if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); // ??????? } } // ????(??? + ??? LED ??,??????) void Error_Handler(void) { while (1) { // ???? LED,??????????“????” // HAL_GPIO_TogglePin(LED_PORT, LED_PIN); // HAL_Delay(500); } } // ??????(??,????) #ifdef USE_FULL_ASSERT void assert_failed(uint8_t *file, uint32_t line) { while (1) { } } #endif
07-11
### STM32系统时钟配置:使用HSE和PLL 在STM32系列微控制器中,系统时钟的配置通常涉及两个主要函数:`HAL_RCC_OscConfig()` 和 `HAL_RCC_ClockConfig()`。这两个函数分别用于初始化振荡器(如HSE、HSI、PLL等)以及配置系统时钟源(SYSCLK)和总线时钟(HCLK、PCLK1、PCLK2等)。以下是一个完整的示例,展示如何使用外部高速晶振(HSE)作为输入并启用锁相环(PLL)来提升系统频率。 --- ### 配置HSE和PLL的步骤 #### 1. 初始化HSE和PLL 使用 `HAL_RCC_OscConfig()` 函数可以配置振荡器类型和参数。该函数接受一个 `RCC_OscInitTypeDef` 类型的结构体指针,其中包含振荡器类型、频率调整等信息。 ```c RCC_OscInitTypeDef RCC_OscInitStruct = {0}; // 启用HSE,并选择HSE作为PLL的输入源 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 2; // 分频系数M RCC_OscInitStruct.PLL.PLLN = 20; // 倍频系数N RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7; // 主PLL分频系数P RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; // PLL48M1CLK分频系数Q RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; // 主PLL除法因子R if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); // 如果返回错误,则进入错误处理 } ``` 该段代码中,`RCC_OscInitStruct` 结构体被配置为启用HSE,并将其作为PLL的输入源,同时启用PLL并设置其分频/倍频参数。如果调用失败,程序将跳转到 `Error_Handler()` 处理函数[^2]。 --- #### 2. 配置系统时钟源为PLL 接下来使用 `HAL_RCC_ClockConfig()` 设置系统主时钟(SYSCLK)为PLL输出,并配置AHB、APB1和APB2的时钟分频。 ```c RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; // 设置系统时钟源为PLL,并配置总线时钟 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) { Error_Handler(); // 如果配置失败,进入错误处理 } ``` 此段代码将系统时钟源切换为PLL输出,并根据CPU频率需求设置AHB、APB1和APB2的分频值。此外,`FLASH_LATENCY_4` 表示Flash访问等待周期,适用于高频率运行场景[^1]。 --- ### 注意事项与常见问题 - **确保HSE稳定**:在调用 `HAL_RCC_OscConfig()` 前,应确认HSE已经正确连接且外部电路正常工作。若HSE无法启动,可能导致函数卡死或返回错误[^4]。 - **PLL参数计算**:PLL的M、N、P等参数需根据系统所需频率进行精确计算。例如,使用HSE=8MHz,通过M=2、N=20得到VCO频率为80MHz,再经过P=7得到系统时钟约为11.43MHz,但实际应用中可能需要更高频率,因此应合理调整参数。 - **时钟切换顺序**:在更改系统时钟源前,应先确保目标时钟源已就绪。否则可能导致系统运行不稳定甚至死机[^1]。 --- ### 示例代码整合 以下是完整初始化流程: ```c void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; // 配置HSE和PLL RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 2; RCC_OscInitStruct.PLL.PLLN = 20; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7; RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } // 配置系统时钟源为PLL RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) { Error_Handler(); } } ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值