目录
前言
正如万物的生长、发展离不开时间、节律一样,芯片的工作也离不开时钟的驱动,本文总结stm32f1xx的时钟系统介绍以及时钟配置时的步骤、寄存器操作,基于标准库函数;
时钟树
上图为stm32f1xx的参考手册中的时钟树图,图中梯形框表示复用选择,方形框表示倍频或分频或时钟源;
stm32中的时钟包括HSI、HSE、LSI、LSE(内部高速振动器、外部高速振荡器、内部低速振荡器、外部低速振荡器)、PLL(锁相环);
其中HSI、HSE、LSI、LSE 可以直接提供时钟,而PLL需要以HSE、HSI为直接源或者以HSE为源并通过MCU内部分频倍频处理后得到PLL;
sysclk系统时钟也可以直接选择HSI、HSE为源头,不过这些频率都比较小在有PLL模块的芯片一般会使用PLL获得一个高频率作为系统时钟;
系统时钟作为AHB总线的输入时钟源,经过AHB总线分频后的输出作为APB1和APB2的时钟源,在时钟树节点中节点可以以某个时钟为源且可以对源的频率进行分频或者倍频给下一级,直到最终的时钟源使用者;结合以下的系统架构图方便理解:
配置列举
下面以系统时钟和uart外设时钟为例说明时钟配置
系统时钟
stm32f1xx系列最高主频为72Mhz,以配置为72Mhz说明具体配置过程:
1、时钟配置外设RCC的寄存器如下
c
typedef struct
{
__IO uint32_t CR; //时钟控制寄存器,主要对HSE、HSI、PLL的开关控制和状态查询
__IO uint32_t CFGR; //时钟配置寄存器,对时钟源的选择配置、分频倍频的配置
__IO uint32_t CIR; //时钟中断寄存器,用于清除时钟中断标志和包括表示时钟中断的位域
__IO uint32_t APB2RSTR; //APB2外设复位寄存器
__IO uint32_t APB1RSTR; //APB1外设复位寄存器
__IO uint32_t AHBENR; //使能AHB上外设的时钟
__IO uint32_t APB2ENR; //使能APB2上外设的时钟
__IO uint32_t APB1ENR; //使能APB1上外设的时钟
__IO uint32_t BDCR;
__IO uint32_t CSR;
#ifdef STM32F10X_CL
__IO uint32_t AHBRSTR;
__IO uint32_t CFGR2;
#endif /* STM32F10X_CL */
#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL)
uint32_t RESERVED0;
__IO uint32_t CFGR2;
#endif /* STM32F10X_LD_VL || STM32F10X_MD_VL || STM32F10X_HD_VL */
} RCC_TypeDef;
2、配置函数调用过程
Reset_Handler【startup_stm32f10x_hd.s】 -> SystemInit【system_stm32f10x.c】 -> SetSysClock【system_stm32f10x.c】-> SetSysClockTo72【system_stm32f10x.c】 = 产生72Mhz时钟
3、函数说明
SystemInit 中有英文注释可以对照手册的对寄存器的说明进行理解,主要是在进行时钟配置前做一些时钟复位的工作 ;
SetSysClock为一个中间函数会根据目标系统时钟宏选择调用哪个函数,这里调用SetSysClockTo72;
SetSysClock函数为时钟配置主要函数
c
static void SetSysClockTo72(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/
/* Enable HSE */
//bit16 使能选择HSE外部高度振荡器,这里为8Mhz
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
//查询CR的bit17判断振荡器是否准备好或者有信号
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) //HSE振荡器正常
{
/* Enable Prefetch Buffer */
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;
/* HCLK = SYSCLK */
//CFGR寄存器的bit7~bit4配置AHB对系统时钟不进行分频
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
/* PCLK2 = HCLK */
//CFGR寄存器的bit13~bit11配置APB2对AHB过来的时钟不进行分频
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
/* PCLK1 = HCLK */
//CFGR寄存器的bit10~bit8配置APB1对AHB过来的时钟进行二分频
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
#ifdef STM32F10X_CL
#else
/* PLL configuration: PLLCLK = HSE * 9 = 72 MHz */
//清除bit16:RCC_CFGR_PLLSRC PLL时钟源是HSI
//清除bit17:RCC_CFGR_PLLXTPRE 对HSE时钟不分频,PREDIV1从时钟树中看到它表示HSE后面的分频器
//清除bit21~bit18 PLL倍频系数先清0 不倍频
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));
//配置bit16:RCC_CFGR_PLLSRC_HSE 选择HSE作为PLL的时钟源
//配置bit21~bit18 对PLL时钟倍频9 得到PLLCLK 为 8Mhz*9=72Mhz
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
#endif /* STM32F10X_CL */
/* Enable PLL */
//使能CR的bit24 PLL使能
RCC->CR |= RCC_CR_PLLON;
/* Wait till PLL is ready */
//判断bit25 PLL时钟是否锁定
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
//清除bit1~bit0,随后设置为0x02表示将PLL作为系统时钟
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
/* Wait till PLL is used as system clock source */
//bit3~bit2 判断是否切换为PLL作为系统时钟
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
{
}
}
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 */
}
}
TIMER时钟
略