0、前言
RCC-復位和時鍾控制器
可以實現配置系統時鍾SYSCLK
,配置AHB(HCLK)
總線時鍾,配置外設APB1(PCLK1)
和APB2(PCLK2)
時鍾
庫函數的標准配置為PCLK2=HCLK=SYSCLK=PLLCLK=72M,PCLK1=HCLK/2=36M
系統初始化時會調用函數實現時鍾配置。
#ifdef SYSCLK_FREQ_HSE
uint32_t SystemCoreClock = SYSCLK_FREQ_HSE; /*!< System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_24MHz
uint32_t SystemCoreClock = SYSCLK_FREQ_24MHz; /*!< System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_36MHz
uint32_t SystemCoreClock = SYSCLK_FREQ_36MHz; /*!< System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_48MHz
uint32_t SystemCoreClock = SYSCLK_FREQ_48MHz; /*!< System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_56MHz
uint32_t SystemCoreClock = SYSCLK_FREQ_56MHz; /*!< System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_72MHz
uint32_t SystemCoreClock = SYSCLK_FREQ_72MHz; /*!< System Clock Frequency (Core Clock) */
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();
#endif
在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
1、時鍾樹
主要時鍾
-
HSE:高速外部時鍾,可由有源晶振或無源晶振提供,4-16MHz
PLL以HSE為來源時可設置不分頻或2分頻
-
PLL:鎖相環時鍾源,可配置來自HSE或HSI/2
-
PLLCLK:鎖相環時鍾,可設置倍頻[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
-
SYSCLK:系統時鍾
-
HCLK:AHB總線時鍾,系統時鍾經AHB預分頻得到,分頻因子[1,2,4,8,16,64,128,256,512]
-
PCLK1:APB1總線時鍾,由HCLK通過低速APB1預分頻得到,分頻因子[1,2,4,8,16]
-
PCLK2:APB2總線時鍾,由HCLK通過高速APB2預分頻得到,分頻因子[1,2,4,8,16]
其他時鍾
- USB時鍾:由PLLCLK通過USB預分頻器得到,分頻因子[1,1.5]
- Cortex系統時鍾:由HCLK8分頻得到,用來驅動內核的系統定時器SysTick
- ADC時鍾:由PCLK2經ADC預分頻得到,分頻因子[2,4,6,8]
- RTC時鍾:由HSE/128或LSE或LSI得到
- MCO時鍾:輸出時鍾,可由PLLCLK/2,HSI,HSE,SYSCLK配置
2、時鍾配置
相關庫函數
配置函數
/*
將RCC外設初始化為復位狀態
*/
void RCC_DeInit(void);
/*
使能HSE,可選參數RCC_HSE_OFF,RCC_HSE_ON,RCC_HSE_Bypass
*/
void RCC_HSEConfig(uint32_t RCC_HSE);
/*
等待時鍾源啟動穩定,返回SUCCESS,ERROR
*/
ErrorStatus RCC_WaitForHSEStartUp(void);
/*
配置PLL時鍾源和PLL倍頻因子
RCC_RLLSource:RCC_PLLSource_HSE_Div1,RCC_PLLSource_HSE_Div2,RCC_PLLSource_HSI_Div2
RCC_PLLMul:RCC_PLLMul_2 [2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
*/
void RCC_PLLConfig(uint32_t RCC_PLLSource, uint32_t RCC_PLLMul);
/*
配置系統時鍾,可選參數RCC_SYSCLKSource_HSI,RCC_SYSCLKSource_HSE,RCC_SYSCLKSource_PLLCLK
*/
void RCC_SYSCLKConfig(uint32_t RCC_SYSCLKSource);
/*
配置HCLK,可選參數RCC_SYSCLK_Div1 [1,2,4,8,16,64,128,256,512]
*/
void RCC_HCLKConfig(uint32_t RCC_SYSCLK);
/*
配置PCLK1,可選參數RCC_HCLK_Div1 [1,2,4,8,16]
*/
void RCC_PCLK1Config(uint32_t RCC_HCLK);
/*
配置PCLK2,可選參數RCC_HCLK_Div1 [1,2,4,8,16]
*/
void RCC_PCLK2Config(uint32_t RCC_HCLK);
操作函數
/*
控制PLL開關,可選參數DISABLE,ENABLE
*/
void RCC_PLLCmd(FunctionalState NewState);
/*
獲取狀態,可選參數
#define RCC_FLAG_HSIRDY ((uint8_t)0x21)
#define RCC_FLAG_HSERDY ((uint8_t)0x31)
#define RCC_FLAG_PLLRDY ((uint8_t)0x39)
#define RCC_FLAG_LSERDY ((uint8_t)0x41)
#define RCC_FLAG_LSIRDY ((uint8_t)0x61)
#define RCC_FLAG_PINRST ((uint8_t)0x7A)
#define RCC_FLAG_PORRST ((uint8_t)0x7B)
#define RCC_FLAG_SFTRST ((uint8_t)0x7C)
#define RCC_FLAG_IWDGRST ((uint8_t)0x7D)
#define RCC_FLAG_WWDGRST ((uint8_t)0x7E)
#define RCC_FLAG_LPWRRST ((uint8_t)0x7F)
返回SET,RESET
*/
FlagStatus RCC_GetFlagStatus(uint8_t RCC_FLAG);
/*
讀取時鍾切換狀態位,返回
* - 0x00: HSI used as system clock
* - 0x04: HSE used as system clock
* - 0x08: PLL used as system clock
*/
uint8_t RCC_GetSYSCLKSource(void);
使用HSE配置系統時鍾
- 1、開啟HSE ,並等待 HSE 穩定
- 2、設置 AHB、APB2、APB1的預分頻因子
- 3、設置PLL的時鍾來源,和PLL的倍頻因子,設置各種頻率主要就是在這里設置
- 4、開啟PLL,並等待PLL穩定
- 5、把PLLCK切換為系統時鍾SYSCLK
- 6、讀取時鍾切換狀態位,確保PLLCLK被選為系統時鍾
/* 設置 系統時鍾:SYSCLK, AHB總線時鍾:HCLK, APB2總線時鍾:PCLK2, APB1總線時鍾:PCLK1
* PCLK2 = HCLK = SYSCLK
* PCLK1 = HCLK/2,最高只能是36M
* 參數說明:pllmul是PLL的倍頻因子,在調用的時候可以是:RCC_PLLMul_x , x:[2,3,...16]
* 舉例:HSE_SetSysClock(RCC_PLLMul_9); 則設置系統時鍾為:8MHZ * 9 = 72MHZ
* HSE_SetSysClock(RCC_PLLMul_16); 則設置系統時鍾為:8MHZ * 16 = 128MHZ,超頻慎用
*
* HSE作為時鍾來源,經過PLL倍頻作為系統時鍾,這是通常的做法
*/
void HSE_SetSysClock(uint32_t pllmul)
{
__IO uint32_t StartUpCounter = 0, HSEStartUpStatus = 0;
// 把RCC外設初始化成復位狀態,這句是必須的
RCC_DeInit();
//使能HSE,開啟外部晶振,野火開發板用的是8M
RCC_HSEConfig(RCC_HSE_ON);
// 等待 HSE 啟動穩定
HSEStartUpStatus = RCC_WaitForHSEStartUp();
// 只有 HSE 穩定之后則繼續往下執行
if (HSEStartUpStatus == SUCCESS)
{
//----------------------------------------------------------------------//
// 使能FLASH 預存取緩沖區
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
// SYSCLK周期與閃存訪問時間的比例設置,這里統一設置成2
// 設置成2的時候,SYSCLK低於48M也可以工作,如果設置成0或者1的時候,
// 如果配置的SYSCLK超出了范圍的話,則會進入硬件錯誤,程序就死了
// 0:0 < SYSCLK <= 24M
// 1:24< SYSCLK <= 48M
// 2:48< SYSCLK <= 72M
FLASH_SetLatency(FLASH_Latency_2);
//----------------------------------------------------------------------//
// AHB預分頻因子設置為1分頻,HCLK = SYSCLK
RCC_HCLKConfig(RCC_SYSCLK_Div1);
// APB2預分頻因子設置為1分頻,PCLK2 = HCLK
RCC_PCLK2Config(RCC_HCLK_Div1);
// APB1預分頻因子設置為1分頻,PCLK1 = HCLK/2
RCC_PCLK1Config(RCC_HCLK_Div2);
//-----------------設置各種頻率主要就是在這里設置-------------------//
// 設置PLL時鍾來源為HSE,設置PLL倍頻因子
// PLLCLK = 8MHz * pllmul
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, pllmul);
//------------------------------------------------------------------//
// 開啟PLL
RCC_PLLCmd(ENABLE);
// 等待 PLL穩定
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
{}
// 當PLL穩定之后,把PLL時鍾切換為系統時鍾SYSCLK
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
// 讀取時鍾切換狀態位,確保PLLCLK被選為系統時鍾
while (RCC_GetSYSCLKSource() != 0x08)
{}
}
else
{
// 如果HSE開啟失敗,那么程序就會來到這里,用戶可在這里添加出錯的代碼處理
// 當HSE開啟失敗或者故障的時候,單片機會自動把HSI設置為系統時鍾,
// HSI是內部的高速時鍾,8MHZ
while (1)
{
}
}
}
使用HSI配置系統時鍾
- 1、開啟HSI ,並等待 HSI 穩定
- 2、設置 AHB、APB2、APB1的預分頻因子
- 3、設置PLL的時鍾來源,和PLL的倍頻因子,設置各種頻率主要就是在這里設置
- 4、開啟PLL,並等待PLL穩定
- 5、把PLLCK切換為系統時鍾SYSCLK
- 6、讀取時鍾切換狀態位,確保PLLCLK被選為系統時鍾
/* 設置 系統時鍾:SYSCLK, AHB總線時鍾:HCLK, APB2總線時鍾:PCLK2, APB1總線時鍾:PCLK1
* PCLK2 = HCLK = SYSCLK
* PCLK1 = HCLK/2,最高只能是36M
* 參數說明:pllmul是PLL的倍頻因子,在調用的時候可以是:RCC_PLLMul_x , x:[2,3,...16]
* 舉例:HSI_SetSysClock(RCC_PLLMul_9); 則設置系統時鍾為:4MHZ * 9 = 72MHZ
* HSI_SetSysClock(RCC_PLLMul_16); 則設置系統時鍾為:4MHZ * 16 = 64MHZ
*
* HSI作為時鍾來源,經過PLL倍頻作為系統時鍾,這是在HSE故障的時候才使用的方法
* HSI會因為溫度等原因會有漂移,不穩定,一般不會用HSI作為時鍾來源,除非是迫不得已的情況
* 如果HSI要作為PLL時鍾的來源的話,必須二分頻之后才可以,即HSI/2,而PLL倍頻因子最大只能是16
* 所以當使用HSI的時候,SYSCLK最大只能是4M*16=64M
*/
void HSI_SetSysClock(uint32_t pllmul)
{
__IO uint32_t HSIStartUpStatus = 0;
// 把RCC外設初始化成復位狀態,這句是必須的
RCC_DeInit();
//使能HSI
RCC_HSICmd(ENABLE);
// 等待 HSI 就緒
HSIStartUpStatus = RCC->CR & RCC_CR_HSIRDY;
// 只有 HSI就緒之后則繼續往下執行
if (HSIStartUpStatus == RCC_CR_HSIRDY)
{
//----------------------------------------------------------------------//
// 使能FLASH 預存取緩沖區
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
// SYSCLK周期與閃存訪問時間的比例設置,這里統一設置成2
// 設置成2的時候,SYSCLK低於48M也可以工作,如果設置成0或者1的時候,
// 如果配置的SYSCLK超出了范圍的話,則會進入硬件錯誤,程序就死了
// 0:0 < SYSCLK <= 24M
// 1:24< SYSCLK <= 48M
// 2:48< SYSCLK <= 72M
FLASH_SetLatency(FLASH_Latency_2);
//----------------------------------------------------------------------//
// AHB預分頻因子設置為1分頻,HCLK = SYSCLK
RCC_HCLKConfig(RCC_SYSCLK_Div1);
// APB2預分頻因子設置為1分頻,PCLK2 = HCLK
RCC_PCLK2Config(RCC_HCLK_Div1);
// APB1預分頻因子設置為1分頻,PCLK1 = HCLK/2
RCC_PCLK1Config(RCC_HCLK_Div2);
//-----------------設置各種頻率主要就是在這里設置-------------------//
// 設置PLL時鍾來源為HSE,設置PLL倍頻因子
// PLLCLK = 4MHz * pllmul
RCC_PLLConfig(RCC_PLLSource_HSI_Div2, pllmul);
//------------------------------------------------------------------//
// 開啟PLL
RCC_PLLCmd(ENABLE);
// 等待 PLL穩定
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
{
}
// 當PLL穩定之后,把PLL時鍾切換為系統時鍾SYSCLK
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
// 讀取時鍾切換狀態位,確保PLLCLK被選為系統時鍾
while (RCC_GetSYSCLKSource() != 0x08)
{
}
}
else
{
// 如果HSI開啟失敗,那么程序就會來到這里,用戶可在這里添加出錯的代碼處理
// 當HSE開啟失敗或者故障的時候,單片機會自動把HSI設置為系統時鍾,
// HSI是內部的高速時鍾,8MHZ
while (1)
{
}
}
}
MCO輸出
MCO GPIO初始化
/*
* 初始化MCO引腳PA8
* 在F1系列中MCO引腳只有一個,即PA8,在F4系列中,MCO引腳會有兩個
*/
void MCO_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 開啟GPIOA的時鍾
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 選擇GPIO8引腳
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
//設置為復用功能推挽輸出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
//設置IO的翻轉速率為50M
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// 初始化GPIOA8
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
輸出
// MCO 引腳初始化
MCO_GPIO_Config();
// 設置MCO引腳輸出時鍾,用示波器即可在PA8測量到輸出的時鍾信號,
// 我們可以把PLLCLK/2作為MCO引腳的時鍾來檢測系統時鍾是否配置准確
// MCO引腳輸出可以是HSE,HSI,PLLCLK/2,SYSCLK
//RCC_MCOConfig(RCC_MCO_HSE);
//RCC_MCOConfig(RCC_MCO_HSI);
//RCC_MCOConfig(RCC_MCO_PLLCLK_Div2);
RCC_MCOConfig(RCC_MCO_SYSCLK);
Systick系統定時器
簡介
SysTick——系統定時器是屬於 CM3 內核中的一個外設,內嵌在 NVIC 中。
系統定時器是一個 24bit 的向下遞減的計數器,計數器每計數一次的時間為 1/SYSCLK,一般我們設置系統時鍾 SYSCLK 等於 72M。當重裝載數值寄存器的值遞減到 0 的時候,系統定時器就產生一次中斷,以此循環往復。
因為 SysTick 是屬於 CM3 內核的外設,所以所有基於 CM3 內核的單片機都具有這個系統定時器,使得軟件在 CM3 單片機中可以很容易的移植。系統定時器一般用於操作系統,用於產生時基,維持操作系統的心跳。
相關庫函數
-
SysTick配置函數
static __INLINE uint32_t SysTick_Config(uint32_t ticks) { if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */ SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */ NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */ SysTick->VAL = 0; /* Load the SysTick Counter Value */ SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ return (0); /* Function successful */ }
ticks用來設置重載寄存器的值,最大不超過\(2^{24}\),當寄存器值遞減到0時產生中斷,隨后重載、遞減,循環往復。
每一個tick對應1/SYSCLK 秒
每次中斷間隔時間為\(T_{INT}=ticks/CLK_{AHB}\)
-
修改系統定時器中斷優先級(非必要)
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);
-
對應中斷函數(
stm32f10x_it.c
)void SysTick_Handler(void) { }