STM32的时钟简介:
STM32中使用任何一个外设都必须打开相应的时钟。在STM32中有5个时钟源可供用户选择:
1.HSI高速内部时钟,RC震荡器,频率为8MHz。
2.HSE高速外部时钟,右英/陶瓷谐振器,或着外部时钟源,4MHz-16MHz.
3.LSI内部低速时钟,RC震荡器频率为40Hz。
4.LSE外部低速时钟,接频率为32.768KHz的石英晶体。
5.PLL锁相环频输出,时钟源可选为HIS/2、HSE或HSE/2。倍频可选2-16倍,但其输出频率最大不能超过72MHz。
系统时钟SYSCLK,它是供STM32中绝大部分器件工作的时钟源,系统时钟可选择为PLL输出、HSI或者HSE。系统时钟的做大频率为72MHz,它通过AHB分频器分频后送给个模块使用,AHB分频器可选择1、2...512分频。AHB分频器输出的时钟送给5大模块使用:
1.送给AHB总线、内核、内存、DMA使用的HCLK时钟。
2.通过8分频送给系统定时器的定时时钟(嘀嗒定时器)。
3.直接送给Cortex的空闲时钟PCLK。
4.送给APB1分频器可选择1、2、4、8、16分频,其输出一路供APB1外设使用(PCLK1,最大频率36M)另一路送给定时器(Timer)2、3、4倍频器使用。该倍频器可选择1或着2倍频,时钟输出供定时器2、3、4使用。
5.送给APB2分频器可选择1、2、4、8、16分频,其输出一路供APB2外设使用(PCLK2,最大频率72M)另一路送给定时器(Timer)1倍频器使用。该倍频器可选择1或着2倍频。时钟输出供定时器1使用。另外,APB2分频器还有一路输出供ADC分频器使用,分频后送给ADC模块使用。ADC分频器可选 择为2、4、6、8分频。
连接在APB1(低速外设)上设备有:电源接口、备份接口、CAN、USB、I2C1、I2C2、UART2、UART3、SPI2、窗口看门狗、TIMER2、TIMER3、TIMER4。
连接在APB2(高速外设)上设备有:UART1、SPI1、Timer1、ADC1、ADC2、所有普通IO口、第二功能IO口。
涉及的寄存器:
RCC 寄存器结构,RCC_TypeDeff,在文件“stm32f10x_map.h”中定义如下:
typedef struct
{
vu32 CR; //HSI,HSE,CSS,PLL等的使能
vu32 CFGR; //PLL等的时钟源选择以及分频系数设定
vu32 CIR; // 清除/使能 时钟就绪中断
vu32 APB2RSTR; //APB2线上外设复位寄存器
vu32 APB1RSTR; //APB1线上外设复位寄存器
vu32 AHBENR; //DMA,SDIO等时钟使能
vu32 APB2ENR; //APB2线上外设时钟使能
vu32 APB1ENR; //APB1线上外设时钟使能
vu32 BDCR; //备份域控制寄存器
vu32 CSR;
} RCC_TypeDef;
在ST公司的外设固件库的示例里,对于工程项目文件,ST并没有在启动函数main()里初始化PLL,因为其已经在硬件初始化阶段完成对系统时钟的配置。这样也就带了一个问题,ST库里的均使用了外设8MHz的晶振,而我们平时的项目就一定采用的是这个频率,实际配置的低于8MHz,MCU不能全速运行,高于8MHz,外设时钟即配置失败,甚至无法运行。那么如何重新配置时钟就需要我们重新设计了。
以下为使用库函数重新编写了STM32F系统时钟PLL初始化过程:
以下的函数需要添加#include "stm32f10x.h"的头文件来调用ST固件库。
static void SysClockInit(void)
{
RCC_DeInit();
RCC_HSEConfig(RCC_HSE_ON);
HSEStartUpStatus = RCC_WaitForHSEStartUp();
if (HSEStartUpStatus == SUCCESS)
{
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
FLASH_SetLatency(FLASH_Latency_2);
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK2Config(RCC_HCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div2);
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
RCC_PLLCmd(ENABLE);
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
{
}
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while(RCC_GetSYSCLKSource() != 0x08)
{
}
}
else
{
while (1)
{
}
}
}