STM32F407RCC配置.docx
《STM32F407RCC配置.docx》由会员分享,可在线阅读,更多相关《STM32F407RCC配置.docx(16页珍藏版)》请在冰豆网上搜索。
STM32F407RCC配置
RCC(ResetandClockControl)配置
这里介绍RCC的时钟控制功能
在STM32F103上,由于小组所有的板子都使用用同样的芯片,同样的晶振,以及同样的库函数,即使我们不去理解RCC,仍然可以将大多数功能调试出来。
但如果使用不同型号的芯片,例如用STM32F407与STM32103进行通信,如果不去弄清楚RCC,在调试中可能会遇到麻烦。
下面就我调试STM32F407的这段时间,介绍一下RCC的部分功能。
文档的前半部分是关于RCC的部分功能描述,后半部分是关于库函数的使用。
时钟结构
(原图请参考STM32F407参考手册RCC部分)
STM32F407最高层是SYSCLK系统时钟,由其生成了AHB时钟,再由AHB时钟生成APB时钟。
SYSCLK系统时钟可以由3个基本的时钟源获得:
HSE(外部高速晶振)或HSI(内部高速晶振)或PLL锁相环倍频。
例如:
板子上焊了8MHz的晶振,则HSE=8MHz。
如果焊了25MHz的,则HSE=25MHz。
HSI是芯片内部自带的晶振,其大小由芯片型号决定,如STM32F407的HSI是16MHz。
PLL倍频的功能是:
将HSE或HSI的频率放大,最大可以放大到168MHz.
SYSCLK系统时钟可以由HSE/HSI/PLL提供。
例如使用库函数:
RCC_SYSCLKConfig(RCC_SYSCLKSource_HSE);代表用HSE外部高速晶振作为系统时钟源。
如果HSE=8MHz,则SYSCLK=8M,即STM32F407就会运行在8M的速度;
如果HSE=25M,则SYSCLK=25M,即STM32F407就会运行在25M。
RCC_SYSCLKConfig(RCC_SYSCLKSource_HSI);代表用HSI内部高速晶振作为系统时钟源,
如果HSI=16M,则SYSCLK=16M,即STM32F407就会运行在16M的速度。
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);代表用PLL时钟作为系统时钟源。
如果配置PLL倍频至168M,则SYSCLK=168M,即STM32F407会运行在168M;
如果倍频至144M,则SYSCLK=144M,STM32F407就会运行在144M;
如果倍频至72M,STM32F407也会像STM32F103运行在72M;
从SYSCLK,紧接着分得AHB时钟(也叫HCLK)。
假设此时我们已经选择PLL作为系统时钟源,且PLL倍频至168M,即SYSCLK=168M。
那么AHB的最高频率就为168MHz。
方框底部的数字/1,2...512代表AHB时钟预分频数,范围1~512,
如果是1,即HCLK为SYSCLK的1分频,HCLK=SYSCLK/1=168/1=168MHz
如果是2,即HCLK为SYSCLK的2分频,HCLK=SYSCLK/2=168/2=84MHz
注意,HCLK是受SYSCLK约束的,
如果我们配置SYSCLK=144M,那么此时HCLK最大只能达到144MHz。
将AHB时钟继续分割,得到的是APB时钟(也叫PCLK)。
假设在此之前,SYSCLK=168M,SYSCLK2分频得HCLK=84M。
(HCLK代表AHB时钟)
与前面AHB时钟类似,方框底部的数字代表预分频数,范围1~16.
如果是1,即PCLK(APB时钟)为HCLK的1分频,PCLK=HCLK/1=84/1=84MHz
如果是2,即PCLK为HCLK的2分频,PCLK=HCLK/2=84/2=42MHz。
APB时钟可以继续分给APB总线上的外设,如果继续配置外设的预分频,可以进一步修改外设的时钟频率。
例如:
配置CAN总线。
第一步:
利用PLL,我们配置系统时钟SYSCLK=168MHz。
第二步:
此时已有SYSCLK=168MHz。
设置AHB分频数为2,即得:
HCLK=SYSCLK/2=84MHz。
第三步:
此时已有HCLK=84MHz。
设置APB分频数为2,即得:
PCLK=HCLK/2=42MHz。
第四步:
此时已有PCLK=42MHz。
设置CanInitStruct.CAN_Prescaler=7,即得CAN时钟=PCLK/7=6MHz。
这样,正确配置了CAN的时钟,配置CAN时才能计算出实际的波特率,从而实现通现。
我们借助CAN总线的例子继续说明RCC的作用。
接着上面的例子,例如:
我们使用STM32F407与STM32F103进行CAN通信,且到这一步已经保证所有的配置都正确,波特率为1M/s。
其中STM32F407与STM32103的CAN都配置成:
CAN_InitStruct.CAN_Prescaler=2;
CAN_InitStruct.CAN_SJW=CAN_SJW_1tq;
CAN_InitStruct.CAN_BS1=CAN_BS1_9tq;
CAN_InitStruct.CAN_BS2=CAN_BS2_8tq;
这样STM32F407与STM32F103在绝大多数情况下是无法通信的,原因如下:
启动后,STM32F407自动执行SystemInit(),并配置成如下规格:
(可在库函数中找到)
根据上图,即可知启动后的STM32F407:
SYSCLK=168MHz
HCLK=SYSCLK/1=168MHz
PCLK1=HCLK/4=42MHz
STM32F407的CAN属于APB1,又由于CAN_InitStruct.CAN_Prescaler=2;
则CAN的时钟为PCLK1/2=21MHz。
根据前面我们对CAN的配置:
CAN_InitStruct.CAN_Prescaler=2;
CAN_InitStruct.CAN_SJW=CAN_SJW_1tq;
CAN_InitStruct.CAN_BS1=CAN_BS1_9tq;
CAN_InitStruct.CAN_BS2=CAN_BS2_8tq;
我们CAN的波特率实际上是:
CAN波特率=(1+9+8)/21=0.857M/s(并非1M/s)
而同样对于STM32103,默认情况下:
SYSCLK=72MHz
HCLK=SYSCLK/1=72MHz
PCLK=HCLK/2=36MHz,
因而根据配置:
CAN_InitStruct.CAN_Prescaler=2;
CAN_InitStruct.CAN_SJW=CAN_SJW_1tq;
CAN_InitStruct.CAN_BS1=CAN_BS1_9tq;
CAN_InitStruct.CAN_BS2=CAN_BS2_8tq;
CAN时钟=18MHz。
因此对于STM32F103:
CAN波特率=(1+9+8)/18=1M/s
STM32F407CAN为0.857M/s,而STM32F103CAN为1M/s,因此无法通信。
为了实现通信,我们需要使得STM32F407和STM32F103CAN的最终时钟相一致,这一点可以通过配置RCC实现。
在这个例子中可以直接通过修改CAN的预分频,但归根结底就是要正确配置RCC。
(写到这里,有一点我不确定:
APB外设的时钟是否有最大值限制?
因为根据参考手册
图标上有:
是不是外设的最高时钟只能到48MHz?
这一点希望各位深入研究一下。
)
下面介绍RCC库函数的使用
在V1.0.0版本的库中,提供了如下函数:
(选择stm32f4xx_rcc.h把文件拉到最后,所有的文件都可以这么做来查看函数)
这里介绍棕色标记的函数。
/*FunctionusedtosettheRCCclockconfigurationtothedefaultresetstate*/
/*用于配置RCC时钟和复位的函数*/
voidRCC_DeInit(void);
/*Internal/externalclocks,PLL,CSSandMCOconfigurationfunctions*****/
/*内部外部时钟,PLL,CSS,MCO配置函数*/
voidRCC_HSEConfig(uint8_tRCC_HSE);
ErrorStatusRCC_WaitForHSEStartUp(void);
voidRCC_AdjustHSICalibrationValue(uint8_tHSICalibrationValue);
voidRCC_HSICmd(FunctionalStateNewState);
voidRCC_LSEConfig(uint8_tRCC_LSE);
voidRCC_LSICmd(FunctionalStateNewState);
voidRCC_PLLConfig(uint32_tRCC_PLLSource,uint32_tPLLM,uint32_tPLLN,uint32_tPLLP,uint32_tPLLQ);
voidRCC_PLLCmd(FunctionalStateNewState);
voidRCC_PLLI2SConfig(uint32_tPLLI2SN,uint32_tPLLI2SR);
voidRCC_PLLI2SCmd(FunctionalStateNewState);
voidRCC_ClockSecuritySystemCmd(FunctionalStateNewState);
voidRCC_MCO1Config(uint32_tRCC_MCO1Source,uint32_tRCC_MCO1Div);
voidRCC_MCO2Config(uint32_tRCC_MCO2Source,uint32_tRCC_MCO2Div);
/*System,AHBandAPBbussesclocksconfigurationfunctions******************/
/*系统时钟,AHB时钟,APB时钟配置函数*/
voidRCC_SYSCLKConfig(uint32_tRCC_SYSCLKSource);
uint8_tRCC_GetSYSCLKSource(void);
voidRCC_HCLKConfig(uint32_tRCC_SYSCLK);
voidRCC_PCLK1Config(uint32_tRCC_HCLK);
voidRCC_PCLK2Config(uint32_tRCC_HCLK);
voidRCC_GetClocksFreq(RCC_ClocksTypeDef*RCC_Clocks);
/*Peripheralclocksconfigurationfunctions**********************************/
/*外设时钟配置函数*/
voidRCC_RTCCLKConfig(uint32_tRCC_RTCCLKSource);
voidRCC_RTCCLKCmd(FunctionalStateNewState);
voidRCC_BackupResetCmd(FunctionalStateNewState);
voidRCC_I2SCLKConfig(uint32_tRCC_I2SCLKSource);
voidRCC_AHB1PeriphClockCmd(uint32_tRCC_AHB1Periph,FunctionalStateNewState);
voidRCC_AHB2PeriphClockCmd(uint32_tRCC_AHB2Periph,FunctionalStateNewState);
voidRCC_AHB3PeriphClockCmd(uint32_tRCC_AHB3Periph,FunctionalStateNewState);
voidRCC_APB1PeriphClockCmd(uint32_tRCC_APB1Periph,FunctionalStateNewState);
voidRCC_APB2PeriphClockCmd(uint32_tRCC_APB2Periph,FunctionalStateNewState);
voidRCC_AHB1PeriphResetCmd(uint32_tRCC_AHB1Periph,FunctionalStateNewState);
voidRCC_AHB2PeriphResetCmd(uint32_tRCC_AHB2Periph,FunctionalStateNewState);
voidRCC_AHB3PeriphResetCmd(uint32_tRCC_AHB3Periph,FunctionalStateNewState);
voidRCC_APB1PeriphResetCmd(uint32_tRCC_APB1Periph,FunctionalStateNewState);
voidRCC_APB2PeriphResetCmd(uint32_tRCC_APB2Periph,FunctionalStateNewState);
voidRCC_AHB1PeriphClockLPModeCmd(uint32_tRCC_AHB1Periph,FunctionalStateNewState);
voidRCC_AHB2PeriphClockLPModeCmd(uint32_tRCC_AHB2Periph,FunctionalStateNewState);
voidRCC_AHB3PeriphClockLPModeCmd(uint32_tRCC_AHB3Periph,FunctionalStateNewState);
voidRCC_APB1PeriphClockLPModeCmd(uint32_tRCC_APB1Periph,FunctionalStateNewState);
voidRCC_APB2PeriphClockLPModeCmd(uint32_tRCC_APB2Periph,FunctionalStateNewState);
/*Interruptsandflagsmanagementfunctions**********************************/
/*中断和标志管理函数*/
voidRCC_ITConfig(uint8_tRCC_IT,FunctionalStateNewState);
FlagStatusRCC_GetFlagStatus(uint8_tRCC_FLAG);
voidRCC_ClearFlag(void);
ITStatusRCC_GetITStatus(uint8_tRCC_IT);
voidRCC_ClearITPendingBit(uint8_tRCC_IT);
这里我们写一个RCC配置函数来说明各函数的用途,其中HSE=8MHz。
/**
*@说明配置STM32F407的时钟系统
*@参数无
*@返回无
*@说明voidClock_Config(void)按如下表格配置时钟
*
*==================================================================
*SupportedSTM32F4xxdevicerevision|RevA
*-----------------------------------------------------------------------------
*SystemClocksource|PLL(HSE)
*-----------------------------------------------------------------------------
*SYSCLK(Hz)|168000000
*-----------------------------------------------------------------------------
*HCLK(Hz)|168000000
*-----------------------------------------------------------------------------
*AHBPrescaler|1
*-----------------------------------------------------------------------------
*APB1Prescaler|4
*-----------------------------------------------------------------------------
*APB2Prescaler|2
*-----------------------------------------------------------------------------
*HSEFrequency(Hz)|8000000
*-----------------------------------------------------------------------------
*PLL_M|8
*-----------------------------------------------------------------------------
*PLL_N|336
*-----------------------------------------------------------------------------
*PLL_P|2
*-----------------------------------------------------------------------------
*PLL_Q|7
*===================================================================
*/
voidClock_Config(void){
ErrorStatusState;
uint32_tPLL_M;
uint32_tPLL_N;
uint32_tPLL_P;
uint32_tPLL_Q;
/*配置前将所有RCC重置为初始值*/
RCC_DeInit();
/*这里选择外部晶振(HSE)作为时钟源,因此首先打开外部晶振*/
RCC_HSEConfig(RCC_HSE_ON);
/*等待外部晶振进入稳定状态*/
while(RCC_WaitForHSEStartUp()!
=SUCCESS);
/*
**我们要选择PLL时钟作为系统时钟,因此这里先要对PLL时钟进行配置
*/
/*选择外部晶振作为PLL的时钟源*/
/*到这一步为止,已有HSE_VALUE=8MHz.
PLL_VCOinputclock=(HSE_VALUEorHSI_VALUE/PLL_M),
根据文档,这个值被建议在1~2MHz,因此我们令PLL_M=8,
即PLL_VCOinputclock=1MHz*/
PLL_M=8;
/*到这一步为止,已有PLL_VCOinputclock=1MHz.
PLL_VCOoutputclock=(PLL_VCOinputclock)*PLL_N,
这个值要用来计算系统时钟,我们令PLL_N=336,
即PLL_VCOoutputclock=336MHz.*/
PLL_N=336;
/*到这一步为止,已有PLL_VCOoutputclock=336MHz.
SystemClock=(PLL_VCOoutputclock)/PLL_P,
因为我们要SystemClock=168Mhz,因此令PLL_P=2.
*/
PLL_P=2;
/*这个系数用来配置SD卡读写,USB等功能,暂时不用,根据文档,暂时先设为7*/
PLL_Q=7;
/*配置PLL并将其使能,获得168Mhz的SystemClock时钟*/
RCC_PLLConfig(RCC_PLLSource_HSE,PLL_M,PLL_N,PLL_P,PLL_Q);
RCC_PLLCmd(ENABLE);
/*到了这一步,我们已经配置好了PLL时钟。
下面我们配置SyetemClock*/
/*选择PLL时钟作为系统时钟源*/
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
/*到了这一步,我们已经配置好了系统时钟,频率为168MHz.下面我们可以对AHB,APB,外设等的时钟进行配置*/
/*时钟的结构请参考用户手册*/
/*首先配置AHB时钟(HCLK).为了获得较高的频率,我们对SYSCLK1分频,得到HCLK*/
RCC_HCLKConfig(RCC_HCLK_Div1);
/*APBx时钟(PCLK)由AHB时钟(HCLK)分频得到,下面我们配置PCLK*/
/*APB1时钟配置.4分频,即PCLK1=42MHz*/
RCC_PCLK1Config(RCC_HCLK_Div4);
/*APB2时钟配置.2分频,即PCLK2=84MHz*/
RCC_PCLK2Config(RCC_HCLK_Div2);
/*****函数结束******/
/*以上函数可以大体上说明这些库函数的作用*/
}
对于RCC_PLLConfig();函数,大家可能会迷惑。
其函数原型为:
voidRCC_PLLConfig(uint32_tRCC_PLLSource,
uint32_tPLLM,
uint32_tPLLN,
uint32_tPLLP,
u