下面是SystemInit(void)函数的源代码:重点和时钟配置有关的是SetSysClock()函数
void SystemInit (void)
{
/* Reset the RCC clock configuration to the default reset state(for debug purpose) */
/* 打开 HSION bit,置为1即为打开 */
RCC->CR |= (uint32_t)0x00000001;
/* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
#ifndef STM32F10X_CL //互联型芯片为105xx和107xx系列我们是hd高密度系列
RCC->CFGR &= (uint32_t)0xF8FF0000;
#else
RCC->CFGR &= (uint32_t)0xF0FF0000;
#endif /* STM32F10X_CL */
/* Reset HSEON, CSSON and PLLON bits */
RCC->CR &= (uint32_t)0xFEF6FFFF;
/* Reset HSEBYP bit */
RCC->CR &= (uint32_t)0xFFFBFFFF;
/* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
RCC->CFGR &= (uint32_t)0xFF80FFFF;
#ifdef STM32F10X_CL
/* Reset PLL2ON and PLL3ON bits */
RCC->CR &= (uint32_t)0xEBFFFFFF;
/* Disable all interrupts and clear pending bits */
RCC->CIR = 0x00FF0000;
/* Reset CFGR2 register */
RCC->CFGR2 = 0x00000000;
#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
/* Disable all interrupts and clear pending bits */
RCC->CIR = 0x009F0000;
/* Reset CFGR2 register */ //可以理解为这里把这些位都设置为默认状态,可以对照参考手册看各个位对应的作用
RCC->CFGR2 = 0x00000000;
#else
/* Disable all interrupts and clear pending bits */
RCC->CIR = 0x009F0000;
#endif /* STM32F10X_CL */
#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)
#ifdef DATA_IN_ExtSRAM
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM */
#endif
/* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
/* Configure the Flash Latency cycles and enable prefetch buffer */
SetSysClock(); //这个函数是重点
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */
#endif
}
下面是对于其中一个SetSysClockTo72()函数的分析,得出时钟配置的流程
static void SetSysClockTo72(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/
/* 使能 HSE */
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
/* 等待HSE就绪并做超时处理*/
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;
}
//如果HSE启动成功,程序继续向下执行
if (HSEStatus == (uint32_t)0x01)
{
/*使能预取指*/
FLASH->ACR |= FLASH_ACR_PRFTBE;
/* Flash 2 wait state */
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
/* AHB总线的时钟预分频因子 72MHZ*/
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
/* APB2总线时钟预分频因子 72MHZ*/
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
/* APB1总线时钟预分频因子 36MHZ*/
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
/* PLL锁相环倍频因子和输入选择为HSE HSE*9=72MHZ */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
RCC_CFGR_PLLMULL));
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
/* 开启使能PLL */
RCC->CR |= RCC_CR_PLLON;
/* 等待PLL稳定 */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
}
/* 选择系统时钟来源为PLLCLK锁相环输出时钟 */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
/* 等待PLLCLK切换为系统时钟,系统时钟稳定 */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
{
}
}
else
{ /* 如果HSE启动失败,用户可以在这里添加处理错误的代码*/
}
}
#endif
总结:
时钟配置流程
- 开启时钟并等待就绪
- 若时钟异常就退出,执行用户可以自行编写异常处理的代码
- 开启正常,先配置相关总线的预分频系数
- 最后选择系统时钟是PLL,还是HSI或者HSE,让外设得到时钟
时钟配置分析(以HSE倍频得到PLLCLK作为系统时钟为例)
- 开启HSE时钟
- 等待HSE时钟就绪(因为时钟起振等原因,需要等待一段时间时钟稳定)
- HSE状态开启正常就继续,异常就转为异常处理
- HSE正常,设置AHB 、APB1和APB2总线的预分频因子
- 选择PLL的时钟源为HSE和倍频因子
- 开启PLL时钟并等待就绪
- 选择PLLCLK作为系统时钟
其它时钟配置和MCO检测时钟输出
针对上面的分析,自己可以为了保证原来库文件的完整性,建议不要自己动手修改其中的库文件代码,我这里手动新建文件和自己编写函数实现其他时钟的配置以及MCO检查时钟输出
下面是我编写的时钟配置函数:
#include "bsp_rcc_config.h"
void HSE_SetSysClk(uint32_t RCC_PLLMul_x)
{
ErrorStatus HSEStatus; //这是一个类型参数,其中原来的库文件中有定义typedef enum
// {ERROR = 0, SUCCESS = !ERROR} ErrorStatus;
RCC_DeInit(); //记住如果要修改时钟配置,要把默认的时钟参数恢复为默认状态
RCC_HSEConfig(RCC_HSE_ON); //按照时钟配置流程,先打开时钟源,我这里还是以HSE为例
HSEStatus = RCC_WaitForHSEStartUp(); //等待HSE就绪
if(HSEStatus == SUCCESS) //如果HSE就绪成功,SUCCESS库文件中定义了ErrorStatus参数
{ //使能预取指
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
FLASH_SetLatency( FLASH_Latency_2);//这里是FLASH编程的知识,48-72MHZ等待两个状态
RCC_HCLKConfig( RCC_SYSCLK_Div1); //AHB和APB1和APB2的预分频因子
RCC_PCLK1Config( RCC_HCLK_Div2);
RCC_PCLK2Config( RCC_HCLK_Div1);
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_x); //选择HSE作为PLL的输入
和倍频因子为形参
RCC_PLLCmd(ENABLE); //PLL使能打开
while( RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET ); //等待PLLCLK就绪
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //选择SYSCLK时钟来源为PLL输出的PLLCLK
while(RCC_GetSYSCLKSource() != 0x08 ); 等待SYSCLK就绪
}
else //时钟就绪失败
{
}
}
void MCO_GPIO_Config(void) //MCO复用为时钟输出数据手册知道是PA8的复用功能
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct); //这是我随便写的一个灯的亮灭的代码
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
GPIO_Init(GPIOB, &GPIO_InitStruct);
}
头文件:
#ifndef __BSP_RCC_CONFIG_H
#define __BSP_RCC_CONFIG_H
#include "stm32f10x.h"
#define ON 1
#define OFF 0
#define LED(A) if(A) GPIO_ResetBits(GPIOB,GPIO_Pin_1);\
else GPIO_SetBits(GPIOB,GPIO_Pin_1)
void HSE_SetSysClk(uint32_t RCC_PLLMul_x);
void MCO_GPIO_Config(void);
#endif
main.c函数
#include "stm32f10x.h"
#include "bsp_rcc_config.h"
void delay(uint32_t count)
{
for(;count!=0;count--);
}
int main(void)
{
HSE_SetSysClk(RCC_PLLMul_9); //RCC_PLLMul_x的x可以是2-16对应的倍频因子,
我这是9默认为8*9=72MHZ
MCO_GPIO_Config();
RCC_MCOConfig(RCC_MCO_SYSCLK);
while(1)
{
LED(ON);
delay(0xFFFFF);
LED(OFF);
delay(0xFFFFF);
}
}