STM32时钟总结:

系统时钟图:(先上劝退图!)
在这里插入图片描述

1、时钟系统结构总结

1. STM32 有5个时钟源:HSI、HSE、LSI、LSE、PLL。(打开时钟源后要有一段时间延迟待其稳定!)
  ①、HSI是高速内部时钟,RC振荡器,频率为8MHz,精度不高。
   ②、HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时
钟源,频率范围为4MHz16MHz。
   ③、LSI是低速内部时钟,RC振荡器,频率为40kHz,提供低功耗时钟。WDG
   ④、LSE是低速外部时钟,接频率为32.768kHz的石英晶体。RTC
   ⑤、PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。
倍频可选择为2
16倍,但是其输出频率最大不得超过72MHz。
2. 系统时钟SYSCLK可来源于三个时钟源:
①、HSI振荡器时钟
②、HSE振荡器时钟
③、PLL时钟
3.STM32可以选择一个时钟信号输出到MCO脚(PA8)上,可以选择为PLL
输出的2分频、HSI、HSE、或者系统时钟。

4.几个重要时钟:
SYSCLK(系统时钟) :
AHB总线时钟
APB1总线时钟(低速): 速度最高36MHz
APB2总线时钟(高速): 速度最高72MHz
PLL时钟

4.任何一个外设在使用之前,必须首先使能其相应的时钟。

2、RCC寄存器配置总结

调用结构体里面各个成员即对应寄存器:(斜体为常用配置)都是32位!
typedef struct
{
__IO uint32_t CR; //HSI,HSE,CSS,PLL等的使能和就绪标志位
__IO uint32_t CFGR; //PLL等的时钟源选择,分频系数设定
__IO uint32_t CIR; // 清除/使能 时钟就绪中断
__IO uint32_t APB2RSTR; //APB2线上外设复位寄存器
__IO uint32_t APB1RSTR; //APB1线上外设复位寄存器
__IO uint32_t AHBENR; //DMA、SDIO等时钟使能
__IO uint32_t APB2ENR; //APB2线上外设时钟使能
__IO uint32_t APB1ENR; //APB1线上外设时钟使能
__IO uint32_t BDCR; //备份域控制寄存器
__IO uint32_t CSR; //控制状态寄存器
} RCC_TypeDef;
各寄存器详解在stm32中文参考指南中有详解,需要自己查询要了解寄存器原理要多看看
打开时钟源之后要等待其稳定

stm32库函数解析:
首先上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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
void SystemInit (void)
{
// 配置RCC_CR寄存器,对比指南:打开HSI RC时钟振荡器
RCC->CR |= (uint32_t)0x00000001;

/* 置零CFGR寄存器中 SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO 位 */
#ifndef STM32F10X_CL
RCC->CFGR &= (uint32_t)0xF8FF0000;
#else
RCC->CFGR &= (uint32_t)0xF0FF0000;
#endif /* STM32F10X_CL */

/* 置零CR寄存器中 HSEON, CSSON and PLLON 位 */
RCC->CR &= (uint32_t)0xFEF6FFFF;

/*置零CR寄存器中 HSEBYP */
RCC->CR &= (uint32_t)0xFFFBFFFF;

/* 置零CFGR寄存器中 PLLSRC, PLLXTPRE, PLLMUL 和 USBPRE/OTGFSPRE */
RCC->CFGR &= (uint32_t)0xFF80FFFF;

#ifdef STM32F10X_CL
/* 置零CR寄存器中 PLL2ON and PLL3ON*/
RCC->CR &= (uint32_t)0xEBFFFFFF;

/* Disable all interrupts and clear pending bits */
RCC->CIR = 0x00FF0000;

/* Reset CFGR2 register */
RCC->CFGR2 = 0x00000000;
#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
/* Disable all interrupts and clear pending bits */
RCC->CIR = 0x009F0000;

/* Reset CFGR2 register */
RCC->CFGR2 = 0x00000000;
#else
/*关掉中断及其相应位 */
RCC->CIR = 0x009F0000;
#endif /* STM32F10X_CL */

#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)
#ifdef DATA_IN_ExtSRAM
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM */
#endif // 一般未执行

//以上的配置完成,下面是一个重点函数涉及到具体设置系统时钟的频率
SetSysClock();


以下结束
#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
}

SetSysClock();函数具体配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static void SetSysClock(void)
{
// 配置对应的频率
#ifdef SYSCLK_FREQ_HSE
SetSysClockToHSE();
#elif defined SYSCLK_FREQ_24MHz
SetSysClockTo24();
#elif defined SYSCLK_FREQ_36MHz
SetSysClockTo36();
#elif defined SYSCLK_FREQ_48MHz
SetSysClockTo48();
#elif defined SYSCLK_FREQ_56MHz
SetSysClockTo56();
#elif defined SYSCLK_FREQ_72MHz
SetSysClockTo72();
#endif
}

要调用哪个频率,宏定义中就定义哪个频率来配置:
保留想要配置的频率,注释不需要配置的频率
/* #define SYSCLK_FREQ_HSE HSE_VALUE /
/
#define SYSCLK_FREQ_24MHz 24000000 /
/
#define SYSCLK_FREQ_36MHz 36000000 /
/
#define SYSCLK_FREQ_48MHz 48000000 /
/
#define SYSCLK_FREQ_56MHz 56000000 /
#define SYSCLK_FREQ_72MHz 72000000
/
如果上述定义均未启用,则HSI用作系统时钟源(重置后默认) */

详解频率配置函数内容,以72M为例!(一大波预警!)
先上一波关于寄存器的宏定义
太多略过。。。 详细解读!

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
static void SetSysClockTo72(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;

/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/
/* 使能打开 HSE CR低16位 */
RCC->CR |= ((uint32_t)RCC_CR_HSEON);

/* 等待HSE稳定 */
do
{
HSEStatus = RCC->CR & RCC_CR_HSERDY; //CR_低17位
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

if ((RCC->CR & RCC_CR_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
} //判断就绪


if (HSEStatus == (uint32_t)0x01)
{
/* 使能预取用缓存区,资料参考FALSH闪存参考资料! */
FLASH->ACR |= FLASH_ACR_PRFTBE;

/* 等待!参考资料*/
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;

//下面的是配置分频!分频!分频!,即配置开头图中AHB后面各个输出的频率(通过分频器分频)
/* HCLK = SYSCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;

/* PCLK2 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;

/* PCLK1 = HCLK/2 */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;


#ifdef STM32F10X_CL
/* Configure PLLs ------------------------------------------------------*/
/* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */
/* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */

RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL |
RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC);
RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 |
RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5);

/* Enable PLL2 */
RCC->CR |= RCC_CR_PLL2ON;
/* Wait till PLL2 is ready */
while((RCC->CR & RCC_CR_PLL2RDY) == 0)
{
}


/* PLL configuration: PLLCLK = PREDIV1 * 9 = 72 MHz */
RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL);
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 |
RCC_CFGR_PLLMULL9);
#else //CL系列的暂时不看

/* 输入频率配置 PLL configuration: PLLCLK = HSE * 9 = 72 MHz */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
RCC_CFGR_PLLMULL));
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9); // 倍频 9倍 8*9 = 72!
#endif /* STM32F10X_CL */

/* 使能打开 PLL */
RCC->CR |= RCC_CR_PLLON;

/* 等待稳定 Wait till PLL is ready */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
}

/*设置PLL作为系统时钟源 */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;

/* 等待PLL生效 */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
{
}
}
else
{
}
}

OVER

补充一下,一个设置在main函数之前执行预设函数的方法:(汇编)
头文件中插入:
;是注释的意思,该段是先执行编译SystemInit(先去除注释才行)

1
2
3
4
5
6
7
8
9
10
11
12
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
;寄存器版本代码,因为没有用到SystemInit函数,所以注释掉以下代码为防止报错!
;库函数版本代码,建议加上这里(外部必须实现SystemInit函数),以初始化stm32时钟等。
;IMPORT SystemInit
;LDR R0, =SystemInit
;BLX R0
LDR R0, =__main
BX R0
ENDP

wechat