主要参考正点原子数据手册和源码资料等。
第19讲 STM32时钟系统精讲_哔哩哔哩_bilibili
此处记录较为重要或者较易出错的一些遗漏之处,作为补充。
一般而言,时钟越高,速度越快,但同时抗干扰能力也越差,功耗也越高,需要根据实际情况来选择合适的时钟频率。
任何一个外设在使用前,都必须先使能其时钟。
这一部分的时钟是用来给USB、OTG、FS、随机数发生器、SDIO这些外设使用的。
重要寄存器
固件库中,对寄存器的定义都在文件 stm32f4xx.h中,这个文件接近1万行。
比如,对时钟相关寄存器的结构体封装。
这里面涉及到的寄存器有复位和时钟相关的,而且,包含F4系列所有的寄存器,我们这里用的是407,所以并不会全部用到。
我们常用的有:
6.3.1 RCC 时钟控制寄存器 (RCC_CR)—主要是用于使能时钟源的,一般是使能某个时钟源,然后等待就绪。比如:
6.3.2 RCC PLL 配置寄存器 (RCC_PLLCFGR)—是用来配置时钟里的两个PLL的
6.3.3 RCC 时钟配置寄存器 (RCC_CFGR)—主要是用来配置分频系数和选择时钟源的(注意区分使能时钟源,要先选择,然后使能,然后等待)。
接下来5个都是使能特定外设的,可自行查看参考手册。
6.3.12 RCC AHB1 外设时钟使能寄存器 (RCC_AHB1ENR)
6.3.13 RCC AHB2 外设时钟使能寄存器 (RCC_AHB2ENR)
6.3.14 RCC AHB3 外设时钟使能寄存器 (RCC_AHB3ENR)
6.3.15 用于 STM32F405xx/07xx 和 STM32F415xx/17xx 的 RCC APB1 外设时钟使能寄存器 (RCC_APB1ENR)
6.3.17 用于 STM32F405xx/07xx 和 STM32F415xx/17xx 的 RCC APB2 外设时钟使能寄存器 (RCC_APB2ENR)
相关库函数
其实还有一些外设使能函数,具体可自行查看库文件。
另外有个关键的地方。
实际开发中,很少会用到这部分的函数。因为固件库提供的系统初始化函数里,就可以完成时钟部分的配置。
时钟配置之SystemInit函数
注意:该函数是库函数提供的,如果没有使用库函数,是没有的。
先看启动代码,文件:startup_stm32f4xx.s
可以看出,在进入main函数之前,系统调用了SystemInit函数。
SystemInit函数在system_stm32f4xx.c中,函数定义如下
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;
#if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */
/* 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
}
一开始,对时钟进行了一些初始化,比如先打开HSI,关闭一些功能位,具体可对照寄存器理解其含义。
之后,就会到达一个时钟配置的核心函数SetSysClock(),也在文件system_stm32f4xx.c中
在SetSysClock函数中,配置了系统时钟,PLL倍频以及分频系数。
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的参数,选择HSE作为其时钟源,并根据M N P三个的值来计算PLL的输出时钟。而M N P这三个值都在文件中有对应宏定义:
/************************* PLL Parameters *************************************/
#if defined (STM32F40_41xxx) || defined (STM32F427_437xx) || defined (STM32F429_439xx) || defined (STM32F401xx)
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */
#define PLL_M 8
#else /* STM32F411xE */
#if defined (USE_HSE_BYPASS)
#define PLL_M 8
#else /* STM32F411xE */
#define PLL_M 16
#endif /* USE_HSE_BYPASS */
#endif /* STM32F40_41xxx || STM32F427_437xx || STM32F429_439xx || STM32F401xx */
/* USB OTG FS, SDIO and RNG Clock = PLL_VCO / PLLQ */
#define PLL_Q 7
#if defined (STM32F40_41xxx)
#define PLL_N 336
/* SYSCLK = PLL_VCO / PLL_P */
#define PLL_P 2 //2 //2---168M 4---84M
#endif /* STM32F40_41xxx */
#if defined (STM32F427_437xx) || defined (STM32F429_439xx)
#define PLL_N 360
/* SYSCLK = PLL_VCO / PLL_P */
#define PLL_P 2
#endif /* STM32F427_437x || STM32F429_439xx */
#if defined (STM32F401xx)
#define PLL_N 336
/* SYSCLK = PLL_VCO / PLL_P */
#define PLL_P 4
#endif /* STM32F401xx */
#if defined (STM32F411xE)
#define PLL_N 400
/* SYSCLK = PLL_VCO / PLL_P */
#define PLL_P 4
#endif /* STM32F411xx */
/******************************************************************************/
根据公式,可以算出PLL输出时钟为:8/8*336/2=128MHz
接着,选择PLL作为系统时钟源。
至此,时钟已经配置完毕。
注意事项:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)