LinkedInSTM32F4 時鐘系統(tǒng)初始化是在system_stm32f4xx.c中的 SystemInit()函數(shù)中完成的。 對于系統(tǒng)時鐘關(guān)鍵寄存器設(shè)置主要是在 SystemInit 函數(shù)中調(diào)用 SetSysClock()函數(shù)來設(shè)置的。我們可以先看看 SystemInit ()函數(shù)體:
?
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函數(shù)開始先進(jìn)行浮點(diǎn)運(yùn)算單元設(shè)置,然后是復(fù)位PLLCFGR,CFGR寄存器,同時通過設(shè)置 CR 寄存器的 HSI 時鐘使能位來打開 HSI 時鐘。默認(rèn)情況下如果 CFGR 寄存器復(fù)位,那么是選擇HSI作為系統(tǒng)時鐘,這點(diǎn)大家可以查看RCC-》CFGR 寄存器的位描述最低2位可以得知,當(dāng)?shù)蛢晌慌渲脼?00的時候(復(fù)位之后),會選擇 HSI振蕩器為系統(tǒng)時鐘。也就是說,調(diào)用 SystemInit 函數(shù)之后,首先是選擇 HSI 作為系統(tǒng)時鐘。
在設(shè)置完相關(guān)寄存器后,接下來SystemInit函數(shù)內(nèi)部會調(diào)用 SetSysClock函數(shù)。這個函數(shù)比較長,我們就把函數(shù)一些關(guān)鍵代碼行截取出來給大家講解一下。這里我們省略一些宏定義標(biāo)識符值的判斷而直接把針對STM32F407 比較重要的內(nèi)容貼出來:
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 穩(wěn)定之后,配置AHB,APB1,APB2 時鐘相關(guān)的分頻因子,也就是相關(guān)外設(shè)的時鐘。等待這些都配置完成之后,打開主PLL時鐘,然后設(shè)置主PLL作為系統(tǒng)時鐘 SYSCLK時鐘源。如果HSE 不能達(dá)到就緒狀態(tài)(比如外部晶振不能穩(wěn)定或者沒有外部晶振),那么依然會是HSI作為系統(tǒng)時鐘。
在這里要特別提出來,在設(shè)置主PLL時鐘的時候,會要設(shè)置一系列的分頻系數(shù)和倍頻系數(shù)參數(shù)。大家可以從SetSysClock函數(shù)的這行代碼看出:
RCC-》PLLCFGR = PLL_M | (PLL_N 《《 6) | (((PLL_P 》》 1) -1) 《《 16) |
(RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q 《《 24);
這些參數(shù)是通過宏定義標(biāo)識符的值來設(shè)置的。默認(rèn)的配置在 System_stm32f4xx.c 文件開頭的地方配置。對于我們開發(fā)板,我們的設(shè)置參數(shù)值如下:
#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
在開發(fā)過程中,我們可以通過調(diào)整這些值來設(shè)置我們的系統(tǒng)時鐘。
這里還有個特別需要注意的地方,就是我們還要同步修改 stm32f4xx.h 中宏定義標(biāo)識符HSE_VALUE 的值為我們的外部時鐘:
#if !defined (HSE_VALUE)
#define HSE_VALUE ((uint32_t)8000000)
#endif
這里默認(rèn)固件庫配置的是25000000,我們外部時鐘為8MHz,所以我們根據(jù)我們硬件情況修改為8000000即可。
講到這里,大家對 SystemInit 函數(shù)的流程會有個比較清晰的理解。那么 SystemInit 函數(shù)是怎么被系統(tǒng)調(diào)用的呢?SystemInit是整個設(shè)置系統(tǒng)時鐘的入口函數(shù)。這個函數(shù)對于我們使用ST提供的 STM32F4 固件庫的話,會在系統(tǒng)啟動之后先執(zhí)行main函數(shù),然后再接著執(zhí)行SystemInit函數(shù)實(shí)現(xiàn)系統(tǒng)相關(guān)時鐘的設(shè)置。這個過程設(shè)置是在啟動文件 startup_stm32f40_41xxx.s中間設(shè)置的,我們接下來看看啟動文件中這段啟動代碼:
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
這段代碼的作用是在系統(tǒng)復(fù)位之后引導(dǎo)進(jìn)入main函數(shù),同時在進(jìn)入main函數(shù)之前,首先
要調(diào)用 SystemInit系統(tǒng)初始化函數(shù)完成系統(tǒng)時鐘等相關(guān)配置。
最后我們總結(jié)一下SystemInit()函數(shù)中設(shè)置的系統(tǒng)時鐘大小:
SYSCLK(系統(tǒng)時鐘) =168MHz
AHB總線時鐘(HCLK=SYSCLK) =168MHz
APB1總線時鐘(PCLK1=SYSCLK/4) =42MHz
APB2總線時鐘(PCLK2=SYSCLK/2) =84MHz
PLL主時鐘 =168MHz
評論
查看更多