LinkedInSTM32F4 時鐘系統初始化是在system_stm32f4xx.c中的 SystemInit()函數中完成的。 對于系統時鐘關鍵寄存器設置主要是在 SystemInit 函數中調用 SetSysClock()函數來設置的。我們可以先看看 SystemInit ()函數體:
?
void SystemInit(void)
{
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
SCB-》CPACR |= ((3UL 《《 10*2)|(3UL 《《 11*2));
#endif
RCC-》CR |= (uint32_t)0x00000001;
RCC-》CFGR = 0x00000000;
RCC-》CR &= (uint32_t)0xFEF6FFFF;
RCC-》PLLCFGR = 0x24003010;
RCC-》CR &= (uint32_t)0xFFFBFFFF;
RCC-》CIR = 0x00000000;
#if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
SystemInit_ExtMemCtl();
#endif
SetSysClock();
#ifdef VECT_TAB_SRAM
SCB-》VTOR = SRAM_BASE | VECT_TAB_OFFSET;
#else
SCB-》VTOR = FLASH_BASE | VECT_TAB_OFFSET;
#endif
}
SystemInit函數開始先進行浮點運算單元設置,然后是復位PLLCFGR,CFGR寄存器,同時通過設置 CR 寄存器的 HSI 時鐘使能位來打開 HSI 時鐘。默認情況下如果 CFGR 寄存器復位,那么是選擇HSI作為系統時鐘,這點大家可以查看RCC-》CFGR 寄存器的位描述最低2位可以得知,當低兩位配置為 00的時候(復位之后),會選擇 HSI振蕩器為系統時鐘。也就是說,調用 SystemInit 函數之后,首先是選擇 HSI 作為系統時鐘。
在設置完相關寄存器后,接下來SystemInit函數內部會調用 SetSysClock函數。這個函數比較長,我們就把函數一些關鍵代碼行截取出來給大家講解一下。這里我們省略一些宏定義標識符值的判斷而直接把針對STM32F407 比較重要的內容貼出來:
static void SetSysClock(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
RCC-》CR |= ((uint32_t)RCC_CR_HSEON);
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)
{
RCC-》APB1ENR |= RCC_APB1ENR_PWREN;
PWR-》CR |= PWR_CR_VOS;
RCC-》CFGR |= RCC_CFGR_HPRE_DIV1;
RCC-》CFGR |= RCC_CFGR_PPRE2_DIV2;
RCC-》CFGR |= RCC_CFGR_PPRE1_DIV4;
RCC-》CFGR |= RCC_CFGR_PPRE2_DIV1;
RCC-》CFGR |= RCC_CFGR_PPRE1_DIV2;
RCC-》PLLCFGR = PLL_M | (PLL_N 《《 6) | (((PLL_P 》》 1) -1) 《《 16) |
(RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q 《《 24);
RCC-》CR |= RCC_CR_PLLON;
while((RCC-》CR & RCC_CR_PLLRDY) == 0)
{
}
FLASH-》ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN
|FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;
RCC-》CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC-》CFGR |= RCC_CFGR_SW_PLL;
while ((RCC-》CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
{
}
}
else
{
}
}
這段代碼的大致流程是這樣的:先使能外部時鐘 HSE,等待 HSE 穩定之后,配置AHB,APB1,APB2 時鐘相關的分頻因子,也就是相關外設的時鐘。等待這些都配置完成之后,打開主PLL時鐘,然后設置主PLL作為系統時鐘 SYSCLK時鐘源。如果HSE 不能達到就緒狀態(比如外部晶振不能穩定或者沒有外部晶振),那么依然會是HSI作為系統時鐘。
在這里要特別提出來,在設置主PLL時鐘的時候,會要設置一系列的分頻系數和倍頻系數參數。大家可以從SetSysClock函數的這行代碼看出:
RCC-》PLLCFGR = PLL_M | (PLL_N 《《 6) | (((PLL_P 》》 1) -1) 《《 16) |
(RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q 《《 24);
這些參數是通過宏定義標識符的值來設置的。默認的配置在 System_stm32f4xx.c 文件開頭的地方配置。對于我們開發板,我們的設置參數值如下:
#define PLL_M 8
#define PLL_Q 7
#define PLL_N 336
#define PLL_P 2
所以我們的主PLL時鐘為:
PLL=8MHz * N/ (M*P)=8MHz* 336 /(8*2) = 168MHz
在開發過程中,我們可以通過調整這些值來設置我們的系統時鐘。
這里還有個特別需要注意的地方,就是我們還要同步修改 stm32f4xx.h 中宏定義標識符HSE_VALUE 的值為我們的外部時鐘:
#if !defined (HSE_VALUE)
#define HSE_VALUE ((uint32_t)8000000)
#endif
這里默認固件庫配置的是25000000,我們外部時鐘為8MHz,所以我們根據我們硬件情況修改為8000000即可。
講到這里,大家對 SystemInit 函數的流程會有個比較清晰的理解。那么 SystemInit 函數是怎么被系統調用的呢?SystemInit是整個設置系統時鐘的入口函數。這個函數對于我們使用ST提供的 STM32F4 固件庫的話,會在系統啟動之后先執行main函數,然后再接著執行SystemInit函數實現系統相關時鐘的設置。這個過程設置是在啟動文件 startup_stm32f40_41xxx.s中間設置的,我們接下來看看啟動文件中這段啟動代碼:
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
這段代碼的作用是在系統復位之后引導進入main函數,同時在進入main函數之前,首先
要調用 SystemInit系統初始化函數完成系統時鐘等相關配置。
最后我們總結一下SystemInit()函數中設置的系統時鐘大小:
SYSCLK(系統時鐘) =168MHz
AHB總線時鐘(HCLK=SYSCLK) =168MHz
APB1總線時鐘(PCLK1=SYSCLK/4) =42MHz
APB2總線時鐘(PCLK2=SYSCLK/2) =84MHz
PLL主時鐘 =168MHz
評論
查看更多