Chapter 15 RCC--使用 HSEHSI 配置时钟

Chapter 15 RCC--使用 HSE/HSI 配置时钟

重点理解时钟树

RCC 主要作用--时钟部分

  • 设置系统时钟 SYSCLK
  • 设置 AHB 分频因子 (决定 HCLK 为多少)
  • 设置 APB2 分频因子(决定 PCLK2 为多少)
  • 设置 APB1 分频因子(决定 PCLK1 为多少)
  • 设置各外设分频因子
  • 控制 AHB、APB2 和 APB1 的 3 条总线时钟的开启
  • 控制每个外设时钟的开启

对于 SYSCLK、HCLK、PCLK2、PCLK1 这 4 个时钟的配置一般是(也是库函数的标准配置):

  • SYSCLK = PLLCLK = HCLK = PCLK2 = 72MHz
  • PCLK1 = HCLK/2 = 36 MHz

RCC 框图剖析--时钟部分

系统时钟

1、HSE 高速外部时钟信号

  • 可以是:有源晶振、无源晶振(需配谐振电容)
  • 频率为:4 ~ 16 MHz(最常用的是 8 MHz,同 HSI)
  • 作为 PLL 时钟来源时,可以:不分频、2 分频(配置由: CFGR[17] ,即 PLLXTPRE )

2、PLL 时钟源

  • PLL 时钟来源:HSE、HSI/2 (配置由:CFGR[16],即 PLLSRC )
  • HSI 内部高速时钟(8 MHz),一般不作 PLL 的时钟来源,频率会漂移(测试和环境)

3、PLL 时钟 PLLCLK

  • PLL 倍频因子(2 ~ 16)(配置由:CFGR[21:18],即 PLLMUL[3:0]),这里设置 9 (9x8=72MHz)
  • PLLCLK 最高为 128 MHz,官方推荐 PLLCLK = 8 x 9 = 72MHz

4、系统时钟 SYSCLK

  • 系统时钟来源可以是:HSI、HSE、PLLCLK(配置由:CFGR[1:0],即 SW[1:0])
  • 这里设置系统时钟为:SYSCLK = PLLCK = 72MHz

5、AHB 总线时钟 HCLK

  • AHB 分频因子(1、2、4、8、16、64、128、256、512)(配置由:CFGR[7:4],即 HPRE[3:0])
  • 分频后为 AHB 时钟 HCLK。片上大部分外设的时钟都是由 HCLK 分频得到的
  • 这里 HCLK = SYSCLK = 72MHz

6、APB2 总线时钟 PCLK2

  • PCLK2 由 HCLK 经过 APB2 预分频器而得。分频因子(1、2、8、16)(配置由:CFGR[13:11],即 PPRE2[2:0])
  • PCLK2 为高速的总线时钟,最高为 72MHz。为片上高速外设挂载,如:GPIO、USART1、SPI1等
  • 这里 PCLK2 = HCLK =72MHz

7、APB1 总线时钟 PCLK1

  • PCLK1 由 HCLK 经过 APB1 预分频器而得。分频因子(1、2、8、16)(配置由:CFGR[10:8],即 PPRE1[2:0])
  • PCLK1 为低速的总线时钟,最高为 32MHz。为片上低速外设挂载,如:USART2/3/4/5、SPI2/3、IIC1/2 等
  • 这里 PCLK1 = HCLK/2 = 36MHz

以上 7 个步骤对应的系统时钟设置的库函数,参考库文件 system_stm32f10x.c 中函数 SetSysClockTo72() 。函数是直接操作寄存器的。

1

其它时钟

1、USB 时钟 USBCLK

  • 由 PLLCLK 经过 USB 预分频器而得。分频因子(1、1.5)(配置由:CFGR[22],即 USBPRE)
  • 最高是 48MHz,由分频因子倒推,PLLCLK 只能是 48MHz、72MHz。一般设置 PLLCLK =72MHz,USBCLK = 48MHz
  • USB 对时钟要求比较高,所以 PLLCLK 只能是 HSE 倍频得到,不能使用 HSI 倍频

2、Cortex 系统时钟

  • 由 HCLK 8 分频得到:9MHz
  • 用于驱动内核的系统定时器 SysTick,用于操作系统的时钟节拍
  • SysTick 也可以用作普通定时

3、ADC 时钟 ADCCLK

  • 由 PCLK2 经过 ADC 预分频器得到。分频因子(2、4、6、8)(配置由:CFGR[15:14],即 ADCPRE[1:0])
  • ADCCLK 最高是 14MHz
  • 如果 ADC 采样周期设成 1.5 个周斯,ADC 的转换时间可以达到最短 1uS,那么时钟就是 14MHz,反推 PCLK2 只能是 28MHz、56MHz、84MHz、112MHz。因为 PCLK2 最高是 72MHz,所以只能取 28MHz、56MHz

4、RTC 时钟(RTCCLK)独立看门狗时钟(IDWGCLK)

  • RTCCLK 来源可以是:HSE/128、LSE(32.768K)、LSI。(选择由:BDCR[9:8],即 RTCSEL[1:0])
  • IDWGCLK 只能由 LSI 提供 (LSI:30 ~ 60KHz,一般取 40KHz)

5、MCO 时钟输入

  • Microcontroller Clock Output,STM32F1 中由 PA8 复用所得,主要是对外提供时钟信号
  • MCO 时钟来源可以是:PLLCLK/2、SYSCLK、HSE、HSI。(选择由:CFGR[26:24],即 MCO[2:0])
  • 可用示波器监控,以此检查系统时钟是否正确配置

配置系统时钟实验

使用 HSE

  • 一般情况,都是使用 HSE(8MHz)
  • 经过 PLL 倍频(x9)后作为系统时钟
  • 在 main 函数前,启动文件 已经调用 SystemInit() 频数,把系统时钟初始化成 72MHz
  • 自己设置系统时钟时,一般不修改底层库文件,而是由上述时钟树流程自己写一个。

使用 HSI

  • 一般不作 PLL 的时钟来源,频率会漂移(测试和环境)
  • HSE 故障时,HSE 和 PLL 都会被关闭,系统会自动切换 HSI 作为系统时钟,这时 SYSCLK = HSI = 8MHz
  • 当 HSE 故障时要采取报警措施。

如果用户想使用 HSI,自己编程程序,思路如下。

软件设计

缩写两个 RCC 驱动文件:bsp_clock.hbsp_clock.c

1、编程要求

  • RCC 外设初始化到复位状态
  • 开启 HSE/HSI,并等待 HSE/HSI 稳定
  • 设置 AHB、APB2、APB1 的预分频因子
  • 设置 PLL的时钟来源、倍频因子(各种频率主要在这里设置)
  • 开启 PLL,并等待 PLL 稳定
  • 把 PLLCLK 切换为系统时钟 SYSCLK
  • 读取时钟切换状态,确保 PLLCLK 被选为系统时钟

2、代码分析

1、使用 HSE 配置系统时钟

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
/**
* @brief Config HSE and set it as system clock
* @param pllmul: PLLCLK = 8MHz * RCC_PLLMul, RCC_PLLMul_x, x=2~16
* @retval none
*/
void HSE_SetSysClock(uint32_t RCC_PLLMul)
{
__IO uint32_t HSEStartUpStatus = 0;

/* Deinitialize RCC */
RCC_DeInit();

// Enable HSE
RCC_HSEConfig(RCC_HSE_ON);

// Wait HSE Start up
HSEStartUpStatus = RCC_WaitForHSEStartUp();

if(HSEStartUpStatus == SUCCESS)
{
//-----------------------------------------------------------
// Enable flash prefetch buffer

FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

FLASH_SetLatency(FLASH_Latency_2);
//-----------------------------------------------------------
// AHB config: HCLK = SYSCLK
RCC_HCLKConfig(RCC_SYSCLK_Div1);
// APB2 config: PCLK2 = HCLK
RCC_PCLK2Config(RCC_HCLK_Div1);
//APB! config: PCLK1 = HCLK/2
RCC_PCLK2Config(RCC_HCLK_Div2);

// Set PLL clock source and multiple factor
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul); // PLLCLK = 8MHz * RCC_PLLMul

// Enable PLL
RCC_PLLCmd(ENABLE);
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) != SUCCESS){
}

// Set PLL clock as system clock
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

//Get system clock status, , make sure PLLCLK is the system clock
while(RCC_GetSYSCLKSource() != 0x08){
}
} else{
// If HSE failure, code runs stuck here.
while(1){
}
}
}

2、使用 HSI 配置系统时钟

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
/**
* @brief Config HSI and set it as system clock
* @param pllmul: PLLCLK = 8MHz * RCC_PLLMul, RCC_PLLMul_x, x=2~16
* @retval none
*/
void HSI_SetSysClock(uint32_t RCC_PLLMul)
{
__IO uint32_t HSIStartUpStatus = 0;

/* Deinitialize RCC */
RCC_DeInit();

// Enable HSI
RCC_HSICmd(ENABLE);

// Wait HSE Start up
HSIStartUpStatus = RCC->CR & RCC_CR_HSIRDY;

if(HSIStartUpStatus == RCC_CR_HSIRDY)
{
//-----------------------------------------------------------
// Enable flash prefetch buffer

FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

FLASH_SetLatency(FLASH_Latency_2);
//-----------------------------------------------------------
// AHB config: HCLK = SYSCLK
RCC_HCLKConfig(RCC_SYSCLK_Div1);
// APB2 config: PCLK2 = HCLK
RCC_PCLK2Config(RCC_HCLK_Div1);
//APB! config: PCLK1 = HCLK/2
RCC_PCLK2Config(RCC_HCLK_Div2);

// Set PLL clock source and multiple factor
RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul); // PLLCK = 4MHz * RCC_PLLMul

// Enable PLL
RCC_PLLCmd(ENABLE);
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) != SUCCESS){
}

// Set PLL clock as system clock
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

//Get system clock status, make sure PLLCLK is the system clock
while(RCC_GetSYSCLKSource() != 0x08){
}
} else{
// If HSI failure, code runs stuck here.
while(1){
}
}
}

3、软件延时

1
2
3
4
void delay(__IO uint32_t nCount)
{
for(; nCount!=0; nCount--);
}

4、MCO 输出

STM32F103 系列中,PA8 可以利用为 MCO 引脚。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* @brief
* @param
* @retval
*/
void MCO_GPIO_Conifg(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// Enable GPIOA clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// Select MCO GPIO pin to PA8
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
// Set MCO GPIO pin mode as alternative fuction push-pull output
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
// Set MCO GPIO pin speed as 50MHz
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// Initialize GPIPA
GPIO_Init(GPIOA, &GPIO_InitStructure);

}

MCO 输出时钟选择:

1
2
3
4
5
6
	// Config MCO input clock: HSE, HSI, PLLCLK/2, SYSCLK
// Measure MCO signal on PA8
// RCC_MCOConfig(RCC_MCO_HSE);
// RCC_MCOConfig(RCC_MCO_HSI);
// RCC_MCOConfig(RCC_MCO_PLLCLK_Div2);
RCC_MCOConfig(RCC_MCO_SYSCLK);

5、main 函数:

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
int main(void)
{
// System clock has been set to 72MHz

/* Re-config system clock, use only HSE or HSI */
// Set HSE as system clock: SYSCLK = 8MHz * RCC_PLLMul_x: x=2~16
// HSE_SetSysClock(RCC_PLLMul_9);

// Set HSI as system clock: SYSCLK = 4MHz * RCC_PLLMul_x: x=2~16
HSI_SetSysClock(RCC_PLLMul_9);

LED_GPIO_Config();
// MCO I/O pin config
MCO_GPIO_Conifg();
// Config MCO input clock: HSE, HSI, PLLCLK/2, SYSCLK
// Measure MCO signal on PA8
// RCC_MCOConfig(RCC_MCO_HSE);
// RCC_MCOConfig(RCC_MCO_HSI);
// RCC_MCOConfig(RCC_MCO_PLLCLK_Div2);
RCC_MCOConfig(RCC_MCO_SYSCLK);

while(1)
{
LED_G_ON;
SOFT_DELAY;

LED_G_OFF;
SOFT_DELAY;
}
}