19集 STM32 時(shí)鐘

藍(lán)色方框?yàn)闀r(shí)鐘源:HSI RC、HSE OSC、PLL、LSE OSC、LSI RC
梯形紫色為選擇器:就像一個(gè)切換開關(guān)。
可以從兩步分析該時(shí)鐘圖:
1、從SYSCLK開始看,由誰可以提供時(shí)鐘給SYSCLK,逐一分析。
2、從RTCCLK、IWDGCLK開始分析,由誰可以提供時(shí)鐘給RTCCLK\IWDGCLK。
一、時(shí)鐘源:內(nèi)部時(shí)鐘的精度都比較差(《STM32中文參考手冊(cè) V10》 ---- 57頁)
HSI RC: 高速內(nèi)部時(shí)鐘(約8M),當(dāng)被用作PLL時(shí)鐘的輸入時(shí),系統(tǒng)時(shí)鐘能得到的最大頻率為64MHz
HSE OSC: 高速外部時(shí)鐘,一般采用晶振(可選4~16M,一般選8M)
PLL: 鎖相環(huán)(2~16倍),用于倍頻時(shí)鐘,可選來源 HSI 二分頻、HSE、HSE二分頻。
PLL的設(shè)置必須在其激活前完成,一旦PLL被激活,這些參數(shù)都無法被改動(dòng),如果發(fā)現(xiàn)PLL的 設(shè)置無效,就可能在設(shè)置時(shí)PLL已經(jīng)完成激活,如果需要使用USB接口,PLL必須被設(shè)置為輸 出48MHz或者72MHz時(shí)鐘,用于提供48MHz的USBCLK時(shí)鐘。
LSE OSC: 低速外部時(shí)鐘,一般采用晶振(32.768KHz)用于提供RTC時(shí)鐘
LSI RC: 低速內(nèi)部時(shí)鐘(約40KHz),用于提供獨(dú)立看門口作為時(shí)鐘源
二、SYSCLK:(最重要的時(shí)鐘)
從上圖中可以看到,SYSCLK為最重要的時(shí)鐘,幾乎大部分的外設(shè)時(shí)鐘都是從SYSCLK得到時(shí)鐘信號(hào)。所以可以從SYSCLK開始分析。
SYSCLK可以選擇從HSI RC、PLL、HSE OSC取其中一個(gè)時(shí)鐘信號(hào),而PLL則可以從HSI RC的二分頻、HSE OSC、HSE OSC 二分頻取得時(shí)鐘信號(hào)。
系統(tǒng)復(fù)位后,HSI為默認(rèn)的系統(tǒng)時(shí)鐘來源。在做時(shí)鐘源切換時(shí),目標(biāo)時(shí)鐘源穩(wěn)定就緒后才會(huì)發(fā)生切換。
1、CSS 時(shí)鐘安全系統(tǒng):(當(dāng)系統(tǒng)運(yùn)行速度莫名其妙的下降、或者USB不工作,可以留意這里)
因STM32內(nèi)部時(shí)鐘是利用電容產(chǎn)生的時(shí)鐘,并不是太穩(wěn)定,所以一般項(xiàng)目中都會(huì)采用外部晶振通過HSE OSC提供穩(wěn)定的時(shí)鐘給系統(tǒng),當(dāng)外部晶振出現(xiàn)問題即HSE失效時(shí),CSS就會(huì)自動(dòng)將時(shí)鐘源切換至HSI RC。
CSS可以通過軟件被激活,一旦其被激活,CSS將在HSE振蕩器啟動(dòng)延遲后被使能,并在HSE時(shí)鐘關(guān)閉后關(guān)閉。
如果HSE時(shí)鐘發(fā)生故障,HSE振蕩器被自動(dòng)關(guān)閉,時(shí)鐘失效事件將被送到高級(jí)定時(shí)器(TIM1和TIM8)的剎車輸入端,并產(chǎn)生時(shí)鐘安全中斷CSSI,允許軟件完成營(yíng)救操作。此CSSI中斷連接到Cortex-M3的NMI中斷。
一旦CSS被激活,并且HSE時(shí)鐘出現(xiàn)故障,CSS中斷就產(chǎn)生,并且NMI也自動(dòng)產(chǎn)生。NMI將不斷執(zhí)行,直到CSS中斷掛起位被清除。因此,在NMI的處理程序中必須通過設(shè)置時(shí)鐘中斷寄存器(RCC_CIR)里的CSSC位來清除CSS中斷。
如果HSE振蕩器被直接或間接地作為系統(tǒng)時(shí)鐘,時(shí)鐘故障將導(dǎo)致系統(tǒng)時(shí)鐘自動(dòng)切換到HSI振蕩器,同時(shí)外部HSE振蕩器被關(guān)閉。在時(shí)鐘失效時(shí),如果HSE振蕩器時(shí)鐘是用做系統(tǒng)時(shí)鐘的PLL的輸入時(shí)鐘,PLL也將關(guān)閉。(問題來了,那么USBCLK是不是也就失效了吧?)
2、每個(gè)時(shí)鐘都可以獨(dú)立開啟或關(guān)閉,這樣可以關(guān)閉不需要的時(shí)鐘從而降低功耗。
從下圖可以看出,每次需要使用某個(gè)外設(shè)時(shí)都要開啟相應(yīng)的外設(shè)時(shí)鐘。

(參考STM32中文參考手冊(cè) V10 56頁)
三、RTCCLK(RTC時(shí)鐘)
RTCCLK可以選擇從HSE OSC的128分頻、LSE OSC、LSI RC中取時(shí)鐘信號(hào)。
這里沒有CSS,那如果采用HSE的時(shí)鐘信號(hào)而其又失效,豈不是RTC也就失效了?
四、IWDGCLK(看門狗時(shí)鐘)
看門口時(shí)鐘只能從 LSI RC中取得時(shí)鐘源。
五、MCO 輸出(PA8引腳)
STM32還可以對(duì)外輸出時(shí)鐘,時(shí)鐘源可以從SYSCLK、HSI、HSE、PLLCLK的二分頻取得信號(hào)。
六、重要的時(shí)鐘
SYSCLK系統(tǒng)時(shí)鐘
AHB總線時(shí)鐘(高速)
APB1總線時(shí)鐘(低速):速度最高36MHz 連接低速外設(shè)
APB2總線時(shí)鐘(高速):速度最高72MHz 連接高速外設(shè)
PLL時(shí)鐘:用于倍頻
七、寄存器相關(guān)(具體參閱 《STM32中文參考手冊(cè) V10》 60~75頁)
typedef struct
{
__IO uint32_t CR; // HSI、HSE、CSS、PLL等的使能和就緒標(biāo)志位
__IO uint32_t CFGR; // PLL等的時(shí)鐘源選擇、分頻系數(shù)設(shè)定
__IO uint32_t CIR; // 清除、使能時(shí)鐘就緒中斷
__IO uint32_t APB2RSTR; // APB2線上所有外設(shè)復(fù)位寄存器
__IO uint32_t APB1RSTR; // APB1線上所有外設(shè)復(fù)位寄存器
__IO uint32_t AHBENR; // DMA、SDIO等時(shí)鐘使能
__IO uint32_t APB2ENR; // APB2線上所有外設(shè)時(shí)鐘使能
__IO uint32_t APB1ENR; // APB1線上所有外設(shè)時(shí)鐘使能
__IO uint32_t BDCR; // 備份域控制寄存器
__IO uint32_t CSR; // 控制狀態(tài)寄存器
} RCC_TypeDef;
0x4002 1000 - 0x4002 13FF 復(fù)位和時(shí)鐘控制(RCC)
#define PERIPH_BASE ((uint32_t)0x40000000)
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)
#define RCC_BASE (AHBPERIPH_BASE + 0x1000)
#define RCC ((RCC_TypeDef *) RCC_BASE)
八、庫函數(shù)類型(system_stm32f10x.c -- SystemInit() 函數(shù)中通過直接配置寄存器配置,并未采用庫函數(shù))
1、時(shí)鐘使能配置:
RCC_LSEConfig() 、RCC_HSEConfig()、
RCC_HSICmd() 、 RCC_LSICmd() 、 RCC_PLLCmd() …
2、時(shí)鐘源相關(guān)配置:
RCC_PLLConfig ()、 RCC_SYSCLKConfig() 、
RCC_RTCCLKConfig() …
3、分頻系數(shù)選擇配置:
RCC_HCLKConfig() 、 RCC_PCLK1Config() 、 RCC_PCLK2Config()…
4、外設(shè)時(shí)鐘使能:
RCC_APB1PeriphClockCmd(): //APB1線上外設(shè)時(shí)鐘使能
RCC_APB2PeriphClockCmd(); //APB2線上外設(shè)時(shí)鐘使能
RCC_AHBPeriphClockCmd(); //AHB線上外設(shè)時(shí)鐘使能
5. 其他外設(shè)時(shí)鐘配置:
RCC_ADCCLKConfig (); RCC_RTCCLKConfig();
6、狀態(tài)參數(shù)獲取參數(shù):
RCC_GetClocksFreq();
RCC_GetSYSCLKSource();
RCC_GetFlagStatus()
7、RCC中斷相關(guān)函數(shù) :
RCC_ITConfig() 、 RCC_GetITStatus() 、 RCC_ClearITPendingBit()…
20集 SystemInit()
SystemInit()函數(shù)在 startup_stm32f10x_hd.s 文件里面先于main函數(shù)調(diào)用。
也就是說我們?cè)趯憁ian函數(shù)時(shí),不必去調(diào)用SystemInit()函數(shù)。
一、代碼分析 (需要參考 《STM32中文參考手冊(cè) V10.pdf》、《STM32F10xxx閃存編程參考手冊(cè).pdf》)
void SystemInit (void)
{
// 開啟HSI內(nèi)部8MHz高速時(shí)鐘 1
RCC->CR |= (uint32_t)0x00000001;
// 設(shè)置HSI做為系統(tǒng)時(shí)鐘 1:0
// SYSCLK不分頻 7:4
// APB1\APB2 不分頻 13:8
// ADC預(yù)分頻 PCLK2 2分頻后作為ADC時(shí)鐘 15:14
// MCO引腳不輸出時(shí)鐘 26:24
RCC->CFGR &= (uint32_t)0xF8FF0000;
// HSE振蕩器關(guān)閉 16
// CSS時(shí)鐘監(jiān)視關(guān)閉 19
// PLL關(guān)閉 24
RCC->CR &= (uint32_t)0xFEF6FFFF;
// 設(shè)置外部4-16MHz振蕩器沒有旁路 18
RCC->CR &= (uint32_t)0xFFFBFFFF;
// 設(shè)置HSI振蕩器時(shí)鐘經(jīng)2分頻后作為PLL輸入時(shí)鐘 16
// 設(shè)置HSE不分頻 17
// 設(shè)置PLL 2倍頻輸出 21:18
// 設(shè)置USB預(yù)分頻 PLL時(shí)鐘1.5倍分頻作為USB時(shí)鐘 22
RCC->CFGR &= (uint32_t)0xFF80FFFF;
// 設(shè)置LSI、LSE、HSI、HSE、PLL就緒中斷關(guān)閉 12:8
// 清除LSI、LSE、HSI、HSE、PLL就緒中斷標(biāo)志位 20:16
// 清楚CSSF時(shí)鐘安全系統(tǒng)中斷標(biāo)志位 23
RCC->CIR = 0x009F0000;
/* 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
}
#define SYSCLK_FREQ_72MHz 72000000
static void SetSysClock(void)
{
#ifdef SYSCLK_FREQ_HSE
SetSysClockToHSE();
#elif defined SYSCLK_FREQ_24MHz
SetSysClockTo24();
#elif defined SYSCLK_FREQ_36MHz
SetSysClockTo36();
#elif defined SYSCLK_FREQ_48MHz
SetSysClockTo48();
#elif defined SYSCLK_FREQ_56MHz
SetSysClockTo56();
#elif defined SYSCLK_FREQ_72MHz // <--- 該宏被定義
SetSysClockTo72(); // <--- 該函數(shù)被調(diào)用
#endif
}
#define RCC_CR_HSEON ((uint32_t)0x00010000)
#define RCC_CR_HSERDY ((uint32_t)0x00020000)
#define HSE_STARTUP_TIMEOUT ((uint16_t)0x0500)
#define FLASH_ACR_PRFTBE ((uint8_t)0x10)
#define FLASH_ACR_LATENCY ((uint8_t)0x03)
#define FLASH_ACR_LATENCY_2 ((uint8_t)0x02)
#define RCC_CFGR_HPRE_DIV1 ((uint32_t)0x00000000)
#define RCC_CFGR_PPRE2_DIV1 ((uint32_t)0x00000000)
#define RCC_CFGR_PPRE1_DIV2 ((uint32_t)0x00000400)
#define RCC_CFGR_PLLSRC ((uint32_t)0x00010000)
#define RCC_CFGR_PLLXTPRE ((uint32_t)0x00020000)
#define RCC_CFGR_PLLMULL ((uint32_t)0x003C0000)
#define RCC_CFGR_PLLSRC_HSE ((uint32_t)0x00010000)
#define RCC_CFGR_PLLMULL9 ((uint32_t)0x001C0000)
#define RCC_CR_PLLON ((uint32_t)0x01000000)
#define RCC_CR_PLLRDY ((uint32_t)0x02000000)
#define RCC_CFGR_SW ((uint32_t)0x00000003)
#define RCC_CFGR_SW_PLL ((uint32_t)0x00000002)
#define RCC_CFGR_SWS ((uint32_t)0x0000000C)
typedef enum {RESET = 0, SET = !RESET} FlagStatus, ITStatus;
/* 要設(shè)置 72MHz 需要使用HSE外部時(shí)鐘 */
static void SetSysClockTo72(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
// 開啟HSE時(shí)鐘 16
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
// 在一定的時(shí)間內(nèi)等待 HSE 時(shí)鐘穩(wěn)定
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; // HSE時(shí)鐘就緒
}
else
{
HSEStatus = (uint32_t)0x00; // HSE時(shí)鐘未就緒
}
// 如果HSE時(shí)鐘就緒
if (HSEStatus == (uint32_t)0x01)
{
// 啟用預(yù)取緩沖區(qū) 4
FLASH->ACR |= FLASH_ACR_PRFTBE;
// 48MHz < SYSCLK <= 72MHz 設(shè)置兩個(gè)等待周期 2:0
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
/*** SYSCLK -> HCLK -> PLCK1\PCLK2 */
/* HCLK = SYSCLK */
// 設(shè)置AHB分頻系數(shù)為 1 即不分頻,等于SYSCLK的頻率 即72MHz
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
/* PCLK2 = HCLK */
// 設(shè)置APB2分頻系數(shù)為 1 即不分頻,等于HCLK的頻率 即72MHz
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
/* PCLK1 = HCLK/2 */
// 設(shè)置APB1分頻系數(shù)為 2 即二分頻,等于HCLK/2的頻率 即36MHz
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
/* PLL configuration: PLLCLK = HSE * 9 = 72 MHz */
// 這里主要是清零操作 21:16
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));
// 設(shè)置HSE作為PLL輸入時(shí)鐘 16
// 設(shè)置PLL 9 倍頻輸出 (HSE為8M晶振輸入:SYSCLK = 8MHz * 9 = 72MHz)
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
/* Enable PLL */
// 開啟PLL
RCC->CR |= RCC_CR_PLLON;
/* Wait till PLL is ready */
// 等待PLL就緒標(biāo)志位就緒 25
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
// 清零操作
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
// 設(shè)置PLL為SYSCLK時(shí)鐘源
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
{
}
}
else // HSE未就緒
{ /* If HSE fails to start-up, the application will have wrong clock
configuration. User can add here some code to deal with this error */
}
}
二、總結(jié)
SystemInit():
設(shè)置SYSCLK、APB1、APB2不分頻,MCO不輸出時(shí)鐘
SYSCLK由PLL提供時(shí)鐘、PLL則由HSI經(jīng)2分頻后提供時(shí)鐘
設(shè)置USB時(shí)鐘為PLL輸出時(shí)鐘的1.5倍
關(guān)閉所有時(shí)鐘源的中斷、清除所有時(shí)鐘源的中斷標(biāo)志位
SetSysClockTo72():
該函數(shù)首先是開啟HSE時(shí)鐘,然后在一定的時(shí)間等待其穩(wěn)定。
穩(wěn)定之后,設(shè)置FLASH開啟預(yù)取緩沖區(qū)、設(shè)置兩個(gè)等待周期。
接著設(shè)置AHB、APB2、APB1分配系數(shù)來配置HCLK、PCLK2、PCLK1的時(shí)鐘頻率。
再設(shè)置HSE作為PLL時(shí)鐘源,并設(shè)置PLL為9倍頻,再開啟PLL,等待PLL就緒。
三、注意
SystemCoreClock 在實(shí)際寫代碼中可以通過這個(gè)變量來獲得當(dāng)前所設(shè)置的系統(tǒng)時(shí)鐘頻率值
system_stm32f10x.h:
extern uint32_t SystemCoreClock;
system_stm32f10x.c:
#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
/* #define SYSCLK_FREQ_HSE HSE_VALUE */
#define SYSCLK_FREQ_24MHz 24000000
#else
/* #define SYSCLK_FREQ_HSE HSE_VALUE */
/* #define SYSCLK_FREQ_24MHz 24000000 */
/* #define SYSCLK_FREQ_36MHz 36000000 */
/* #define SYSCLK_FREQ_48MHz 48000000 */
/* #define SYSCLK_FREQ_56MHz 56000000 */
#define SYSCLK_FREQ_72MHz 72000000
#endif
#ifdef SYSCLK_FREQ_HSE
uint32_t SystemCoreClock = SYSCLK_FREQ_HSE;
#elif defined SYSCLK_FREQ_24MHz
uint32_t SystemCoreClock = SYSCLK_FREQ_24MHz;
#elif defined SYSCLK_FREQ_36MHz
uint32_t SystemCoreClock = SYSCLK_FREQ_36MHz;
#elif defined SYSCLK_FREQ_48MHz
uint32_t SystemCoreClock = SYSCLK_FREQ_48MHz;
#elif defined SYSCLK_FREQ_56MHz
uint32_t SystemCoreClock = SYSCLK_FREQ_56MHz;
#elif defined SYSCLK_FREQ_72MHz
uint32_t SystemCoreClock = SYSCLK_FREQ_72MHz;
#else /*!< HSI Selected as System Clock source */
uint32_t SystemCoreClock = HSI_VALUE;
#endif