一、时钟系统综述:
1,时钟系统在STM32中,相当于人的心脏,内核及外设,根据时钟的频率执行各位的模块;
2,时钟树,下面分开进行说明:
①HSE使用外置的8MHZ晶振,在PLLXTPRE中,通过配置寄存器设置,输出8MHZ还是4MHZ;
②在这块,通过配置寄存器 PLLSRC选择:①中输出的时钟、还是HSI内部时钟/2;一般选择前者
③PLL锁相环模块,在这个模块中,对②产生的时钟,进行倍频处理,一般选用9倍频;
④通过SW寄存器配置,选择SYSCLK系统时钟的时钟源,一般选用PLLCLK,即72MHZ
⑤在④产生的SYSCLK模块,进入AHB总线中,通过AHB预分频器处理,这里一般选用 1分频;
⑥在AHB总线处理后,进入到APB1预分频器处理,一般选用2分频,得到36mhz
⑦进入到APB2预分频器处理,一般选用1分频,得到72mhz
到此,基本时钟模块配置完,核心的时钟线路配置完成;
二、时钟模块固件库实现
整体逻辑:
* 使用HSE时,设置系统时钟的步骤
* 1、开启HSE ,并等待 HSE 稳定
* 2、设置 AHB、APB2、APB1的预分频因子
* 3、设置PLL的时钟来源,和PLL的倍频因子,设置各种频率主要就是在这里设置
* 4、开启PLL,并等待PLL稳定
* 5、把PLLCK切换为系统时钟SYSCLK
* 6、读取时钟切换状态位,确保PLLCLK被选为系统时钟
1- 使用__IO,即VOlatie,不被编译器优化。HSEStartUpStatus用于接受HSE的启动状态;
并复位处理,开启外部HSE模块,等待HSE准备就绪
void HSE_SetSysClock(uint32_t pllmul)
{
__IO uint32_t HSEStartUpStatus = 0;//使用__IO,即VOlatie,不被编译器优化
// 把RCC外设初始化成复位状态,这句是必须的;
原因在启动文件中,在进入到main前,时钟就已经配置好了,此处进行复位得到对于RCC模块的寄存器复位,相当于没有配置(具体可以在库函数中寻找到这函数)
RCC_DeInit();
//使能HSE,开启外部晶振
RCC_HSEConfig(RCC_HSE_ON);
// 等待 HSE 启动稳定
如果 HSEStartUpStatus为0,则表示HSE并未准备好;
如果是SUCCEES,则表示已经准备好
HSEStartUpStatus = RCC_WaitForHSEStartUp();
if (HSEStartUpStatus == SUCCESS)
{
}
2-到flash中进行预取指运行
// 使能FLASH 预存取缓冲区
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
// SYSCLK周期与闪存访问时间的比例设置,这里统一设置成2
// 设置成2的时候,SYSCLK低于48M也可以工作,如果设置成0或者1的时候,
// 如果配置的SYSCLK超出了范围的话,则会进入硬件错误,程序就死了
// 0:0 < SYSCLK <= 24M
// 1:24< SYSCLK <= 48M
// 2:48< SYSCLK <= 72M
FLASH_SetLatency(FLASH_Latency_2);
3-配置AHB\APB2\APB1分频(本质上是操作各模块的分频寄存器数据)
// AHB预分频因子设置为1分频,HCLK = SYSCLK
RCC_HCLKConfig(RCC_SYSCLK_Div1);
// APB2预分频因子设置为1分频,PCLK2 = HCLK
RCC_PCLK2Config(RCC_HCLK_Div1);
// APB1预分频因子设置为1分频,PCLK1 = HCLK/2
RCC_PCLK1Config(RCC_HCLK_Div2);
4-配置PLL的时钟源和倍频因子
// 设置PLL时钟来源为HSE,设置PLL倍频因子
// PLLCLK = 8MHz * pllmul
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, pllmul);
5-开启PLL时钟,并且配置SYSCLK模块的时钟源是PLL
// 开启PLL
RCC_PLLCmd(ENABLE);
// 等待 PLL稳定
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
{
}
// 当PLL稳定之后,把PLL时钟切换为系统时钟SYSCLK
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
// 读取时钟切换状态位,确保PLLCLK被选为系统时钟
while (RCC_GetSYSCLKSource() != 0x08)
{
}
至此时钟源模块配置完成,为了检查配置,在MCO输出,并可以通过示波器检查数据的时钟周期
三、MCO配置输出SYSCLK
整体逻辑:
1,配置GPIO模块,PA8,复用推挽输出(不能配置直接推挽)
2,开始MCO模块,并配置时钟源为SYSCLK
配置GPIO,老一套了,注意是复用推挽输出即可
void MCO_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 开启GPIOA的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 选择GPIO8引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
//设置为复用功能推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
//设置IO的翻转速率为50M
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// 初始化GPIOA8
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
配置MCO
RCC_MCOConfig(RCC_MCO_SYSCLK);
最后main函数中配置
/*
* 配置MCO引脚:PA8 对外提供时钟,最高频率不能超过IO口的翻转频率50MHZ
* MCO 时钟来源可以是:PLLCLK/2 ,HSI,HSE,SYSCLK
*/
#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_clkconfig.h"
#include "bsp_mcooutput.h"
// 软件延时函数,使用不同的系统时钟,延时不一样
void Delay(__IO u32 nCount);
int main(void)
{
// 程序来到main函数之前,启动文件:statup_stm32f10x_hd.s已经调用
// SystemInit()函数把系统时钟初始化成72MHZ
// SystemInit()在system_stm32f10x.c中定义
// 如果用户想修改系统时钟,可自行编写程序修改
// 重新设置系统时钟,这时候可以选择使用HSE还是HSI
// 使用HSE时,SYSCLK = 8M * RCC_PLLMul_x, x:[2,3,...16],最高是128M
HSE_SetSysClock(RCC_PLLMul_9);
// MCO 引脚初始化
MCO_GPIO_Config();
// 设置MCO引脚输出时钟,用示波器即可在PA8测量到输出的时钟信号,
// 我们可以把PLLCLK/2作为MCO引脚的时钟来检测系统时钟是否配置准确
// MCO引脚输出可以是HSE,HSI,PLLCLK/2,SYSCLK
//RCC_MCOConfig(RCC_MCO_HSE);
//RCC_MCOConfig(RCC_MCO_HSI);
//RCC_MCOConfig(RCC_MCO_PLLCLK_Div2);
RCC_MCOConfig(RCC_MCO_SYSCLK);
// LED 端口初始化
LED_GPIO_Config();
while (1)
{
LED1( ON ); // 亮
Delay(0x0FFFFF);
LED1( OFF ); // 灭
Delay(0x0FFFFF);
}
}
// 软件延时函数,使用不同的系统时钟,延时不一样
void Delay(__IO uint32_t nCount)
{
for(; nCount != 0; nCount--);
}