环境:STM32F207
内容:SetSysClock STM32F207使用固件库,怎样设置系统时钟
在实际讲函数之前让我们来看看这个函数的功能备注:
/**
* @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倍频器和分频器因子,AHB/APBX预分频器和FLASH相关设置
注意:这个函数只能够被调用一次,那就是在systemInit函数里面
注意:如果能够看懂下面这个函数做了什么,那么您对STM32的时钟树已经掌握了。不敢说是完全掌握,至少知道STM32时钟树是怎么一回事。
在说之前我们先说说频率最大值:
这里说了三个时钟AHB,APB2,APB1
AHB最大频率120MHZ
APB2最大频率60MHZ
APB1最大频率30MHZ
源代码如下:
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)
{
/* 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_3WS; //⑨
/* Select the main PLL as system clock source */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); //⑩
RCC->CFGR |= RCC_CFGR_SW_PLL; // (11)
/* Wait till the main PLL is used as system clock source */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL); //(12)
{
}
}
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 */
}
}
①
/* Enable HSE */
RCC->CR |= ((uint32_t)RCC_CR_HSEON);// ①
这个地方的作用是打开外部晶振,让其起振。
就如同上面所述,大概意思就是可以通过程序打开或者关闭,但是当CPU进入待机模式之后,硬件会自动停止外部晶振震动,可能是考虑到了功耗原因吧,然后就是
当我们直接或者间接使用了HSE作为系统时间,我们就不能够将这个设置成0.
②
/* 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));
单从字面上理解的话,我们就知道,这里的意思是等待外部晶振起振,并且稳定下来。如下:
这个地方大概意思就是等待HSE稳定,其实这里我有个疑问,希望读者在评论中说明下,就是这里:After the HSION bit is cleared,HSERDY goes low after 6 ....
问题就是HSI什么时候清零。
③
/* HCLK = SYSCLK / 1*/
RCC->CFGR |= RCC_CFGR_HPRE_DIV1;//③
在stm32f2xx.h里面定义有
#define RCC_CFGR_HPRE_DIV1 ((uint32_t)0x00000000) /*!< SYSCLK not divided */
我们再看手册是否正确
也就是说STM32时钟树里面的AHB时钟不会被分频,那代表什么意思呢?如下图:
例如SYSCLK最后是120MHZ,那么AHB出来之后,还是120MHZ,这就是不分频的意思。
④
/* PCLK2 = HCLK / 2*/
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;//④
这里对CFGR做了操作,同样在stm32f2xx.h里面
#define RCC_CFGR_PPRE2_DIV2 ((uint32_t)0x00008000) /*!< HCLK divided by 2 */
这里将第十六位bit15写成了1,bit15含义如下:
上面两张图的大概意思就是PPRE2就是设置外设高速时钟APB2的,其中bit15=1,100,就是说APB2被AHB 2分频,时钟树如下:
加入SYSCLK=120,那么经过AHB之后,到达APB2前端,时钟=120,经过PPRE2之后,APB2=60M,当然,这是外设最高时钟。
⑤
/* PCLK1 = HCLK / 4*/
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;//⑤
同上,这里是设置APB1的,APB2是高速外设,时钟最大=60M,APB1是高速时钟,时钟最大=30M。
在stm32f2xx.h里面有
#define RCC_CFGR_PPRE1_DIV4 ((uint32_t)0x00001400) /*!< HCLK divided by 4 */
这里将第11位和第13位写成1,也就是bit10和bit12是1,如下
PPRE1值也就应该是101,如下图
同上面时钟树里面也就是说,APB1=SYSCLK/4=120 / 4 = 30M
⑥
/* Configure the main PLL */
RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
(RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);//⑥
这里看起来有很多东西,先捋一捋:
我们先看看PLL_N,PLL_P,PLL_Q,PLL_M分别代表什么意思:
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */
#define PLL_M 25
#define PLL_N 240
/* SYSCLK = PLL_VCO / PLL_P */
#define PLL_P 2
/* USB OTG FS, SDIO and RNG Clock = PLL_VCO / PLLQ */
#define PLL_Q 5
如同上面所说:
系统时钟SYSCLK=PLL_VCO,
PLL_VCO=HSE / PLL_M * PLL_N
如果我们要让SYSCLK=120M,那么这几个值该怎么设置呢?如果我们每个都去假设,那可能是得不偿失的,这里有个方法,先看下面公式:
PLL_VCO=(HSE/PLL_M)*PLL_N。这里我们可以先让括号里面的值=1M,那么后面的计算就很简单了,这是一种很实用的方法,大家都是设置寄存器,为什么我们不找最简单的方式呢。
如果我们HSE=25M ,那么1M=25/PLL_M ,所以PLL_M=25,现在我们要得到120M的系统时钟,先下面:
SYSCLK=PLL_VCO/PLL_P;
这个PLLP的值只有这么几种,也就是说,我们最好把PLL_VCO设置成一个很特别的值,比如240M,那么我们PLLP=2就解决问题了。
好了,接下来怎么做,我就不说了,其他就很简单了,如果有需要的帮助的同学后面还是不懂,可以留言。但是FLASH那里我还没有看是怎么回事,所以暂时也不是很清楚。
OK,这是讲解STM32F207时钟相关的最后一篇文章,后面的文章主要是针对部分外设来展开。