stm32学习第六天

一、什么是RCC时钟

RCC: reset clock control 复位和时钟控制器

二、关于RCC时钟相关代码

记得最开始没有配置固件库的时候,我们都需要写一个函数来避免编译器报错。

void SystemInit()
{
}

为什么呢,因为这个函数就是配置时钟相关的函数,我们就来看看这个函数相关的源代码
在这里插入图片描述

进去过后我们可以看到有一个设置系统时钟函数
在这里插入图片描述
在按F12我们就能看到时钟相关的函数了
在这里插入图片描述

可以看到这里的时钟有着不同的速度,代表不同的时钟。

三、时钟树讲解

在我们F103官方手册上我们可以看到一个时钟树
在这里插入图片描述

3.1 时钟

3.1.1 HSE时钟

HSE:High Speed External Clock sighal (高速的外部时钟)
控制:RCC_CR时钟控制寄存器的位16:HSEON
来源:无源晶振(4-16M),通常使用8M
对应框图:在这里插入图片描述相关的寄存器
在这里插入图片描述

3.1.2 HSI时钟

HSE:High Speed Internal Clock sighal (高速的内部时钟)
控制:RCC_CR时钟控制寄存器的位0:HSEON
来源:芯片内部,大小位8M,当HSE故障的时候,系统时钟就会自动切换到HSI,直到HSE启动成功
对应框图:
在这里插入图片描述
相关的寄存器
在这里插入图片描述

3.1.3 RLLCLK时钟

RLLCLK:锁相环时钟
来源:HSI/2、HSE经过倍频所得
控制:GFGR:RLLXTPRE、RLLMUL
注意事项:PLL时钟源头使用HIS/2的时候,PLLMUL最大只能是16M,这个时候PLLCLK最大只能是64M,小于ST官方推荐的最大时钟72M
对应框图:
在这里插入图片描述
相关的详细原理可以去看火哥的视屏讲解。

3.1.4 SYSCLK时钟

SYSCLK:系统时钟
来源:HSI、HSE、RLLCLK
控制:GFGR:SW
注意事项:通常配置是SYSCLK = RLLCLK = 72M
对应框图:
在这里插入图片描述

3.1.5 HCLK时钟

HCLK: AHB高速总线时钟,速度最高为72M,为AHB总线的外设的提供时钟,
来源:系统时钟分频得到,一般设置HCLK = SYSCLK = 72M
控制:CFGR:HPRE
对应框图:
在这里插入图片描述

3.1.6 PCLK1时钟

PCLK1: APB1低速总线时钟,速度最高为36M,为AP1总线的外设的提供时钟,
来源:系统时钟分频得到,一般设置PCLK1 = HCLK/2 = 36M
控制:RCC_CFGR时钟配置寄存器的PPRE1位
对应框图:
在这里插入图片描述

3.1.7 PCLK2时钟

PCLK2: APB2高速总线时钟,速度最高为72M,为APB2总线的外设的提供时钟,
来源:系统时钟分频得到,一般设置PCLK2 = HCLK= 72M
控制:RCC_CFGR时钟配置寄存器的PPRE2位
对应框图:
在这里插入图片描述

3.1.7 RTC时钟

RTC: 为芯片内部的RTC外设提供时钟(独立看门狗时钟 real time)
来源:HSE_RTC、LSE、LSI
控制:RCC备份域控制寄存器RCC_BDCR;RECSEL位
对应框图:
在这里插入图片描述

3.1.8 MCO输出时钟

MCO: 微控制器时钟输出引脚,又PAB复用所得
来源:HSE、HSI、SYSCLK、PLCLK/2
控制:CRGR:MCO
对应框图:
在这里插入图片描述
到这里基本上所有时钟都说完了。可以去看看野火的视频,中级篇第一个视频。

四、使用HSE配置时钟系统来改变灯的闪烁

同样的,我们使用之前的固件库,复制一份,并在USER里面新建一个RCC文件夹,创建两个bsp_rccclkconfig.c 和 bsp_rccclkconfig.h文件,添加进来。

我们参考一下STM32官方的源代码

static void SetSysClockToHSE(void)
{__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  
  /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/    
  /* Enable HSE */    
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);
 
  /* Wait till HSE is ready and if Time out is reached exit */
  do
  {
    HSEStatus = RCC->CR & RCC_CR_HSERDY;
    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)
  {

#if !defined STM32F10X_LD_VL && !defined STM32F10X_MD_VL && !defined STM32F10X_HD_VL
    /* Enable Prefetch Buffer */
    FLASH->ACR |= FLASH_ACR_PRFTBE;

    /* Flash 0 wait state */
    FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);

#ifndef STM32F10X_CL
    FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_0;
#else
    if (HSE_VALUE <= 24000000)
	{
      FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_0;
	}
	else
	{
      FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_1;
	}
#endif /* STM32F10X_CL */
#endif
 
    /* HCLK = SYSCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
      
    /* PCLK2 = HCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
    
    /* PCLK1 = HCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV1;
    
    /* Select HSE as system clock source */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    RCC->CFGR |= (uint32_t)RCC_CFGR_SW_HSE;    

    /* Wait till HSE is used as system clock source */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x04)
    {
    }
  }
  else
  { /* If HSE fails to start-up, the application will have wrong clock 
         configuration. User can add here some code to deal with this error */
  }  
}

我们自己配置bsp_rccclkconfig.c
这里面很多时钟相关的东西,所以我们需要打开stm32f10x_rcc.h这个库
在这里插入图片描述
我们可以直接跳到最后,这里有很多函数名,大概书写的这个函数的作用。

  1. 避免之前的时钟干扰,我们先将直线的时钟复位
//原型 void RCC_DeInit(void);
// 12 复位所有时钟
RCC_DeInit();

下面的就按照源代码的步骤进行调库处理
按照我源代码的顺序,我们第一步需要去使能HSE

  1. 使能HSE
//原型 void RCC_HSEConfig(uint32_t RCC_HSE);
// 1 使能HSE ,并等待HSE稳定。
	RCC_HSEConfig(RCC_HSE_ON);
  1. // 2 等待HSE启动稳定,并做超时处理。判断启动成功和失败
//原型 ErrorStatus RCC_WaitForHSEStartUp(void);
ErrorStatus  HSEStatus;
HSEStatus = RCC_WaitForHSEStartUp();
	// 3 HSE 启动成功,则继续往下处理。
	if(HSEStatus == SUCCESS)
	{
		//如果成功,则开始执行
	}else
	{
		//如果失败则打印失败的消息
	}
  1. 设定我们启动HSE成功 条件下。我们第一步就是使能FLASH预存缓冲
//原型 void FLASH_PrefetchBufferCmd(uint32_t FLASH_PrefetchBuffer);
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
  1. 设置SYSCLK 周期与闪存访问时间的比例,这里统一设置成 2,因为设置成 2 的时候,SYSCLK 低于 48M 也可以工作,如果设成 0 或者 1 的时候,如果配置的 SYSCLK 超出了范围的话,则会进入硬件错误,程序就死了。
//原型 void FLASH_SetLatency(uint32_t FLASH_Latency);
FLASH_SetLatency(FLASH_Latency_2);
  1. 设置 AHB、APB2、APB1 预分频因子
//原型 void RCC_HCLKConfig(uint32_t RCC_SYSCLK);
//原型 void RCC_PCLK1Config(uint32_t RCC_HCLK);
//原型 void RCC_PCLK2Config(uint32_t RCC_HCLK);
RCC_HCLKConfig(RCC_HCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div2);
RCC_PCLK2Config(RCC_HCLK_Div1);
  1. 配置锁相环时钟
//原型 void RCC_PLLConfig(uint32_t RCC_PLLSource, uint32_t RCC_PLLMul);
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_x);
/*设置锁相环时钟的来源和频率*/
  1. 使锁相环时钟使能
//原型 void RCC_PLLCmd(FunctionalState NewState);
RCC_PLLCmd(ENABLE);
  1. 等待系统稳定,并把锁相环时钟配置为系统时钟,判断读取状态
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
		{
			// 10 选择系统时钟来源
			RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
			// 11 读取时钟切换状态,确保PLLCLK被选为系统时钟
			while(RCC_GetSYSCLKSource() != 0X08)
			{
					
			}
		}

RCC_PLLMul_x 最高只能配置为16.

思路就是这样 ,,如果不知道函数返回值的意思,我们可以用F12进入到相关的函数解释里面,会有大概的解释我们看一下完整的代码
bsp_rccclkconfig.h

#ifndef _BSP_RCCCLKCONFIG_H
#define _BSP_RCCCLKCONFIG_H

#include "stm32f10x.h"

void HSE_SetSysClk(uint32_t RCC_PLLMul_x);


#endif /*_BSP_RCCCLKCONFIG_H*/

bsp_rccclkconfig.c

#include "bsp_rccclkconfig.h"

void HSE_SetSysClk(uint32_t RCC_PLLMul_x)
{
	//声明写在最前,不然会报错
	ErrorStatus  HSEStatus;
	// 12 复位所有时钟
	RCC_DeInit();
	// 1 使能HSE ,并等待HSE稳定。
	RCC_HSEConfig(RCC_HSE_ON);
	// 2 等待HSE启动稳定,并做超时处理。
	HSEStatus = RCC_WaitForHSEStartUp();
	// 3 HSE 启动成功,则继续往下处理。
	if(HSEStatus == SUCCESS)
	{
		// 4 使能 FLASH 预存取缓冲区
		FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
		// 5 SYSCLK 周期与闪存访问时间的比例设置,这里统一设置成 2
		FLASH_SetLatency(FLASH_Latency_2);
		// 6 设置 AHB、APB2、APB1 预分频因子
		RCC_HCLKConfig(RCC_HCLK_Div1);
		RCC_PCLK1Config(RCC_HCLK_Div2);
		RCC_PCLK2Config(RCC_HCLK_Div1);
		// 7 配置PLLCLK
		RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_x);
		// 8 使能 PLL
		RCC_PLLCmd(ENABLE);
		// 9 等待PLL稳定
		while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
		{
			// 10 选择系统时钟来源
			RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
			// 11 读取时钟切换状态,确保PLLCLK被选为系统时钟
			while(RCC_GetSYSCLKSource() != 0X08)
			{
					
			}
		}
	}else
	{
			//打印错误信息
	}
}

我们还是采用之前的闪烁灯的代码
bsp_led.h

#ifndef 	_BSP_LED_H
#define 	_BSP_LED_H

#include 	"stm32f10x.h"

#define LED_GPIO_PORT 	GPIOB
#define LED_GPIO_CLK 		RCC_APB2Periph_GPIOB
#define LED_GPIO_PIN		GPIO_Pin_1

void LED_CONFIG(void);

#endif	 /*_BSP_LED_H*/

bsp_led.c

#include "bsp_led.h"
/*延时函数*/
void Delay(uint32_t count)
{
		for(; count!= 0; count--);
}
	
void LED_CONFIG(void)
{
		/*定义一个 GPIO_InitTypeDef 类型的结构体*/
		GPIO_InitTypeDef LED_CONFIG;
		/*打开APB2上 GPIOB的时钟*/
		RCC_APB2PeriphClockCmd(LED_GPIO_CLK, ENABLE);
		/*配置GPIO口的输出*/
		LED_CONFIG.GPIO_Mode  = GPIO_Mode_Out_PP;
		LED_CONFIG.GPIO_Pin   =	LED_GPIO_PIN;
		LED_CONFIG.GPIO_Speed = GPIO_Speed_50MHz;
		
		GPIO_Init(LED_GPIO_PORT, &LED_CONFIG);

	while(1)
	{
			/*配置低电平输入   亮灯*/
			GPIO_ResetBits(LED_GPIO_PORT, LED_GPIO_PIN);
			Delay (0xFFFFF);		//延时等待
			/*配置高电平输入    灭灯*/
			GPIO_SetBits(LED_GPIO_PORT, LED_GPIO_PIN);
			Delay (0xFFFFF);		//延时等待
	}	
}

main.c

#include "stm32f10x.h"    //默认是在当前文件夹
#include "bsp_led.h"
#include "bsp_rccclkconfig.h"
int main(void)
{
	//通过修改返回值,来改变灯的闪烁频率
	//默认配置72M,也就是 RCC_PLLMul_9 原来内部默认是8M   8*9 = 72M,我们这里可以改大改小	
	HSE_SetSysClk(RCC_PLLMul_16);
	LED_CONFIG();

}

这样就可以看到我们配置不同的频率,我们灯在同一的延时下,延时的时长是不一样的。

五、使用 HSI 配置系统时钟

HSI 设置系统时钟函数跟 HSE 设置系统时钟函数在原理上是一样的,有一个区别的地方就是,HSI 必须 2 分频之后才能作为 PLL 的时钟来源,所以使用 HSI 时,最大的系统时钟 SYSCLK只能是 HSI/216=416=64MHZ。
函数调用举例:HSI_SetSysClock(RCC_PLLMul_9); 则设置系统时钟为:8 / 2 MHZ * 9 =36MHZ。
我们基本不需要修改什么,把HSE换成HSI就好了

bsp_rccclkconfig.h

#ifndef _BSP_RCCCLKCONFIG_H
#define _BSP_RCCCLKCONFIG_H

#include "stm32f10x.h"

void HSE_SetSysClk(uint32_t RCC_PLLMul_x);
void HSI_SetSysClk(uint32_t RCC_PLLMul_x);

#endif /*_BSP_RCCCLKCONFIG_H*/

bsp_rccclkconfig.c

void HSI_SetSysClk(uint32_t RCC_PLLMul_x)
{
//声明写在最前,不然会报错
	ErrorStatus  HSIStatus;
	// 12 复位所有时钟
	RCC_DeInit();
	// 1 使能HSE ,并等待HSE稳定。
	RCC_HSICmd(ENABLE);
	// 2 等待HSE启动稳定,并做超时处理。
	HSIStatus = RCC_WaitForHSEStartUp();
	// 3 HSE 启动成功,则继续往下处理。
	if(HSIStatus == SUCCESS)
	{
		// 4 使能 FLASH 预存取缓冲区
		FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
		// 5 SYSCLK 周期与闪存访问时间的比例设置,这里统一设置成 2
		FLASH_SetLatency(FLASH_Latency_2);
		// 6 设置 AHB、APB2、APB1 预分频因子
		RCC_HCLKConfig(RCC_HCLK_Div1);
		RCC_PCLK1Config(RCC_HCLK_Div2);
		RCC_PCLK2Config(RCC_HCLK_Div1);
		// 7 配置PLLCLK
		RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_x);
		// 8 使能 PLL
		RCC_PLLCmd(ENABLE);
		// 9 等待PLL稳定
		while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
		{
			// 10 选择系统时钟来源
			RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
			// 11 读取时钟切换状态,确保PLLCLK被选为系统时钟
			while(RCC_GetSYSCLKSource() != 0X08)
			{
					
			}
		}
	}else
	{
			//打印错误信息
	}

}

这里就可以看到灯的闪烁频率明显要慢很多,因为HSI的频率只能是64M比较慢。不像HSE可以配置为128M。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

永不秃头的程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值