stm32时钟系统


https://jiangzuojiben.github.io/https://jiangzuojiben.github.io/https://jiangzuojiben.github.io/

STM32时钟系统和SysTick定时器

STM32F429的时钟树如下图所示

时钟树

STM32有5个时钟源:HSI、HSE、LSI、LSE、PLL。

HSI是高速内部时钟,RC振荡器,频率为16MHz,精度不高。可以直接作为系统时钟或者用作PLL时钟输入。

HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~26MHz。
LSI是低速内部时钟,RC振荡器,频率为32kHz,提供低功耗时钟。主要供独立看 门狗和自动唤醒单元使用。

LSE是低速外部时钟,接频率为32.768kHz的石英晶体。RTC
PLL为锁相环倍频输出。

PLL为锁相环倍频输出。STM32F4有三个PLL:

主PLL(PLL)由HSE或者HSI提供时钟信号,并具有两个不同的输出时钟。

①第一个输出PLLP用于生成高速的系统时钟(最高180MHz)

②第二个输出PLLQ为48M时钟,用于USB OTG FS时钟,随机数

​ 发生器的时钟和SDIO时钟。

第一个专用PLL(PLLI2S)生成精确时钟,在I2S和SAI1上实现高品质音频

N是用于PLLI2S vco的倍频系数,其取值范围是:192~432;

R是I2S时钟的分频系数,其取值范围是:2~7;

Q是SAI时钟分频系数,其取值范围是:2~15;P没用到。

第二个专用PLL(PLLSAI)同样用于生成精确时钟,用于SAI1输入时钟,同时还为LCD_TFT接口提供精确时钟。

N是用于PLLSAI vco的倍频系数,其取值范围是:192~432;

Q是SAI时钟分频系数,其取值范围是:2~15;

R是时钟的分频系数,其取值范围是:2-7;

主PLL时钟计算——PLLCLK= HSE * N / (M*P)

主PLL时钟计算

系统时钟SYSCLK有三个时钟源:

HSI振荡器时钟。HSE振荡器时钟。PLL时钟。

Stm32_Clock_Init时钟系统初始化函数剖析

在系统启动之后,程序会先执行 HAL 库定义的 SystemInit 函数,进行系统一些初始化配置

1
2
3
4
5
6
7
8
9
10
Reset_Handler    PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main //后

LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP

转入SystemInit函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void (void)
{
/* FPU浮点计算 设置 ------------------------------------------------------------*/

SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
#endif
/* 复位 RCC 时钟配置为默认配置 ------------*/
/*打开 HSION 位 系统默认使用HSI时钟*/
RCC->CR |= (uint32_t)0x00000001;

/* 复位 CFGR 寄存器 */
RCC->CFGR = 0x00000000;

/* 复位 HSEON, CSSON and PLLON 位 */
RCC->CR &= (uint32_t)0xFEF6FFFF;

/* 复位寄存器 PLLCFGR */
RCC->PLLCFGR = 0x24003010;

/* 复位 HSEBYP 位 */
RCC->CR &= (uint32_t)0xFFFBFFFF;

/* 关闭所有中断 */
RCC->CIR = 0x00000000;

#if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */

/* 配置中断向量表地址=基地址+偏移地址 ------------------*/
#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
}

从上面代码可以看出,SystemInit 主要做了如下四个方面工作:

1) FPU 设置

2) 复位 RCC 时钟配置为默认复位值(默认开始了 HIS)

3) 外部存储器配置

4) 中断向量表地址配置

HAL 库的 SystemInit 函数并没有像标准库的 SystemInit 函数一样进行时钟的初始化配置。HAL库的 SystemInit 函数除了打开 HSI 之外,没有任何时钟相关配置,所以使用 HAL 库我们必须编写自己的时钟配置函数。打开工程模板SYSTEM分组下面定义的sys.c 文件中的时钟初始化函数 Stm32_Clock_Init 的内容:

时钟系统配置一般步骤:

①使能PWR时钟:调用函数__HAL_RCC_PWR_CLK_ENABLE()。

②设置调压器输出电压级别:调用函数__HAL_PWR_VOLTAGESCALING_CONFIG()。

调压器输出级别

③选择是否开启Over-Driver功能:调用函数HAL_PWREx_EnableOverDrive()。

④配置时钟源相关参数:调用函数HAL_RCC_OscConfig()。

1
HAL_StatusTypeDef HAL_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct);

查看RCC_OscInitTypeDef结构体的成员变量

1
2
3
4
5
6
7
8
9
10
typedef struct 
{
uint32_t OscillatorType; //需要选择配置的振荡器类型
uint32_t HSEState; //HSE 状态
uint32_t LSEState; //LSE 状态
uint32_t HSIState; //HIS 状态
uint32_t HSICalibrationValue; //HIS 校准值
uint32_t LSIState; //LSI 状态
RCC_PLLInitTypeDef PLL; //PLL 配置
}RCC_OscInitTypeDef;

设置RCC_PLLInitTypeDef结构体类型的PLL。

1
2
3
4
5
6
7
8
9
typedef struct 
{
uint32_t PLLState; //PLL 状态
uint32_t PLLSource; //PLL 时钟源
uint32_t PLLM; //PLL 分频系数 M
uint32_t PLLN; //PLL 倍频系数 N
uint32_t PLLP; //PLL 分频系数 P
uint32_t PLLQ; //PLL 分频系数 Q
}RCC_PLLInitTypeDef;

配置系统时钟源以及AHB,APB1和APB2的分频系数:调用函数HAL_RCC_ClockConfig()。

1
HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, uint32_t FLatency);

RCC_ClkInitTypeDef结构体类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
typedef struct
{
uint32_t ClockType; /* 时钟源类型
#define RCC_CLOCKTYPE_SYSCLK ((uint32_t)0x00000001)
#define RCC_CLOCKTYPE_HCLK ((uint32_t)0x00000002)
#define RCC_CLOCKTYPE_PCLK1 ((uint32_t)0x00000004)
#define RCC_CLOCKTYPE_PCLK2 ((uint32_t)0x00000008)
提供了四种时钟源类型*/


uint32_t SYSCLKSource; /*系统时钟时钟源类型选择三种HSI,HSE,PLLCLK */

uint32_t AHBCLKDivider; /*AHB分频系数 */

uint32_t APB1CLKDivider; /*APB1分频系数 */

uint32_t APB2CLKDivider; /*APB2分频系数 */

}RCC_ClkInitTypeDef;

Stm32_Clock_Init()函数实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
//时钟系统配置函数
//Fvco=Fs*(plln/pllm);
//SYSCLK=Fvco/pllp=Fs*(plln/(pllm*pllp));
//Fusb=Fvco/pllq=Fs*(plln/(pllm*pllq));

//Fvco:VCO频率
//SYSCLK:系统时钟频率
//Fusb:USB,SDIO,RNG等的时钟频率
//Fs:PLL输入时钟频率,可以是HSI,HSE等.
//plln:主PLL倍频系数(PLL倍频),取值范围:64~432.
//pllm:主PLL和音频PLL分频系数(PLL之前的分频),取值范围:2~63.
//pllp:系统时钟的主PLL分频系数(PLL之后的分频),取值范围:2,4,6,8.(仅限这4个值!)
//pllq:USB/SDIO/随机数产生器等的主PLL分频系数(PLL之后的分频),取值范围:2~15.

//外部晶振为25M的时候,推荐值:plln=360,pllm=25,pllp=2,pllq=8.
//得到:Fvco=25*(360/25)=360Mhz
// SYSCLK=360/2=180Mhz
// Fusb=360/8=45Mhz
//返回值:0,成功;1,失败
void Stm32_Clock_Init(u32 plln,u32 pllm,u32 pllp,u32 pllq)
{
HAL_StatusTypeDef ret = HAL_OK;
RCC_OscInitTypeDef RCC_OscInitStructure;
RCC_ClkInitTypeDef RCC_ClkInitStructure;

__HAL_RCC_PWR_CLK_ENABLE(); //使能PWR时钟

//下面这个设置用来设置调压器输出电压级别,以便在器件未以最大频率工作
//时使性能与功耗实现平衡,此功能只有STM32F42xx和STM32F43xx器件有,
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);//设置调压器输出电压级别1
/*
设定了三个不同的级别
#define PWR_REGULATOR_VOLTAGE_SCALE1 PWR_CR_VOS 180 MHz
#define PWR_REGULATOR_VOLTAGE_SCALE2 PWR_CR_VOS_1 168 MHz
#define PWR_REGULATOR_VOLTAGE_SCALE3 PWR_CR_VOS_0 120 MHz.
*/

/*--------------------步骤④-----------------------------*/
RCC_OscInitStructure.OscillatorType=RCC_OSCILLATORTYPE_HSE; //时钟源为HSE
RCC_OscInitStructure.HSEState=RCC_HSE_ON; //打开HSE
RCC_OscInitStructure.PLL.PLLState=RCC_PLL_ON;//打开PLL
RCC_OscInitStructure.PLL.PLLSource=RCC_PLLSOURCE_HSE;//PLL时钟源选择HSE
RCC_OscInitStructure.PLL.PLLM=pllm; //主PLL和音频PLL分频系数(PLL之前的分频),取值范围:2~63.
RCC_OscInitStructure.PLL.PLLN=plln; //主PLL倍频系数(PLL倍频),取值范围:64~432.
RCC_OscInitStructure.PLL.PLLP=pllp; //系统时钟的主PLL分频系数(PLL之后的分频),取值范围:2,4,6,8.(仅限这4个值!)
RCC_OscInitStructure.PLL.PLLQ=pllq; //USB/SDIO/随机数产生器等的主PLL分频系数(PLL之后的分频),取值范围:2~15.
ret=HAL_RCC_OscConfig(&RCC_OscInitStructure);//初始化

if(ret!=HAL_OK) while(1);

ret=HAL_PWREx_EnableOverDrive(); //开启Over-Driver功能
if(ret!=HAL_OK) while(1);

//选中PLL作为系统时钟源并且配置HCLK,PCLK1和PCLK2
RCC_ClkInitStructure.ClockType=(RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2);//通过或运算提供了4种时钟源类型。
RCC_ClkInitStructure.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK;//设置系统时钟时钟源为PLL
RCC_ClkInitStructure.AHBCLKDivider=RCC_SYSCLK_DIV1;//AHB分频系数为1
RCC_ClkInitStructure.APB1CLKDivider=RCC_HCLK_DIV4; //APB1分频系数为4
RCC_ClkInitStructure.APB2CLKDivider=RCC_HCLK_DIV2; //APB2分频系数为2
ret=HAL_RCC_ClockConfig(&RCC_ClkInitStructure,FLASH_LATENCY_5);//同时设置FLASH延时周期为5WS,也就是6个CPU周期。

if(ret!=HAL_OK) while(1);
}