STM32笔记归纳8:时钟

时钟

目录

时钟

一、时钟树

1.1.时钟和时钟树的概念

1.2.分频器、锁相环和复用器

1.3.树根

1.4.树干

1.4.1.SYSCLK来自HSI

1.4.2.SYSCLK来自锁相环

1.4.3.SYSCLK来自HSE

1.5.树枝

二、时钟树的编程

2.1.时钟树的初始状态

2.2.标准库的启动代码

2.3.时钟树的编程接口

2.4.配置时钟树

2.4.1.启动HSE

2.4.2.配置锁相环

2.4.3.配置分频器

2.4.4.配置SYSCLK来源

2.5.配置Flash指令预取

2.6.片上外设开关和复位


一、时钟树

1.1.时钟和时钟树的概念

**时钟:**高低变化的方波信号

类似于人的心跳,片上外设的运行需要输入时钟信号

时钟信号的频率决定了片上外设的工作速度

时钟树:

1.2.分频器、锁相环和复用器

**分频器:**对频率做除法

**锁相环:**对频率做乘法

**复用器:**对频率做选择

1.3.树根

树根位于时钟树最低下的一部分

单片机中有两颗时钟树(类似于人体有两套血液循环系统)

每颗时钟树有两个时钟源(类似于人体有四颗心脏)

大树的频率需求比较高,一般为几十兆赫兹,一般称为高速树,高速树的时钟源称为高速时钟源

小树的频率需求比较低,一般为几十千赫兹,一般称为低速树,低速树的时钟源称为低速时钟源

|---|--------------------------|--------------------------|
| | 内 | 外 |
| 低 | LSI(Low Speed Internal) | LSE(Low Speed External) |
| 高 | HSI(High Speed Internal) | HSE(High Speed External) |

内部时钟源:

位于单片机内部

外部时钟源:

位于单片机外部

通过PC14和PC15引脚外接LSE晶振

通过PD0和PD1引脚外界HSE晶振

注:内部时钟源精度不高,所以需要外部时钟源

1.4.树干

1.4.1.SYSCLK来自HSI

精度低,SYSCLK = 8MHz

1.4.2.SYSCLK来自锁相环

灵活,SYSCLK可变

例如:8,12,16,20,36,72MHz

1.4.3.SYSCLK来自HSE

精度高,SYSCLK = HSE频率

1.5.树枝

**AHB(Advanced High Speed Bus):**高级高速总线

**APB1(Advanced Peripheral Bus 1):**高级外设总线1

**APB2(Advanced Peripheral Bus 2):**高级外设总线2

例1:

假设SYSCLK = 8MHz

要求HCLK = PCLK1 = PCLK2 = 8MHz

将三个分频器系数全都设置为1

例2:

假设SYSCLK = 72MHz

要求HCLK = 36MHz PCLK1 = 9MHz PCLK2 = 36MHz

AHB分频器系数为2

APB1分频器系数为4

APB2分频器系数为1

例3:

假设SYSCLK = Max

要求HCLK = PCLK1 = PCLK2 = Max

AHB分频器系数为1

APB1分频器系数为2

APB2分频器系数为1

二、时钟树的编程

2.1.时钟树的初始状态

SYSCLK的频率直接来自于HSI

AHB,APB1,APB2的分频系数都为1

Cortex-M3内核(CPU)的频率为8HMz

**示例:**使用for循环实现延迟500ms

CPU执行一条指令的时钟周期为:1 / 8*10^(6) = 0.125us

假设每个for循环消耗10个时钟周期:0.125 * 10 = 1.25us

延迟500ms需要的for循环次数为:0.5s / 1.25us = 400000

cpp 复制代码
#include "stm32f10x.h"

int main(void)
{
	/*启动GPIOC时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
	/*声明GPIO结构变量*/
	GPIO_InitTypeDef GPIO_InitStruct;
	/*选择PC13引脚*/
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
	/*输出开漏模式*/
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
	/*最大输出速率为2MHz*/
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	/*初始化PA0引脚*/
	GPIO_Init(GPIOC,&GPIO_InitStruct);
	
	while(1)
	{
		GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
		for(uint32_t i = 0;i < 400000;i++);
		GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
		for(uint32_t i = 0;i < 400000;i++);
	}
}

2.2.标准库的启动代码

打开单片机的启动文件startup

找到reset代码

单片机复位后从ResetHandler开始执行

会将时钟树初始化为最高频率

可以用分号进行注释

cpp 复制代码
#include "stm32f10x.h"

int main(void)
{
	/*启动GPIOC时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
	/*声明GPIO结构变量*/
	GPIO_InitTypeDef GPIO_InitStruct;
	/*选择PC13引脚*/
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
	/*输出开漏模式*/
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
	/*最大输出速率为2MHz*/
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	/*初始化PA0引脚*/
	GPIO_Init(GPIOC,&GPIO_InitStruct);
	
	while(1)
	{
		GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
		for(uint32_t i = 0;i < 666666;i++);
		GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
		for(uint32_t i = 0;i < 666666;i++);
	}
}

2.3.时钟树的编程接口

**RCC(Reset And Clock Controller):**复位和时钟控制器

cpp 复制代码
void RCC_HSEConfig(uint32_t RCC_HSE);//HSE开关
void RCC_HSICmd(FounctionalState NewState);//HSI开关
void RCC_PLLConfig(uint32_t RCC_PLLSource,uint32_t RCC_PLLMul);//锁相环配置
void RCC_PLLCmd(FunctionalState NewState);//锁相环开关
void RCC_SYSCLKConfig(uint32_t RCC_SYSCLKSource);//SYSCLK配置
void RCC_HCLKConfig(uint32_T RCC_SYSCLK);//HCLK配置
void RCC_PCLK1Config(uint32_T RCC_HCLK);//PCLK1配置
void RCC_PCLK2Config(uint32_T RCC_HCLK);//PCLK2配置
FlagStatus RCC_GetFlagStatus(uint8_t RCC_FLAG);//获取RCC状态
uint8_t RCC_GetSTSCLKSource(void);//获取SYSCLK来源

2.4.配置时钟树

  • 开启HSE时钟
  • 配置锁相环参数,启动锁相环
  • 配置分频器的分频系数
  • 选择SYSCLK的来源
2.4.1.启动HSE
cpp 复制代码
//HSE开关
void RCC_HSEConfig(uint32_t RCC_HSE);

解析:

**参数:**开关HSE

  • RCC_HSE_ON:开
  • RCC_HSE_OFF:关
cpp 复制代码
//获取RCC的状态
FlagStatus RCC_GetFlagStatus(uint8_t RCC_FLAG);

解析:

**参数:**RCC_FLAG_HSE

**返回值:**SET(就绪)RESET(未就绪)

cpp 复制代码
//#1:开启HSE
RCC_HSEConfig(RCC_HSE_ON);
while(RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET);
2.4.2.配置锁相环
cpp 复制代码
//配置锁相环的参数
void RCC_PLLConfig(uint32_t RCC_PLLSource,uint32_t RCC_PLLMul);

解析:

**参数1:**选择锁相环输入来源

  • RCC_PLLSource_HSE_Div1:HSE
  • RCC_PLLSource_HSE_Div2:HSE / 2
  • RCC_PLLSource_HSI_Div2:HSI / 2

**参数2:**选择锁相环分频系数

  • RCC_PLLMul_2 ~ RCC_PLLMul_16
cpp 复制代码
//控制锁相环开关
void RCC_PLLCmd(FunctionalStatus NewState);

解析:

**参数:**ENABLE(开)DISABLE(关)

cpp 复制代码
//获取RCC的状态
FlagStatus RCC_GetFlagStatus(uint8_t RCC_FLAG);

解析:

**参数:**RCC_FLAG_PLLRDY

**返回值:**SET(就绪)RESET(未就绪)

cpp 复制代码
//#2:配置并启动锁相环
RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);
RCC_PLLCmd(ENABLE);
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
2.4.3.配置分频器
cpp 复制代码
//配置AHB分频器的分频系数
void RCC_HCLKConfig(uint32_t RCC_SYSCLK);
//配置APB1分频器的分频系数
void RCC_PCLK1Config(uint32_t RCC_HCLK);
//配置APB2分频器的分频系数
void RCC_PCLK2Config(uint32_t RCC_HCLK);

解析:

参数:

  • RCC_SYSCLK_Div1 ~ RCC_SYSCLK_Div51
  • RCC_HCLK_Div1 ~ RCC_HCLK_Div16
  • RCC_HCLK_Div1 ~ RCC_HCLK_Div16
cpp 复制代码
//#3:配置AHB APB1 APB2的分频系数
/*配置AHB分频器的分频系数*/
RCC_HCLKConfig(RCC_SYSCLK_Div1);
/*配置APB1分频器的分频系数*/
RCC_PCLK1Config(RCC_HCLK_Div2);
/*配置APB2分频器的分频系数*/
RCC_PCLK2Config(RCC_HCLK_Div2);
2.4.4.配置SYSCLK来源
cpp 复制代码
//设置SYSCLK的来源
void RCC_SYSCLKConfig(uint32_t RCC_SYSCLKSource);

解析:

**参数:**SYSCLK来源

  • RCC_SYSCLKSource_HSI:HSI
  • RCC_SYSCLKSource_HSE:HSE
  • RCC_SYSCLKSource_PLLCLK:锁相环
cpp 复制代码
//获取SYSCLK的来源
uint8_t RCC_GetSYSCLKSource(void);

解析:

返回值:

  • 0x00:HSI
  • 0x04:HSE
  • 0x08:锁相环
cpp 复制代码
//#4:切换SYSCLK的来源
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while(RCC_GetSYSCLKSource() != 0x08);

2.5.配置Flash指令预取

单片机运行时,CPU从Flash中读取代码执行

但是从Flash模块读取代码的速度远底于CPU执行程序速度

所以要在CPU与Flash之间增加一个缓冲区

Flash将程序提前放在缓冲区中,加快速度(指令预取

cpp 复制代码
//开启Flash指令预取
FLASH_PrefetchBufferCmd(ENABLE);

**注:**需要在SYSCLK <= 8MHz时进行

cpp 复制代码
//设置Flash访问延迟
FLASH_SetLatency(FLASH_Latency_2);

总代码:

cpp 复制代码
#include "stm32f10x.h"

void App_SystemClock_Init(void);

int main(void)
{
	App_SystemClock_Init();
	/*启动GPIOC时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
	/*声明GPIO结构变量*/
	GPIO_InitTypeDef GPIO_InitStruct;
	/*选择PC13引脚*/
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
	/*输出开漏模式*/
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
	/*最大输出速率为2MHz*/
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	/*初始化PA0引脚*/
	GPIO_Init(GPIOC,&GPIO_InitStruct);
	
	while(1)
	{
		GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
		for(uint32_t i = 0;i < 666666;i++);
		GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
		for(uint32_t i = 0;i < 666666;i++);
	}
}

void App_SystemClock_Init(void)
{
	//开启Flash指令预取
	FLASH_PrefetchBufferCmd(ENABLE);
	//设置Flash访问延迟
	FLASH_SetLatency(FLASH_Latency_2);
	
	//#1:开启HSE
	RCC_HSEConfig(RCC_HSE_ON);
	while(RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET);
	
	//#2:配置并启动锁相环
	RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);
	RCC_PLLCmd(ENABLE);
	while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
	
	//#3:配置AHB APB1 APB2的分频系数
	/*配置AHB分频器的分频系数*/
	RCC_HCLKConfig(RCC_SYSCLK_Div1);
	/*配置APB1分频器的分频系数*/
	RCC_PCLK1Config(RCC_HCLK_Div2);
	/*配置APB2分频器的分频系数*/
	RCC_PCLK2Config(RCC_HCLK_Div2);
	
	//#4:切换SYSCLK的来源
	RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
	while(RCC_GetSYSCLKSource() != 0x08);
}

2.6.片上外设开关和复位

cpp 复制代码
//开关AHB总线上的片上外设的时钟
void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph,FunctionalStatus NewState);
//开关APB2总线上的片上外设的时钟
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph,FunctionalStatus NewState);
//开关APB1总线上的片上外设的时钟
void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph,FunctionalStatus NewState);
//复位APB2总线上的片上外设
void RCC_APB2PeriphResetCmd(uint32_t RCC_APB2Periph,FunctionalStatus NewState);
//复位APB1总线上的片上外设
void RCC_APB1PeriphResetCmd(uint32_t RCC_APB1Periph,FunctionalStatus NewState);

**示例1:**开启GPIOA时钟

cpp 复制代码
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

**示例2:**开启USART2时钟

cpp 复制代码
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);

**示例3:**开启DMA1时钟

cpp 复制代码
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);

**示例4:**复位I2C1时钟

cpp 复制代码
RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1,ENABLE);
RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1,DISABLE);
相关推荐
JJRainbow2 小时前
SN75176 芯片设计RS-232 转 RS-485 通信模块设计原理图
stm32·单片机·嵌入式硬件·fpga开发·硬件工程
不做无法实现的梦~3 小时前
PX4编译环境配置和qgc配置安装教程(2)
linux·stm32
花月mmc3 小时前
CanMV K230 波形识别——整体部署(4)
人工智能·python·嵌入式硬件·深度学习·信号处理
宁静致远20213 小时前
STM32模拟IIC读取PCF8563
stm32·单片机·嵌入式硬件
三佛科技-134163842123 小时前
宠物洗澡打泡机方案,宠物泡泡机MCU方案开发设计分享
单片机·嵌入式硬件·物联网·智能家居·pcb工艺·宠物
芯岭技术3 小时前
低成本315/433M接收芯片 XL420 SOP8封装,支持 1527 等常见 OOK编码
单片机·嵌入式硬件
Wangshanjie_983 小时前
【通讯协议】-01、Modbus协议
单片机·信息与通信
听风吹雨yu4 小时前
STM32F407-MD5码计算/Digest认证计算
stm32·单片机·嵌入式硬件
yugi9878384 小时前
RN8302B电表芯片驱动开发指南(基于SPI通信)
驱动开发·单片机·嵌入式硬件