- 2.1、系统时钟配置步骤
- 2.2、外设时钟使能和失能
- [2.3、sys_stm32_clock_init 函数(F1)](#2.3、sys_stm32_clock_init 函数(F1))
- HAL_RCC_OscConfig()函数(F1)
- HAL_RCC_ClockConfig函数(F1)
- [2.4、sys_stm32_clock_init 函数(F4/F7)](#2.4、sys_stm32_clock_init 函数(F4/F7))
- HAL_RCC_OscConfig()函数(F4/F7)
- HAL_RCC_ClockConfig()函数(F4/F7)
- [2.5、sys_stm32_clock_init 函数(H7)](#2.5、sys_stm32_clock_init 函数(H7))
- HAL_RCC_OscConfig()函数(H7)
- HAL_RCC_ClockConfig()函数(H7)
- HAL_RCCEx_PeriphCLKConfig()函数(H7)
一、认识时钟树
1.1、什么是时钟?
时钟(Clock)在数字电路和计算机系统中是一种周期性的信号,用于同步和协调系统中各个部分的操作。时钟信号在单片机中是非常重要的,它指导着各个组件的工作,确保它们在正确的时间执行相应的任务。时钟信号通常是一个方波信号,具有固定的周期和占空比。
时钟树(Clock Tree)是指在数字系统或芯片中,将一个主时钟信号分配和扩展到整个系统中各个模块和部分的一种层次结构。时钟树的目的是确保系统中所有的时钟域都能够按照规定的时序要求同步运行。时钟树通常包括以下关键元素:
-
主时钟源(Clock Source): 时钟树的起点通常是一个主时钟源,这可以是外部晶振、振荡器或其他时钟源。
-
时钟分频器(Clock Divider): 时钟分频器用于将主时钟信号分频,以产生其他模块或部分所需的时钟频率。这样可以根据需要调整时钟频率,使得不同的部分能够以不同的速度运行。
-
时钟缓冲器(Clock Buffer): 时钟缓冲器用于放大和驱动时钟信号,确保时钟信号能够准确地传播到系统中的各个部分。
-
时钟分配网络(Clock Distribution Network): 时钟分配网络将时钟信号传送到系统中的各个模块。这通常包括时钟线路、时钟树分支等。
时钟树的设计需要考虑时序要求、功耗、噪声和电磁兼容性等因素。精心设计的时钟树能够确保系统各个部分协同工作,提高系统的稳定性和性能。在数字集成电路中,时钟树的设计是一个复杂而关键的任务。
1.2、认识时钟树(F1)
H、L、S、I、E代表了一些常见的缩写,通常与单片机或者数字电路中的时钟、速度和内外部资源有关。
含义:
-
H:high
- 通常表示高电平或高状态。在数字电路中,可能指的是高电压或逻辑高。
-
L:low
- 通常表示低电平或低状态。在数字电路中,可能指的是低电压或逻辑低。
-
S:speed
- 可能表示速度。在数字电路或通信领域中,S通常用于表示速度、传输速率或时钟频率。
-
I:internal
- 可能表示内部。在数字电路中,I可能用于表示内部时钟源、内部资源等。
-
E:external
- 可能表示外部。在数字电路中,E可能用于表示外部时钟源、外部资源等。
分频器(Divider)和倍频系数(Multiplier)
分频器和倍频系数是在时钟系统中常见的概念,用于调整时钟信号的频率。让我们更详细地了解这两个概念:
-
分频器(Divider):
- 定义: 分频器用于将输入时钟信号分频,即减小其频率。
- 作用: 分频器通常用于将高频时钟信号分频为较低的频率,以供给某些模块或外设使用。
- 公式: 分频后的频率可以通过原始频率除以分频系数得到,即新频率 = 原始频率 / 分频系数。
-
倍频系数(Multiplier):
- 定义: 倍频系数用于将输入时钟信号倍频,即增加其频率。
- 作用: 倍频系数通常用于提高时钟信号的频率,以满足系统对更高时钟频率的需求。
- 公式: 倍频后的频率可以通过原始频率乘以倍频系数得到,即新频率 = 原始频率 * 倍频系数。
在实际应用中,分频器和倍频系数的配置对于调整系统时钟至关重要,特别是在使用锁相环(PLL)等技术进行时钟调整时。在微控制器或数字系统中,通过配置分频器和倍频系数,可以灵活地适应不同的硬件要求,以满足外设的时钟需求,同时确保系统时钟的稳定性和准确性。
总的来说,分频器和倍频系数是时钟系统中的两个关键元素,通过它们的配置,可以实现对时钟频率的灵活调整,以满足不同应用场景的需求。
锁相环(Phase-Locked Loop,PLL)
锁相环(Phase-Locked Loop,PLL)是一种控制系统,用于调整输出信号的频率和相位,使其与输入信号保持特定的关系。在PLL中,常用的表达式是F/M*N/P,其中:
-
F:
- 表示输出信号的频率。
-
M:
- 表示输入信号的参考倍频系数(Multiplier)。
-
N:
- 表示反馈倍频系数。
-
P:
- 表示分频系数(Divider)。
这个表达式的含义是,通过调整M、N和P的值,可以控制输出频率F。下面是更详细的解释:
-
输入信号:
- 输入信号经过倍频(M倍)得到一个高频信号。
-
反馈:
- 从输出中采样一部分反馈回输入,以与输入信号同步。
-
输出频率:
- 输出频率是输入信号经过M倍频、N倍频、再通过P分频得到的。
具体来说,输出频率F可以通过以下公式计算:
F = F 输入 × M × N P F = \dfrac{F_{\text{输入}} \times M \times N}{P} F=PF输入×M×N
在实际应用中,通过调整M、N和P的值,可以实现对输出频率的精确控制。这种调整通常用于微控制器、射频电路、时钟产生器等应用中,以满足系统对不同频率的需求。
分频器是一种电路或模块,用于将输入的时钟信号分频,即减小其频率。这是在数字电路中常见的一种功能,它允许在系统中使用不同频率的时钟信号,以适应不同的模块和外设的工作需求。分频器通常通过使用可编程的分频系数,将输入时钟信号的频率降低到所需的水平。
以下是分频器的一些关键概念:
-
分频系数(Divider Ratio):
- 分频系数是用于调整输入时钟频率的比例。它是分频器的一个参数,用于控制输出频率。
- 分频系数 N N N 决定了输入频率与输出频率之间的关系,输出频率等于输入频率除以 N N N。
- 公式: 输出频率 = 输入频率 N \text{输出频率} = \frac{\text{输入频率}}{N} 输出频率=N输入频率
-
整数分频和分数分频:
- 整数分频是指分频系数 N N N 为整数,例如 2、3、4 等。
- 分数分频是指分频系数 N N N 可以是分数,例如 1 / 2 1/2 1/2、 1 / 3 1/3 1/3 等。在某些应用中,分数分频可以提供更灵活的频率调整。
-
分频器的应用:
- 分频器常用于微控制器、数字信号处理器(DSP)、时钟生成器、通信系统等领域。
- 在微控制器中,分频器用于为外设提供适当的时钟频率。
- 在通信系统中,分频器可用于调整射频信号的频率。
-
分频器类型:
- 固定分频器:分频系数是固定的,通常在设计中确定。
- 可编程分频器:分频系数可以通过软件或硬件配置进行调整,提供更大的灵活性。
总体而言,分频器是数字电路中的重要组件,允许系统中的不同部分以不同的时钟频率运行,以满足不同模块的工作要求。在时钟系统设计中,分频器起到了关键的作用,有助于优化电路的性能和功耗。
时钟安全系统(CSS)和自由运行时钟(FCLK)
这两个概念涉及到STM32微控制器的时钟管理和安全特性。让我们更详细地了解它们:
-
时钟安全系统(Clock Security System,CSS):
- 功能: CSS是STM32微控制器上的一项安全特性,旨在监测外部高速时钟(HSE)的稳定性。
- 作用: 如果HSE启动失败(例如,外部晶振故障),CSS会自动切换到内部高速时钟(HSI),并触发非可屏蔽中断(NMI)。
- 应用: 这个特性可以用于提高系统的稳定性,防止由于外部时钟问题导致系统工作不正常。
-
自由运行时钟(Free Running Clock,FCLK):
- 功能: FCLK是用于采样中断和调试模块计时的时钟。
- 作用: 在特定的应用场景中,即使系统进入休眠模式,FCLK仍然保持运行,以确保一些必要的计时和中断采样。
- 应用: 主要用于允许在休眠状态下执行一些必要的计时或调试操作,以保持对系统状态的监控。
这两个特性都是STM32系列微控制器提供的一些高级时钟管理和安全功能,有助于提高系统的可靠性和稳定性。在使用这些特性时,建议仔细查阅相关的STM32芯片手册和参考资料,以确保正确配置和使用。
STM32F103时钟树简图
理清这些概念的时序关系和逻辑,我们可以按照以下步骤来组织:
-
高速时钟源OSC(HSE、HSI):
- HSE(High-Speed External)和 HSI(High-Speed Internal)是两种不同的时钟源。
- HSE是外部高速晶振,HSI是芯片内部的高速时钟源。
-
锁相环PLL:
- 锁相环(PLL)用于调整时钟频率,将其倍频到系统所需的频率。
- 使用
HAL_RCC_OscConfig()
配置时钟源,选择 HSE 或 HSI 以及其他相关配置。
-
分频系数:
- 配置PLL的分频系数,以调整输出时钟的频率。
- 这可能包括主时钟(SYSCLK)、AHB时钟、APB1时钟、APB2时钟等。
- 使用
HAL_RCC_ClockConfig()
进行系统时钟、总线和外设时钟的配置。
-
外设时钟使能:
- 使用
__HAL_RCC_PPP_CLK_ENABLE()
使能外设的时钟。 - 在这里,PPP表示具体的外设,比如UART、SPI、I2C等。
- 使用
-
扩展外设时钟(RTC/ADC/USB):
- 使用
HAL_RCCEx_PeriphCLKConfig()
扩展外设的时钟。 - 这通常包括RTC、ADC、USB等外设。
- 使用
-
内核时钟:
- 内核时钟是供给CPU内核的时钟。
- 它通常与系统时钟相同,但可能有差异。
- 在配置时钟时需要确保内核时钟的要求得到满足。
-
具体时钟频率设置:
- 在上述配置完成后,系统时钟 SYSCLK、AHB总线时钟 HCLK、APB1总线时钟和 APB2总线时钟会根据你的配置和分频系数产生相应频率。
- 根据具体的需求,设置最大的 APB1 和 APB2 时钟频率(36M 和 72M)。
-
低速时钟源OSC(LSI、LSE):
- LSI(Low-Speed Internal)和 LSE(Low-Speed External)是低速时钟源。
- 通常用于RTC等外设,LSE的频率是32.768KHz,LSI的频率是40KHz。
-
IWDG、RTC:
- IWDG(Independent Watchdog)和 RTC(Real-Time Clock)是一些特殊的定时器或时钟相关的外设。
- 它们可能需要特殊的时钟配置或者需要特别的时钟源。
总体而言,上述步骤涵盖了从时钟源的选择、通过PLL调整时钟频率、配置各个时钟分频系数,到最终配置系统总线和外设时钟的过程。在具体的应用中,需要根据芯片手册和硬件设计的要求进行调整。
STM32F103时钟树
这些概念,包括它们之间的关系:
-
PLLXTPRE:
- PLLXTPRE用于配置PLL输入时钟源的预分频因子。
- 通常,可以选择不预分频(PLLXTPRE_DIV1)或者2分频(PLLXTPRE_DIV2)。
-
PLLSRC:
- PLLSRC用于选择PLL的输入时钟源,通常可以选择HSE(外部高速晶振)或HSI(内部高速时钟源)。
-
PLLMUL:
- PLLMUL用于配置PLL的倍频因子,即将输入时钟的频率乘以多少倍。
- 具体的倍频因子可以是2、3、4、...、16。
-
PLLCLK:
- PLLCLK是指通过PLL倍频后得到的时钟。
-
SYSCLK:
- SYSCLK是系统的主时钟,通常由PLLCLK提供。
- 通过
HAL_RCC_ClockConfig()
配置PLL、分频因子等,以设置SYSCLK的频率。
-
HCLK:
- HCLK是AHB总线的时钟,通常与SYSCLK相同,或者是SYSCLK的一部分。
- 通过
HAL_RCC_ClockConfig()
设置HCLK的频率。
-
Cortex系统时钟:
- 这是Cortex-M处理器内部的时钟,通常等于HCLK。
-
滴答定时器:
- 滴答定时器通常是一个用于生成定时中断的内部计时器。
- 它的时钟源可以是SYSCLK或HCLK,通过
HAL_InitTick()
等函数进行配置。
-
FCLK:
- FCLK通常是指Flash存储器的时钟,其频率与HCLK相同。
-
TIMXCLK:
- TIMXCLK是指定时器(TIM)的时钟源,可以是内部时钟源或者外部时钟源。
- 如果APB1预分频系数等于1,则TIMXCLK的频率不变,否则频率会×2。
-
PCLK:
- PCLK是指外设总线的时钟,分别对应APB1和APB2总线。
- 通过
HAL_RCC_ClockConfig()
配置APB1和APB2的分频因子。
-
I2SCLK:
- I2SCLK是I2S接口的时钟。
-
USBCLK:
- USBCLK是USB接口的时钟。
-
RTCCLK:
- RTCCLK是实时时钟(RTC)的时钟源。
- 通常可以选择LSE(低速外部时钟)或LSI(低速内部时钟)。
-
RC-IWDGCLK:
- RC-IWDGCLK是独立看门狗(IWDG)的时钟源,可以选择LSI。
-
MCO:
- MCO是主时钟输出,通过配置可以将SYSCLK、HSI、HSE等输出到MCO引脚,用于外部设备的时钟监测或其他应用。
这些概念的详细理解有助于在STM32CubeMX或其他开发环境中正确配置时钟和时序。请根据具体的硬件和应用需求,在配置时钟时参考芯片手册和相关文档。
STM32CubeMX时钟树(F103)
1.3、认识时钟树(F4)
STM32F407时钟树
STM32F429时钟树
STM32F4时钟树简图
STM32CubeMX时钟树(F407)
STM32CubeMX时钟树(F429)
为了让 FSUSB 满足 48MHz 把 Main PLL 的倍频系数配置成X384 而不是X360这样系统时钟就超频了
首先,为了让 FSUSB 满足 48MHz,我们需要将 Main PLL 的倍频系数配置为 X384。以下是一个示例代码,以确保系统时钟的配置满足 USB 的需求。
在这里,我将使用 HAL 库的一些函数作为例子,实际的代码可能会因为使用的具体库和芯片型号而有所不同。以下是一个基本的流程:
c
#include "stm32f4xx_hal.h"
void SystemClock_Config(void);
void Error_Handler(void);
int main(void)
{
HAL_Init();
// 配置系统时钟
SystemClock_Config();
// 初始化USB外设(这里使用USB FS作为示例)
HAL_PCD_MspInit(&hpcd_USB_FS);
HAL_PCD_Init(&hpcd_USB_FS);
// 进行其他初始化和应用程序代码
while (1)
{
// 主循环
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
// 配置 HSE 为 8MHz 外部晶振
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 8; // 分频系数,原始频率 / 8 = 1MHz
RCC_OscInitStruct.PLL.PLLN = 384; // 倍频系数,1MHz * 384 = 384MHz
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // 384MHz / 2 = 192MHz (SYSCLK)
RCC_OscInitStruct.PLL.PLLQ = 8; // 分频系数,384MHz / 8 = 48MHz (USB频率)
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
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_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK)
{
Error_Handler();
}
// 更新系统时钟配置
SystemCoreClockUpdate();
}
void Error_Handler(void)
{
while (1)
{
// 处理错误
}
}
这个代码片段中,关键的部分是 RCC_OscInitStruct
和 RCC_ClkInitStruct
结构体的配置,特别是 PLL.PLLN
的值设置为 384,以满足 48MHz 的 USB 频率要求。这个例程假设使用的是 STM32Cube HAL 库,并在 main()
函数中调用了 HAL_Init()
。实际上,具体的配置可能需要根据你的硬件设计和使用的芯片型号进行调整。在编写代码时,请参考相关的芯片手册和硬件设计文档。
1.4、认识时钟树(F7)
STM32F7时钟树简图
STM32CubeMX时钟树(F767/F750)
1.5、认识时钟树(H7)
STM32H7时钟树简图
STM32H7时钟树-低频部分简图
### STM32CubeMX时钟树(H7)
二、配置系统时钟
2.1、系统时钟配置步骤
系统时钟配置步骤,下面是对每个步骤进行一些详细解释:
-
配置HSE_VALUE:
- 在
stm32xxxx_hal_conf.h
文件中,你需要设置HSE_VALUE
宏定义,用于告诉HAL库外部晶振的频率。这是时钟系统的基础,确保HAL库能正确配置时钟。
- 在
-
调用SystemInit()函数(可选):
SystemInit()
函数通常包含在启动文件中,用于执行一些基本的系统初始化。在这里,它可以执行一些硬件的初始化,例如设置向量表和启用FPU等。这一步通常是由编译器自动调用的,你只需确保启动文件正确。
-
选择时钟源,配置PLL:
-
使用
HAL_RCC_OscConfig()
函数配置时钟源和PLL。 -
通过填写
RCC_OscInitTypeDef
结构体,可以指定HSE或HSI作为时钟源,并配置PLL的倍频系数和分频系数。 -
示例如下:
cRCC_OscInitTypeDef RCC_OscInitStruct; 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 = 8; // 时钟源分频 RCC_OscInitStruct.PLL.PLLN = 336; // PLL倍频 RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // PLL分频 RCC_OscInitStruct.PLL.PLLQ = 7; // USB OTG FS, SDIO and RNG分频 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); }
-
-
选择系统时钟源,配置总线分频器:
-
使用
HAL_RCC_ClockConfig()
函数配置系统时钟和总线分频器。 -
通过填写
RCC_ClkInitTypeDef
结构体,可以选择PLL为系统时钟源,并配置AHB、APB1、APB2等分频系数。 -
示例如下:
cRCC_ClkInitTypeDef RCC_ClkInitStruct; 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_DIV4; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) { Error_Handler(); }
-
-
配置扩展外设时钟(可选):
-
使用
HAL_RCCEx_PeriphCLKConfig()
函数配置扩展外设时钟,如RTC、ADC、USB等。 -
示例如下:
cRCC_PeriphCLKInitTypeDef PeriphClkInitStruct; PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USB | RCC_PERIPHCLK_RTC; PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_PLL; PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) { Error_Handler(); }
-
这些步骤涵盖了从外部晶振到PLL配置,再到系统时钟和总线分频的整个过程。确保在配置时钟时参考芯片手册和硬件设计文档,以确保满足具体硬件的要求。
2.2、外设时钟使能和失能
在HAL库中,使能和失能外设时钟的方法通常使用了一些预定义的宏和函数。以下是对这两个操作的分析:
-
使能外设时钟:
- 通常使用形如
__HAL_RCC_XXX_CLK_ENABLE()
的宏,其中XXX
表示具体的外设(比如GPIOA、USART1等)。 - 这个宏实际上是在底层调用了HAL库中相应外设的时钟使能函数,这个函数会配置相应的时钟使能寄存器,以确保外设可以正常工作。
- 通常使用形如
-
失能外设时钟:
- 通常使用形如
__HAL_RCC_XXX_CLK_DISABLE()
的宏,其中XXX
表示具体的外设。 - 类似地,这个宏底层会调用HAL库中相应外设的时钟失能函数,将时钟使能寄存器中的相应位清零,以禁用外设的时钟。
- 通常使用形如
这些宏实际上是对底层寄存器的访问进行了封装,方便用户使用。例如,对于GPIOA的时钟使能:
c
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能 GPIOA 时钟
__HAL_RCC_GPIOA_CLK_DISABLE(); // 禁止 GPIOA 时钟
上述操作会调用底层的函数,具体实现会涉及到对相应寄存器的设置或清零。这些宏和函数的实现在HAL库的源代码中,用户一般无需直接关注底层实现,只需要正确使用这些宏即可。
这种封装提高了代码的可读性和可维护性,同时确保了对寄存器的操作是正确的。
2.3、sys_stm32_clock_init 函数(F1)
HAL_RCC_OscConfig()函数(F1)
HAL_RCC_ClockConfig函数(F1)
2.4、sys_stm32_clock_init 函数(F4/F7)
HAL_RCC_OscConfig()函数(F4/F7)
HAL_RCC_ClockConfig()函数(F4/F7)
2.5、sys_stm32_clock_init 函数(H7)
HAL_RCC_OscConfig()函数(H7)
HAL_RCC_ClockConfig()函数(H7)
HAL_RCCEx_PeriphCLKConfig()函数(H7)