【嵌入式】---- RCC

1、为什么要有时钟
时钟对单片机来说就像人和心脏的关系一样,单片机有了时钟,才能够执行指令。所以使用任何一个外设都必须打开相应的时钟。
简单来说时钟是单片机的脉搏,为单片机执行每条指令提供驱动。

2、STM32系统时钟框图
【嵌入式】---- RCC_第1张图片
将这个框图分成两部分分析:
系统时钟SYSCLK 的左边就是设置系统时钟使用哪个时钟源;
系统时钟SYSCLK 的右边是系统时钟通过AHB预分频器,给总线上挂在的外设设置对应的时钟频率

3、时钟源
在 STM32 中,一共有 5 个时钟种类,分别是 HSI 、 HSE 、 LSI 、 LSE 、 PLL 。

①HSI 是高速内部时钟, RC 振荡器,频率为 8MHz ;

②HSE 是高速外部时钟,可接石英 / 陶瓷谐振器,或者接外部时钟源,频率范围是 4MHz – 16MHz ;

③LSI 是低速内部时钟, RC 振荡器,频率为 40KHz ;

④LSE 是低速外部时钟,接频率为 32.768KHz 的石英晶体;

⑤PLL 为锁相环倍频输出,严格的来说并不算一个独立的时钟源(因为PLL并不是自己产生的时钟源,而是通过其他三个时钟源倍频得到的时钟), PLL 的输入可以接 HSI/2 、 HSE 或者 HSE/2 。PLL倍频可选择为 2 – 16 倍,但是其输出频率最大不得超过 72MHz 。

4、系统时钟确定
三种不同的时钟源可被用来驱动系统时钟(SYSCLK):
● HSI振荡器时钟
● HSE振荡器时钟
● PLL时钟
默认时钟使用HSE,经过PLL倍频,得到72M系统时钟。
【嵌入式】---- RCC_第2张图片

5、与时钟有关的总线
(1)AHB总线
系统时钟(SYSCLK)最大频率为 72MHz ,它通过 AHB 分频器分频后送给5 大模块使用:

   ①送给 AHB 总线、内核、内存和 DMA 使用的 HCLK 时钟;

   ②通过 8 分频后送给 Cortex 的系统定时器时钟STCLK;

   ③直接送给 Cortex 的空闲运行时钟 FCLK ;

   ④送给 APB1 分频器。 APB1分频器可以选择1、2、4、8、16分频,其输出一路供 APB1 外设使用( PCLK1 ,最大频率36MHz),另一路送给定时器 (Timer)2 、 3 、 4 倍频器使用。该倍频器根据PCLK1的分频值自动选择 1 或者 2 倍频,时钟输出供定时器 2 、 3 、 4 使用。

   ⑤送给 APB2 分频器。 APB2分频器可以选择1、2、4、8、16分频,其输出一路供 APB2 外设使用( PCLK2 ,最大频率 72MHz ),另外一路送给定时器 (Timer)1 倍频使用。该倍频器根据PCLK2的分频值自动选择1 或 2 倍频,时钟输出供定时器 1 使用。另外 APB2 分频器还有一路输出供 ADC 分频器使用,分频后送给 ADC 模块使用。 ADC分频器可选择为2、4、6、8分频。

【嵌入式】---- RCC_第3张图片

(2)APB1,APB2总线
APB1从AHB上2分频,频率是36MHz,
APB2从AHB上1分频,频率是72MHz。
APB1和APB2总线上连接的模块:
【嵌入式】---- RCC_第4张图片

6、MCO时钟输出
【嵌入式】---- RCC_第5张图片
STM32可以选择一个时钟信号输出到MCO脚(PA8)上,可以选择为PLL输出的2分频、HSI、HSE、或者系统时钟SYSCLK。
这样可以把时钟信号输出供外部使用,也可以接在示波器上查看当前时钟频率是否与设定值吻合。
有些外部IC没有时钟不工作,但是加一个晶振需要成本,就可以考虑这个

7、程序
(1)默认时钟(使用HSE,经过PLL倍频,得到72M系统时钟)

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

(2)HSE时钟

void HSE_SetSysClk( uint32_t RCC_PLLMul_x )
{
	ErrorStatus HSEStatus;
	
	// 把RCC 寄存器复位成复位值
	RCC_DeInit();	

	// 使能 HSE 
	RCC_HSEConfig(RCC_HSE_ON);
	
	HSEStatus = RCC_WaitForHSEStartUp();
	
	if( HSEStatus == SUCCESS ) {
		// 使能预取指
		FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
		FLASH_SetLatency(FLASH_Latency_2);
		
		RCC_HCLKConfig(RCC_SYSCLK_Div1);
		RCC_PCLK1Config(RCC_HCLK_Div2);
		RCC_PCLK2Config(RCC_HCLK_Div1);
		
		// 配置 PLLCLK = HSE * RCC_PLLMul_x
		RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_x);
		
		// 使能PLL
		RCC_PLLCmd(ENABLE);
		
		// 等待PLL稳定
		while( RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET );
		
		// 选择系统时钟
		RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
		
		while( RCC_GetSYSCLKSource() != 0x08 );
	} else {
		/* 如果HSE 启动失败,用户可以在这里添加处理错误的代码 */
	}
}


(3)HSI时钟

void HSI_SetSysClk( uint32_t RCC_PLLMul_x )
{
	__IO uint32_t HSIStatus = 0;
	
	// 把RCC 寄存器复位成复位值
	RCC_DeInit();	

	// 使能 HSI 
	RCC_HSICmd(ENABLE);
	
	HSIStatus = RCC->CR & RCC_CR_HSIRDY;
	
	if( HSIStatus == RCC_CR_HSIRDY ) {
		// 使能预取指
		FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
		FLASH_SetLatency(FLASH_Latency_2);
		
		RCC_HCLKConfig(RCC_SYSCLK_Div1);
		RCC_PCLK1Config(RCC_HCLK_Div2);
		RCC_PCLK2Config(RCC_HCLK_Div1);
		
		// 配置 PLLCLK = HSE * RCC_PLLMul_x
		RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_x);
		
		// 使能PLL
		RCC_PLLCmd(ENABLE);
		
		// 等待PLL稳定
		while( RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET );
		
		// 选择系统时钟
		RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
		
		while( RCC_GetSYSCLKSource() != 0x08 );
	} else {
		/* 如果HSI 启动失败,用户可以在这里添加处理错误的代码 */
	}
}

(4)MCO

/*
 * 复用推挽模式开启MCO对应引脚PA8
 **/
void MCO_GPIO_Config()
{
	GPIO_InitTypeDef  GPIO_InitStruct;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOA, &GPIO_InitStruct);	
}
RCC_MCOConfig(RCC_MCO_SYSCLK);  /* 参数为要输出的时钟 */

配置好之后示波器探头接到PA8 上即可看到时钟波形

8、程序分享
链接:https://pan.baidu.com/s/18mjutYzEUkMvhEcDCtFnsg
提取码:u68h

你可能感兴趣的:(嵌入式,arm,嵌入式,stm32,单片机)