机制分析
上电后默认使用的是内部的HSI,一般工作在8MHz,且准度不可靠,标准库会调用启动代码和system_stm32f4xx.h/system_stm32f4xx.c中的相关函数将系统时钟选为HSE+PLL 168MHz。注意,若你没有使用标准库,而是在KEIL的 运行时环境设置 界面选择添加的 CORE和S文件,则不会切换时钟(不包含相关函数),而是默认使用HSI。
使用HSE和PLL的时钟路径如上图所示;
1.启动HSE,外部晶振结合内部电路起振,输出HSE时钟信号。
2.将PLL输入信号选为HSE。
3.HSE除以M后,输入PLL,应在1~2MHz之间,常用1MHz;注意,相关设置函数的注释中建议使用2MHz,理由是此时PLL的准确度会最好,但在实际使用中会导致后面的信号频率超出限制范围。
4.上一步的结果乘N=VCO,注意VCO也有范围要求,VCO/P=PLLCLK最大168MHz(更高会不稳定)。
5.启动PLL,等待PLL起振,工作稳定。
6.设置后续的AHB与APB总线频率(HCLK/PCLK),影响内核工作频率和外设频率等。
7.将PLLCLK选作SYSCLK,设置完成。
注意:在实际设置中,还会设置电源、FLASH相关寄存器,以使得时钟设置生效且能正常读写内存,后面有介绍。
代码分析
-stm32f4xx.h
#if defined(STM32F40_41xxx)
#if !defined (HSE_VALUE)
#define HSE_VALUE ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
HSE_VALUE应该等于开发板上外部晶振的工作频率,后面的程序会用到这个值进行计算,务必修改正确;
标准库默认值为25MHz,我个人所用为8MHz,应修改为8000000.
- system_stm32f4xx.c
#if defined(STM32F40_41xxx)
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */
#define PLL_M 25
#if defined (STM32F40_41xxx)
#define PLL_N 336
/* SYSCLK = PLL_VCO / PLL_P */
#define PLL_P 2
#endif /* STM32F40_41xxx */
用于确定M N P的值,设置PLL,修改时要注意每一步的频率限制和倍数限制。
具体参考手册6.3.2.
SystemInit_ExtMemCtl(void);
使用外置存储器是需要使用的函数,暂时不理会。
SystemInit(void)
系统初始化,主要是将RCC相关寄存器设为复位值,再调用时钟设置函数,完成任务。
SetSysClock();
主要的时钟设置操作都在里面,实现前面机制分析中的过程
/* 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)
启动HSE,并等待和判断它工作正常。
/* Select regulator voltage output Scale 1 mode */
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;
先选择了电源模块的工作模式,后修改了总线的分频系数。
参考手册5.1.3
运行模式,调压器为 1.2 V 域(内核、存储器和数字外设)提供全功率。在此模式下,
调压器的输出电压(约 1.2 V)可通过软件调整为不同的电压值:
— 对于 STM32F405xx/07xx 和 STM32F415xx/17xx
可通过 VOS(PWR_CR 寄存器的位 15)动态配置成级别 1 或级别 2。
参考手册3.4.1
注意:STM32F405xx/07xx 和 STM32F415xx/17xx 器件:
当 VOS =“0”时,fHCLK 最大值 = 144 MHz。
当 VOS =“1”时,fHCLK 最大值 = 168 MHz。
#if defined(STM32F40_41xxx)
/* Configure the main PLL */
RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
(RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
#endif
/* Enable the main PLL */
RCC->CR |= RCC_CR_PLLON;
/* Wait till the main PLL is ready */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
}
设置PLL相关参数,并启动,等待正常
#if defined(STM32F40_41xxx) || defined(STM32F412xG)
/* 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;
#endif /* STM32F40_41xxx || STM32F412xG */
修改FLASH存取控制寄存器
参考手册3.4.1中有表格参考。
/* 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);
{
}
将PLLCLK切换为系统时钟,并等待成功。
最后发现,在相关函数中,默认使用HSE和PLL的组合,没有使用RCC.c和.h中的任何代码,所以想要使用其他时钟设置的话,需要自己写代码,使用c h 文件可以帮助你;同时,在不借助标准外设库代码的情况下,以上三个文件也可以正常使用。