学前概念须知:
STM32F429有5条总线 越高时钟越快由低到高分别是 :
APB1 -->APB2–>AHB1–>AHB2–>AHB3
HSE: 高速的外部时钟,板子采用无源晶振,设置为25M,精度较高,一般配置为这个。
HSI: 高速的内部时钟,为16M,当HSE故障时,自动切换到HSI,直到HSE启动.
PLLCLK:锁相环时钟 HSE->PLL->倍频到180M M/N/P
系统时钟:来源 HSI HSE PLLCLK 控制RCC_CFGR时钟配置寄存器的SW位 配置为10PLLCLK
HCLK:【 AHB高速总线时钟】,最高180M,为AHB总线外设提供 [important]
AHB: advanced high-performance bus 【高性能总线】
FCLK:free clock 内核CPU自由时钟
RTC: real time clock,实时时钟,为芯片内部的RTC提供时钟,具体看功能框图
下面都是由低到高速,对应5条总线的时钟 ,后面是分频因子
说明:
1–6部分为主系统时钟,A~F为外设和其他时钟
图中最高为168MHZ有误,应改为180MHZ
系统时钟配置流程:
static void SetSysClock(void)
选择HSE打开,默认25M -->
进入锁相环/M=25 ,最终为1M–>
设置倍频因子xN = 360M,VCO默认1 最终为360M–>
/ P设置为2 最终360/2=180M–>
选择PLLCLK作为系统时钟,不选HSI/HSE–>
设置1倍分频,得HCLK 360M–>
设置倍频因子为8,360/8=45M,最后得到APB1 45MHZ
设置倍频因子为8,360/4=90M,最后得到APB2 90MHZ
其他外设的时钟需要用到的时候再进行配置,这里不作说明。
总结:
先配置好HSI/HSEPLL振荡器作为PLL时钟源,并配置分频系数M/N/P/Q
A,B,C,D,F时钟在需要的时候才需要配置。
可通过MCO时钟输出来检测自己配置的PLLCLK在示波器显示,来判断是否成功配置为180MHZ。
1-使能HSE,并等待HSE稳定
2-配置 AHB APB2 APB1 总线的预分频因子
3-配置 PLL的各种分频因子,并使能PLL
4-选择系统时钟来源
想要配置自己想要的时钟,可以直接修改提供的SetSysClock函数,不过为了保证库函数的完整性,我们可以通过固件库来自己实现一个,一般不选用HSI,精确度没有HSE的高。下面程序采用条件编译的方式,#if 0#else #endif 部分是从系统自带的时钟配置函数裁剪下来的部分。
bsp_rccclkconfig.c
#include "bsp_rccclkconfig.h"
#if 0
static void SetSysClock(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/*-------------------------1-使能HSE,并等待HSE稳定----------------------------------------*/
/* 使能 HSE */
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
/* 等待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;
}
else
{
HSEStatus = (uint32_t)0x00;
}
/* HSE启动成功 */
if (HSEStatus == (uint32_t)0x01)
{
/*----------------------------------------------------------------------------------------*/
/* 选择电压调节器输出为模式1 */
/* 使能电源接口时钟 */
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR |= PWR_CR_VOS;
/*----------------------------2-配置 AHB APB2 APB1 总线的预分频因子-----------------------*/
/* 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;
/*----------------------------3-配置 PLL的各种分频因子,并使能PLL-----------------------*/
/* 配置 主 PLL */
RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
(RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
/* 使能 PLL */
RCC->CR |= RCC_CR_PLLON;
/* 等待PLL启动稳定 */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
}
/*----------------------------------------------------------------------------------------*/
/* 打开OVER-Drive模式,为的是达到更高的频率 */
PWR->CR |= PWR_CR_ODEN;
while((PWR->CSR & PWR_CSR_ODRDY) == 0)
{
}
PWR->CR |= PWR_CR_ODSWEN;
while((PWR->CSR & PWR_CSR_ODSWRDY) == 0)
{
}
/* 配置 Flash 预取指, 指令缓存, 数据缓存 和等待周期 */
FLASH->ACR = FLASH_ACR_PRFTEN |
FLASH_ACR_ICEN |
FLASH_ACR_DCEN |
FLASH_ACR_LATENCY_5WS;
/*----------------------------4-选择系统时钟来源-----------------------------------------*/
/* 选择主锁相环时钟作为系统时钟 */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= RCC_CFGR_SW_PLL;
/* 等待PLLCLH切换称系统时钟 */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
{
}
}
else
{ /* HSE启动失败后,用户纠错的代码 */
}
}
#endif
取值范围:
// m : 2~63
// n :192~432
// p :2、4、6、8
// q :2~15
// SYSCLK = (HSE/m) * n /p = 25/25 * 432 / 2 = 216M
// HSE_SetSysClk(25,432,2,7)
void HSE_SetSysClk(uint32_t m,uint32_t n,uint32_t p,uint32_t q)
{
__IO uint32_t HSEStartUpStatus = 0;
/* 使能HSE 并等待HSE稳定*/
RCC_HSEConfig(RCC_HSE_ON);
HSEStartUpStatus = RCC_WaitForHSEStartUp();
if( HSEStartUpStatus == SUCCESS )
{
/* 选择电压调节器输出为模式1 */
/* 使能电源接口时钟 */
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR |= PWR_CR_VOS;
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK2Config(RCC_HCLK_Div2);
RCC_PCLK1Config(RCC_HCLK_Div4);
RCC_PLLConfig(RCC_PLLSource_HSE, m, n, p, q);
RCC_PLLCmd(ENABLE);
while ( ( RCC_GetFlagStatus(RCC_FLAG_PLLRDY) ) == RESET )
{
}
/* 打开OVER-Drive模式,为的是达到更高的频率 */
PWR->CR |= PWR_CR_ODEN;
while((PWR->CSR & PWR_CSR_ODRDY) == 0)
{
}
PWR->CR |= PWR_CR_ODSWEN;
while((PWR->CSR & PWR_CSR_ODSWRDY) == 0)
{
}
/* 配置 Flash 预取指, 指令缓存, 数据缓存 和等待周期 */
FLASH->ACR = FLASH_ACR_PRFTEN |
FLASH_ACR_ICEN |
FLASH_ACR_DCEN |
FLASH_ACR_LATENCY_5WS;
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while ( ( RCC_GetSYSCLKSource() ) != 0X08 )
{
}
}
else{ /* HSE启动识别,用户在这里添加纠错代码 */ }
}
/*
*以下部分是配置MCO引脚输出的配置函数
*/
// MCO1 PA8 GPIO 初始化
void MCO1_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
// MCO1 GPIO 配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
// MCO2 PC9 GPIO 初始化
void MCO2_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
// MCO2 GPIO 配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
bsp_rccclkconfig.h
#ifndef __BSP_RCCCLKCONFIG_H
#define __BSP_RCCCLKCONFIG_H
#include "stm32f4xx_rcc.h"
void HSE_SetSysClk(uint32_t m,uint32_t n,uint32_t p,uint32_t q);
#endif /*__BSP_RCCCLKCONFIG_H*/
main.c
#include "stm32f4xx.h"
#include "bsp_rccclkconfig.h"
int main(void)
{
// 程序来到main函数之前,启动文件已经调用SystemInit()函数把系统时钟初始化成180MHZ
// SystemInit()在system_stm32f4xx.c中定义,如果想修改系统时钟,可自行编写程序修改
// 重新设置系统时钟,这时候可以选择使用HSE还是HSI
/* 把系统时钟初始化为216M */
HSE_SetSysClk(25,432,2,9);
// MCO1 输出PLLCLK
RCC_MCO1Config(RCC_MCO1Source_PLLCLK, RCC_MCO1Div_1);
while (1);
}