时钟树
一图胜千言。照例先上一张时钟树的图。
来自:RM0090,STM32F40xxx、STM32F41xxx、STM32F42xxx、STM32F43xxx参考手册
配置时钟流程
- 使能外部高速时钟(HSE)
- 等待HSE时钟信号稳定
- 配置锁相环PLL
- 等待PLL就绪
- 选择PLL作为主时钟源(配置SW)
- 等待系统时钟切换完成(检查SWS)
- 配置外设总线的分频系数
具体配置方法
官方提供了三种配置方法。
KEIL-MDK4使用寄存器配置
参考文档:
C:\Keil\ARM\Startup\ST\STM32F4xx\startup_stm32f40xx.s
C:\Keil\ARM\Startup\ST\STM32F4xx\system_stm32f4xx.c
复位后,CPU执行Reset_Handler函数。在startup_stm32f40xx.s文件中。
先执行SystemInit(),再执行__main()。在__main()函数中会跳转到用户的main()函数。
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
在SystemInit()函数中,会调用SetSysClock()函数。
/* Configure the System clock source, PLL Multiplier and Divider factors,
AHB/APBx prescalers and Flash settings ----------------------------------*/
SetSysClock();
SetSysClock()函数的官方标准模板为:
/**
* @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 */ // 使能外部高速时钟HSE
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */ // 等待HSE时钟信号稳定
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启动成功
}
else
{
HSEStatus = (uint32_t)0x00; // HSE启动失败
}
if (HSEStatus == (uint32_t)0x01) // HSE成功
{
/* 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_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;
/* Select the main PLL as system clock source */ // 选择PLL作为系统时钟源
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 */
}
}
关于主锁相环的配置,有以下参数:
/************************* PLL Parameters *************************************/
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */
#define PLL_M 25
#define PLL_N 336
/* SYSCLK = PLL_VCO / PLL_P */
#define PLL_P 2
/* USB OTG FS, SDIO and RNG Clock = PLL_VCO / PLLQ */
#define PLL_Q 7
/******************************************************************************/
KEIL-MDK5使用LL外设驱动配置
在KEIL5的标准模板中,时钟是在main()函数中配置的。
参考文档:
C:\Keil_v5\ARM\Packs\Keil\STM32F4xx_DFP\2.14.0\MDK\Templates_LL\Src\main.c
main()函数:
/**
* @brief Main program
* @param None
* @retval None
*/
int main(void)
{
/* Configure the system clock to 168 MHz */
SystemClock_Config();
SystemCoreClockUpdate();
/* Add your application code here */
#ifdef RTE_CMSIS_RTOS2
/* Initialize CMSIS-RTOS2 */
osKernelInitialize ();
/* Create thread functions that start executing,
Example: osThreadNew(app_main, NULL, NULL); */
/* Start thread execution */
osKernelStart();
#endif
/* Infinite loop */
while (1)
{
}
}
main()函数的第一件事就是配置系统时钟:
/**
* @brief System Clock Configuration
* The system Clock is configured as follow :
* System Clock source = PLL (HSE)
* SYSCLK(Hz) = 168000000
* HCLK(Hz) = 168000000
* AHB Prescaler = 1
* APB1 Prescaler = 4
* APB2 Prescaler = 2
* HSE Frequency(Hz) = 8000000
* PLL_M = 8
* PLL_N = 336
* PLL_P = 2
* VDD(V) = 3.3
* Main regulator output voltage = Scale1 mode
* Flash Latency(WS) = 5
* @param None
* @retval None
*/
void SystemClock_Config(void)
{
/* Enable HSE oscillator */
LL_RCC_HSE_Enable();
while(LL_RCC_HSE_IsReady() != 1)
{
};
/* Set FLASH latency */
LL_FLASH_SetLatency(LL_FLASH_LATENCY_5);
/* Main PLL configuration and activation */
LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSE, LL_RCC_PLLM_DIV_8, 336, LL_RCC_PLLP_DIV_2);
LL_RCC_PLL_Enable();
while(LL_RCC_PLL_IsReady() != 1)
{
};
/* Sysclk activation on the main PLL */
LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL)
{
};
/* Set APB1 & APB2 prescaler */
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_4);
LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_2);
/* Set systick to 1ms */
SysTick_Config(168000000 / 1000);
/* Update CMSIS variable (which can be updated also through SystemCoreClockUpdate function) */
SystemCoreClock = 168000000;
}
注意:官方例程中开发板使用的外部晶振是8M的,PLL的预分频系数M是8,倍频系数N是336,再2分频到得168M。
如果外部晶振是25M的,则需要更改M系数为25.
KEIL-MDK5使用HAL外设驱动配置
文档模板:
参考文档:
C:\Keil_v5\ARM\Packs\Keil\STM32F4xx_DFP\2.14.0\MDK\Templates\Src\main.c
与LL配置方法类似,也是在main()函数中,HAL_Init()之后就调用SystemClock_Config()来配置系统时钟。
/**
* @brief System Clock Configuration
* The system Clock is configured as follow :
* System Clock source = PLL (HSE)
* SYSCLK(Hz) = 168000000
* HCLK(Hz) = 168000000
* AHB Prescaler = 1
* APB1 Prescaler = 4
* APB2 Prescaler = 2
* HSE Frequency(Hz) = 8000000
* PLL_M = 25
* PLL_N = 336
* PLL_P = 2
* PLL_Q = 7
* VDD(V) = 3.3
* Main regulator output voltage = Scale1 mode
* Flash Latency(WS) = 5
* @param None
* @retval None
*/
static void SystemClock_Config(void)
{
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_OscInitTypeDef RCC_OscInitStruct;
/* Enable Power Control clock */
__HAL_RCC_PWR_CLK_ENABLE();
/* The voltage scaling allows optimizing the power consumption when the device is
clocked below the maximum system frequency, to update the voltage scaling value
regarding system frequency refer to product datasheet. */
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/* Enable HSE Oscillator and activate PLL with HSE as source */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 25;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 7;
if(HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
/* Initialization Error */
Error_Handler();
}
/* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
clocks dividers */
RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if(HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
{
/* Initialization Error */
Error_Handler();
}
/* STM32F405x/407x/415x/417x Revision Z devices: prefetch is supported */
if (HAL_GetREVID() == 0x1001)
{
/* Enable the Flash prefetch */
__HAL_FLASH_PREFETCH_BUFFER_ENABLE();
}
}
/**
* @brief This function is executed in case of error occurrence.
* @param None
* @retval None
*/
static void Error_Handler(void)
{
/* User may add here some code to deal with this error */
while(1)
{
}
}
后两种方法,需要勾选RCC等组件:
需要引用的文件有: