STM32单片机时钟系统详解:从基础概念到具体配置
STM32单片机时钟系统详解:从基础概念到具体配置
本文主要介绍STM32单片机的时钟系统,重点讲解了STM32F1系列的时钟树结构、相关配置函数以及系统时钟配置步骤。内容详尽,包含具体代码示例和时钟树图,适合对STM32单片机感兴趣的开发者阅读。
一、什么是时钟?
简单来说,时钟是具有周期性的脉冲信号,最常用的是占空比50%的方波。
时钟对单片机来说就像脉搏一样重要,在单片机中起着至关重要的作用。搞懂时钟走向及关系是很有必要的。
二、STM32F1
1. 时钟树图
从长竖线下来左右两边分别是芯片外部和芯片内部。
(1)两组引脚:外部时钟源
- OSC_OUT、OSC_IN:外部高速晶振所连接的引脚
- OSC32_OUT、OSC32_IN:外部低速晶振所连接的引脚
(2)四个时钟源
一般我们选用(8MHz)HSE作为时钟源,材料一般是石英晶体(LSE的材料一般也是石英)。
H:high L:low S:speed I:internal E:external
实物图:
时钟源优缺点:
外部:成本高、稳定性高
内部:成本低、不太稳定
(3)时钟树图说明
① 当HSI被用于作为PLL时钟的输入时,系统时钟能得到的最大频率是64MHz。
② 用户可通过多个预分频器配置AHB、高速APB(APB2)和低速APB(APB1)域的频率。AHB和 APB2域的最大频率是72MHz。APB1域的最大允许频率是36MHz。
③ SDIO接口的时钟频率固定为HCLK/2。
④ RCC(复位和时钟控制)通过AHB时钟(HCLK)8分频后作为Cortex系统定时器(SysTick)的外部时钟。通过对SysTick 控制与状态寄存器的设置,可选择上述时钟或Cortex(HCLK)时钟作为SysTick时钟。(系统嘀嗒定时器)
⑤ ADC时钟 由高速APB2时钟经2、4、6或8分频后获得。ADCCLK最大14MHz
72MHz/6=12MHz
⑥ 定时器时钟频率分配由硬件按以下2种情况自动设置:APB1和APB2线上的TIM频率都是72MHz
- 如果相应的APB预分频系数是1,定时器的时钟频率与所在APB总线频率一致。
- 否则,定时器的时钟频率被设为与其相连的APB总线频率的2倍。
TIM1和TIM8是高级定时器;TIM6和TIM7是基本定时器;TIM2~TIM5是通用定时器。
⑦ FCLK是Cortex™-M3的自由运行时钟。详情见ARM的Cortex™-M3技术参考手册。
⑧ 内核的:AHB总线上的外设,SDIO、FSMC、存储器和DMA
2. STM32F103时钟树简图
- 高频部分:
如上图所示,系统时钟的来源均来自高频部分(HSE、HSI和PLL(锁相环输出)),一般我们选择PLLCLK(锁相环输出)作为系统时钟来源。
①、内核时钟来源是HCLK(AHB总线时钟,72MHz),部分外设(SDIO、DMA、FSMC、Flash等)时钟来源也来自HCLK,即挂载在AHB总线上。
②、挂载在APB1、APB2上的外设的基础频率分别是36MHz和72MHz,但是外设的具体时钟频率是多少还要看:进入这个外设之前有无分频器、倍频器,进入之后,里面有无预分频器。
- 低频部分:
LSE:是RTC实时时钟的主要来源(优选);
LSI:主要用于给独立看门狗(IWDG)和RCT提供时钟,(RC振荡器不是很精准)。
(RTC时钟来源,优选LSE,次选HSE/128分频,最后才选LSI。)
- 其他说明:
(1)、SYSCLK官方最大为72MHz,但是可以超频(暂不考虑)。
(2)、如果选择时钟源是HSI(8M),8MHz/2*16=64MHz(最大),系统时钟是得不到72MHz的。
3. 相关函数解析
(1)HAL_RCC_OscConfig()
Osc(振荡器)
HAL_StatusTypeDef HAL_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct)
// RCC_OscInitTypeDef结构体成员
typedef struct
{
uint32_t OscillatorType; /* 选择需要配置的振荡器 */
uint32_t HSEState; /* HSE 状态 */
uint32_t HSEPredivValue; /* HSE 预分频值 */
uint32_t LSEState; /* LSE 状态 */
uint32_t HSIState; /* HSI状态 */
uint32_t HSICalibrationValue; /* HSI 校准值 */
uint32_t LSIState; /* LSI 状态 */
RCC_PLLInitTypeDef PLL; /* PLL 结构体 */
}RCC_OscInitTypeDef;
// RCC_PLLInitTypeDef结构体成员
typedef struct
{
uint32_t PLLState; /* PLL 状态 */
uint32_t PLLSource; /* PLL 时钟源 */
uint32_t PLLMUL; /* PLL 倍频系数 */
}RCC_PLLInitTypeDef;
这个函数主要就是对RCC_OscInitTypeDef这个结构体的一些配置,主要是对时钟源和锁相环的配置。
(2)HAL_RCC_ClockConfig()
HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, uint32_t FLatency)
// RCC_ClkInitTypeDef结构体成员
typedef struct
{
uint32_t ClockType; /* 要配置的时钟(SYSCLK/HCLK/PCLK1/PCLK2) */
uint32_t SYSCLKSource; /* 系统时钟源 */
uint32_t AHBCLKDivider; /* AHB 时钟预分频系数 */
uint32_t APB1CLKDivider; /* APB1 时钟预分频系数 */
uint32_t APB2CLKDivider; /* APB2 时钟预分频系数 */
}RCC_ClkInitTypeDef;
// uint32_t FLatency 参数选择
#define FLASH_LATENCY_0 0x00000000U /* FLASH 0个等待周期 */
#define FLASH_LATENCY_1 FLASH_ACR_LATENCY_0 /* FLASH 1个等待周期 */
#define FLASH_LATENCY_2 FLASH_ACR_LATENCY_1 /* FLASH 2个等待周期 */
// 实际设置FLASH_ACR寄存器LATENCY位域,请参考《 STM32F10xxx闪存编程参考手册.pdf 》3.1小节
我们一般用的是72MHz的主频,所以是选择两个等待状态,010:FLASH_LATENCY_2
这个函数主要是用来配置系统时钟及时钟源的选择,配置进入AHB总线、APB1、APB2时钟线前的预分频器的配置,进而配置正确的频率HCLK(72MHz)、PCLK1(36MHz)、PCLK2(72MHz)。
- 为什么要设置FLatency参数?
Flash的时钟来源是AHB上的72MHz,但Flash本身最大均需24MHz,所以使用72MH访问Flash需要等待。
(3)__HAL_RCC_PPP_CLK_ENABLE()
PPP:代表具体的外设,如GPIOA、GPIOB、USART1、ADC、......等。
表示使能某个具体的外设时钟,这个函数对应下图的操作。
我们要使用某个外设,必需先使能该外设时钟!!!
与之对应的就是失能时钟函数:
__HAL_RCC_PPP_CLK_DISABLE(); /* 禁止 PPP 时钟 */
4. 系统时钟配置步骤
SystemInit()函数的调用说明:
在启动文件内的Reset Handle复位中断服务函数中调用
SystemInit()内只配置了总段向量所在位置,并没有配置适中相关的,所以对F103来,可不调用,而在有些系列,他可能还对单片机的时钟系统相关的寄存器进行复位(H7系列,必须调用),不同版本的库,做的事不一样。
(1)代码段
// stm32f1xx_hal_conf.h
#if !defined (HSE_VALUE)
#define HSE_VALUE 8000000U /*!< Value of the External oscillator in Hz */
#endif /* HSE_VALUE */
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0}; // 振荡器结构体变量初始化
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; // 时钟结构体变量初始化
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; // 振荡器类型为HSE
RCC_OscInitStruct.HSEState = RCC_HSE_ON; // HSE使能
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; // HSE预分频值为1:8MHz/1=8MHz
RCC_OscInitStruct.HSIState = RCC_HSI_ON; // HSI使能
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; // PLL锁相环使能
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; // PLL锁相环时钟源为HSE
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; // PLL锁相环倍频系数为9:8MHz*9=72MHz
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) // 初始化振荡器配置
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks 初始化CPU、AHB和APB总线时钟
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK // 要配置的时钟:HCLK、SYSCLK、PCLK1、PCLK2
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 系统时钟源为PLL锁相环时钟
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB时钟分频系数为1:72MHz/1=72MHz
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; // APB1时钟分频系数为2:72MHz/2=36MHz
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; // APB2时钟分频系数为1:72MHz/1=72MHz
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) // 初始化时钟配置
{
Error_Handler();
}
}
(2)图解
- MCO:STM32对外部输出时钟的通道,引脚PA8的复用功能。
三、STM32F4
四、STM32F7
五、STM32H7
六、其他问题
- 在stm32f103单片机的时钟系统中,APB1总线最大允许是36MHz,但是APB1预分频器上却有1分频,理论上最小的预分频系数不应该是2吗?这样才可以保证APB1最大为36Mz,如果使用1分频的话,得到了72MHz了。