STM32F4时钟系统初始化是在system_stm32f4xx.c中的SystemInit()函数中完成的。对于系统时钟关键寄存器设置主要是在SystemInit函数中调用SetSysClock()函数来设置的。我们可以先看看SystemInit()函数体:
//@brief Setup the microcontroller system
// Initialize the Embedded Flash Interface, the PLL and update the
// SystemFrequency variable.
//@param None
//@retval None
void SystemInit(void)
{
// FPU settings ------------------------------------------------------------
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); // set CP10 and CP11 Full Access
#endif
// Reset the RCC clock configuration to the default reset state ------------
// Set HSION bit
RCC->CR |= (uint32_t)0x00000001;
// Reset CFGR register
RCC->CFGR = 0x00000000;
// Reset HSEON, CSSON and PLLON bits
RCC->CR &= (uint32_t)0xFEF6FFFF;
// Reset PLLCFGR register
RCC->PLLCFGR = 0x24003010;
// Reset HSEBYP bit
RCC->CR &= (uint32_t)0xFFFBFFFF;
// Disable all interrupts
RCC->CIR = 0x00000000;
#ifdef DATA_IN_ExtSRAM
SystemInit_ExtMemCtl();
#endif // DATA_IN_ExtSRAM
// Configure the System clock source, PLL Multiplier and Divider factors,
AHB/APBx prescalers and Flash settings ----------------------------------
SetSysClock();
// Configure the Vector Table location add offset address ------------------
#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
}
SystemInit函数开始先进行浮点运算单元设置,然后是复位PLLCFGR,CFGR寄存器,同时通过设置CR寄存器的HSI时钟使能位来打开HSI时钟。默认情况下如果CFGR寄存器复位,那么是选择HSI作为系统时钟,这点大家可以查看RCC->CFGR寄存器的位描述最低2位可以得知,当低两位配置为00的时候(复位之后),会选择HSI振荡器为系统时钟。也就是说,调用SystemInit函数之后,首先是选择HSI作为系统时钟。
在设置完相关寄存器后,接下来SystemInit函数内部会调用SetSysClock函数。这个函数比较长,我们就把函数一些关键代码行截取出来给大家讲解一下。这里我们省略一些宏定义标识符值的判断而直接把针对STM32F407比较重要的内容贴出来:
//@brief Configures the System clock source, PLL Multiplier and Divider factors,
// AHB/APBx prescalers and Flash settings
//@Note This function should be called only once the RCC clock configuration
// is reset to the default reset state (done in SystemInit() function).
//@param None
//@retval None
static void SetSysClock(void)
{
//////////////////////////////////////////////////////////////////////////
// PLL (clocked by HSE) used as System clock source //
//////////////////////////////////////////////////////////////////////////
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
// Enable HSE
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
// Wait till HSE is ready and if Time out is reached exit
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)
{
// Select regulator voltage output Scale 1 mode, System frequency up to 168 MHz
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR |= PWR_CR_VOS;
// HCLK = SYSCLK / 1
RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
// PCLK2 = HCLK / 2
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
// PCLK1 = HCLK / 4
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
// Configure the main PLL
RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
(RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
// Enable the main PLL
RCC->CR |= RCC_CR_PLLON;
// Wait till the main PLL is ready
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
}
// Configure Flash prefetch, Instruction cache, Data cache and wait state
FLASH->ACR = FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;
// Select the main PLL as system clock source
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= RCC_CFGR_SW_PLL;
// Wait till the main PLL is used as system clock source
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
{
}
}
else
{
// If HSE fails to start-up, the application will have wrong clock
// configuration. User can add here some code to deal with this error
}
}
这段代码的大致流程是这样的:先使能外部时钟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) //Value of the External oscillator in Hz
#endif // HSE_VALUE
这里默认固件库配置的是25000000,我们外部时钟为8MHz,所以我们根据我们硬件情况修改为8000000即可。
最后我们总结一下SystemInit()函数中设置的系统时钟大小:
SYSCLK(系统时钟) =168MHz
AHB总线时钟(HCLK=SYSCLK) =168MHz
APB1总线时钟(PCLK1=SYSCLK/4) =42MHz
APB2总线时钟(PCLK2=SYSCLK/2) =84MHz
PLL主时钟 =168MHz
上一篇:STM32F10x利用定时器测量脉冲宽度
下一篇:stm32f4 DMA任意长度buffer无停顿传输
推荐阅读最新更新时间:2024-03-16 16:16