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)
{
}
}
}