1. 电源供电
看下面的框图,STM32F429的电源供电,能看到Vdd,Vdda,Vcap,Vss一堆电源标识:
这些常用标识的解释如下:
vss为接地端,其他都是为各功能分别供电的。
2. 上电复位和手动复位
当系统由可靠的电源供电时,一旦通电,电源迅速地达到额定输出电压,一旦断电,电源迅速地下降到0V,并且在接通的时候,电压不会降低。这时能够可靠地使用基于一个电容和一个电阻的低成本硬件复位。这种形式的复位电路称为阻容复位。
如果电源不够可靠,而涉及安全性,这种简单的阻容解决方案就不合适了。
2.1 硬件复位
STM32F429开发板的硬件复位原理图如下:
STM32这款CPU的复位引脚是低电平有效,即RESET为低电平时,CPU处于复位状态。
R21和C37组成简单的RC复位电路。当系统上电瞬间,电容两端电压可以认为是0,CPU处于复位状态。3.3V电源通过电阻给电容充电,当电容的电压升到CPU的高电平门槛电压时,CPU退出复位状态转入运行状态。
在设计电路时,需要选择适当的R值和C值,以保证RESET低电平持续时间满足CPU复位最小脉宽的要求。
当按下RESET轻触开关时,C37两端被短路接地,可实现手动复位CPU。
2.2 软件复位
除了上电和手动复位,程序设计设置中还经常要用到软件复位,即调用一条函数就可以实现复位功能。此函数已经由CMSIS软件包中的core_cm4.h文件提供,函数如下:
/** brief System Reset details Initiates a system reset request to reset the MCU. */__STATIC_INLINE void __NVIC_SystemReset(void){ __DSB(); /* Ensure all outstanding memory accesses included buffered write are completed before reset */ SCB-》AIRCR = (uint32_t)((0x5FAUL 《《 SCB_AIRCR_VECTKEY_Pos) | (SCB-》AIRCR & SCB_AIRCR_PRIGROUP_Msk) | SCB_AIRCR_SYSRESETREQ_Msk ); /* Keep priority group unchanged */ __DSB(); /* Ensure completion of memory access */ for(;;) /* wait until reset */ { __NOP(); }} 软件复位反映到实际硬件上,就是给硬件复位部分发一个复位信号:
3. RCC时钟控制
首先,单片机能正常工作的必要条件之一就是时钟电路,时钟是单片机的脉搏,是单片机的驱动源,单片机工作是在统一的时钟脉冲控制下一拍一拍进行工作的。这个脉冲由单片机控制器中的时序电路发出的。所以单片机就很需要晶振。
3.1 晶振
晶振,全称是石英晶体振荡器,是一种高精度和高稳定度的振荡器。通过一定的外接电路来,可以生成频率和峰值稳定的正弦波。而单片机在运行的时候,需要一个脉冲信号,做为自己执行指令的触发信号,可以简单的想象为:单片机收到一个脉冲,就执行一次或多次指令。
单片机工作时,是一条一条地从ROM中取指令,然后一步一步地执行。单片机访问一次存储器的时间,称之为一个机器周期,这是一个时间基准。—个机器周期包括12个时钟周期。如果一个单片机选择了12兆赫兹晶振,它的时钟周期是1/12us,它的一个机器周期是12×(1/12)us,也就是1us。
3.2 内部时钟和外部时钟的不同
首先,晶振是时钟电路的振荡器,时钟信号由振荡电路产生。
由芯片内部RC时钟电路产生时钟信号,即内部时钟。
由芯片外部晶体,加振荡电路产生时钟信号,即外部时钟。
STM3F429有如下四种时钟源可供使用:
HSI (High-speed internal oscillator) :
HSI是内部的高速RC振荡器,频率16MHz,可被用于系统时钟。优势是低成本,无需外部时钟,快速启动(仅需几个微秒),缺点是精度差,即使经过校准。
HSE (High-speed external oscillator):
HSE是外部的高速振荡器,通过外接时钟源,有源或者无源晶振驱动,时钟范围4-26MHz。优势是精度高,缺点是增加成本。
LSE (Low-speed external oscillator)
LSE是外部的低速振荡器,通过外接时钟源,有源或者无源晶振驱动,一般接32.768KHz,主要用于RTC实时时钟。
LSI (Low-speed internal oscillator)
LSI是内部的低速RC振荡器,频率约是32KHz,主要用于独立看门狗和自动唤醒,也可以用于RTC实时时钟。
通过下面的时钟树再进一步的认识这几个时钟:
3.3 HSE和LSE外部电路的硬件设计
HSE时钟
当前开发板是用的8MHz晶振为HSE提供时钟,硬件设计如下:
晶振和负载电容需要尽可能近地靠近F4的晶振引脚,以减小输出失真和启动稳定时间。负载电容值必须根据选定的晶振进行调节。
对于C109和C111,我们推荐使用高质量陶瓷电容,这种电容是设计用于需要高频率的场合,并且可以满足晶体或谐振器的需求。C109和C111通常具有相同的值。
这里再额外补充一个知识点,HSE旁路时钟和外置晶振区别:一般板子是采用的外置晶振模式,高速外部 (HSE) 时钟可以使用一个4到26MHz 的晶振 / 陶瓷谐振振荡器产生:
而bypass 旁路的意思就是不使用它,绕过它。具体到HSE旁路的话,用户直接提供4-26MHz的时钟源即可,可以使用有源晶振或者FPGA提供时钟等方式:
LSE时钟
当前开发板是用的32768Hz晶振为LSE提供时钟,硬件设计如下:
3.4 时钟具体理解
STM32 也可以说有5个时钟源:HSI、HSE、LSI、LSE、PLL。
①、HSI是高速内部时钟,RC振荡器,频率为16MHz,精度不高。可以直接作为系统时钟或者用作PLL时钟输入。
②、HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~26MHz。(此处以25M为例)
③、LSI是低速内部时钟,RC振荡器,频率为32kHz,提供低功耗时钟。主要供独立看门狗和自动唤醒单元使用。
④、LSE是低速外部时钟,接频率为32.768kHz的石英晶体。RTC
⑤、PLL为锁相环倍频输出。
PLL 为锁相环倍频输出。 STM32F4 有三个 PLL:
1) 主 PLL(PLL)由 HSE 或者 HSI 提供时钟信号,并具有两个不同的输出时钟。第一个输出 PLLP 用于生成高速的系统时钟(最高 180MHz)第二个输出 PLLQ 为 48M 时钟, 用于 USB OTG FS 时钟,随机数发生器的时钟和 SDIO时钟。
2) 第一个专用 PLL(PLLI2S)用于生成精确时钟, 在 I2S 和 SAI1 上实现高品质音频性能。 其中, N 是用于 PLLI2S vco 的倍频系数,其取值范围是: 192~432; R 是 I2S 时钟的分频系数,其取值范围是: 2~7; Q 是 SAI 时钟分频系数,其取值范围是: 2~15; P 没用到。
3) 第二个专用 PLL(PLLSAI)同样用于生成精确时钟,用于 SAI1 输入时钟,同时还为 LCD_TFT接口提供精确时钟。 其中, N 是用于 PLLSAI vco 的倍频系数,其取值范围是: 192~432;Q 是 SAI 时钟分频系数,其取值范围是: 2~15; R 是 LTDC 时钟的分频系数,其取值范围是: 2~7; P 没用到。
主 PLL 时钟第一个高速时钟输出 PLLP 的计算方法:配置180MHz为例:
分析:主 PLL 时钟的时钟源要先经过一个分频系数为 M 的分频器,然后经过倍频系数为 N 的倍频器出来之后还需要经过一个分频系数为 P(第一个输出 PLLP)或者 Q(第二个输出 PLLQ)的分频器分频之后,最后才生成最终的主 PLL 时钟。例如我们的外部晶振选择 25MHz。同时我们设置相应的分频器 M=25,倍频器倍频系数 N=360,分频器分频系数 P=2,那么主 PLL 生成的第一个输出高速时钟 PLLP 为:
PLL=25MHz * N/ (M*P)=25MHz* 360 /(25*2) = 180MHz
配置过程如下图所示:
4. 系统时钟的初始化寄存器源码分析
在系统进入主函数之前,首先会执行SystemInit这个函数对系统进行初始化
看一看这个程序的内容:
源代码如下:
void SystemInit(void){/* FPU 设置------------------------------------------------------------*/#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)SCB-》CPACR |= ((3UL 《《 10*2)|(3UL 《《 11*2)); /* set CP10 and CP11 Full Access */#endif/* 复位 RCC 时钟配置为默认配置-----------*/RCC-》CR |= (uint32_t)0x00000001;//打开 HSION 位RCC-》CFGR = 0x00000000;//复位 CFGR 寄存器RCC-》CR &= (uint32_t)0xFEF6FFFF;//复位 HSEON, CSSON and PLLON 位RCC-》PLLCFGR = 0x24003010; //复位寄存器 PLLCFGRRCC-》CR &= (uint32_t)0xFFFBFFFF;//复位 HSEBYP 位RCC-》CIR = 0x00000000;//关闭所有中断#if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)SystemInit_ExtMemCtl();#endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM *//* 配置中断向量表地址=基地址+偏移地址 ------------------*/#ifdef VECT_TAB_SRAMSCB-》VTOR = SRAM_BASE | VECT_TAB_OFFSET;#elseSCB-》VTOR = FLASH_BASE | VECT_TAB_OFFSET;#endif} 可以看出这段代码的作用是:
1) FPU 设置
2) 复位 RCC 时钟配置为默认复位值(默认开始了 HIS)
3) 外部存储器配置
4) 中断向量表地址配置
做了这些工作,但是在F4的HAL库汇总SystemInit函数,并没有设置系统的主频和外设时钟的频率,所以所以需要自己去写这个函数。
首先先分析一下使用寄存器版本来写的SystemInit函数吧:
//系统时钟初始化函数//plln:主PLL倍频系数(PLL倍频),取值范围:64~432.//pllm:主PLL和音频PLL分频系数(PLL之前的分频),取值范围:2~63.//pllp:系统时钟的主PLL分频系数(PLL之后的分频),取值范围:2,4,6,8.(仅限这4个值!)//pllq:USB/SDIO/随机数产生器等的主PLL分频系数(PLL之后的分频),取值范围:2~15.void Stm32_Clock_Init(u32 plln,u32 pllm,u32 pllp,u32 pllq){ RCC-》CR|=0x00000001; //设置HISON,开启内部高速RC振荡 RCC-》CFGR=0x00000000; //CFGR清零 RCC-》CR&=0xFEF6FFFF; //HSEON,CSSON,PLLON清零 RCC-》PLLCFGR=0x24003010; //PLLCFGR恢复复位值 RCC-》CR&=~(1《《18); //HSEBYP清零,外部晶振不旁路 RCC-》CIR=0x00000000; //禁止RCC时钟中断 Sys_Clock_Set(plln,pllm,pllp,pllq);//设置时钟 //配置向量表 #ifdef VECT_TAB_RAM MY_NVIC_SetVectorTable(1《《29,0x0);#else MY_NVIC_SetVectorTable(0,0x0);#endif } 这个函数需要的参数是4个,分别与系统原理图对应的是,如下图所示:
接下来,查看时钟控制CR寄存器的描述:
(1)开启HISON,开启内部高速RC震荡
(2)时钟配置寄存器CFRG清零
(3)配置时钟控制CR寄存器, 使位HSEON,CSSON,PLLON清零(第16、19、24位)
(4)RCC PLL 配置寄存器 (RCC_PLLCFGR),恢复默认值:
RCC-》PLLCFGR=0x24003010; //PLLCFGR恢复复位值
(5)设置CR寄存器,外部晶振不旁路
(6)RCC 时钟中断寄存器 (RCC_CIR),RCC-》CR=0X00000000; //禁止RCC时钟中断
(7)设置时钟,使用函数Sys_Clock_Set(plln,pllm,pllp,pllq);//设置时钟,函数原型如下:
u8 Sys_Clock_Set(u32 plln,u32 pllm,u32 pllp,u32 pllq)
{
u16 retry=0;
u8 status=0;
RCC-》CR|=1《《16; //HSE 开启
while(((RCC-》CR&(1《《17))==0)&&(retry《0X1FFF))retry++;//等待HSE RDY
if(retry==0X1FFF)status=1; //HSE无法就绪
else
{
RCC-》APB1ENR|=1《《28; //电源接口时钟使能
PWR-》CR|=3《《14; //高性能模式,时钟可到180Mhz
RCC-》CFGR|=(0《《4)|(5《《10)|(4《《13);//HCLK 不分频;APB1 4分频;APB2 2分频。
RCC-》CR&=~(1《《24); //关闭主PLL
RCC-》PLLCFGR=pllm|(plln《《6)|(((pllp》》1)-1)《《16)|(pllq《《24)|(1《《22);//配置主PLL,PLL时钟源来自HSE
RCC-》CR|=1《《24; //打开主PLL
while((RCC-》CR&(1《《25))==0);//等待PLL准备好
FLASH-》ACR|=1《《8; //指令预取使能。
FLASH-》ACR|=1《《9; //指令cache使能。
FLASH-》ACR|=1《《10; //数据cache使能。
FLASH-》ACR|=5《《0; //5个CPU等待周期。
RCC-》CFGR&=~(3《《0); //清零
RCC-》CFGR|=2《《0; //选择主PLL作为系统时钟
while((RCC-》CFGR&(3《《2))!=(2《《2));//等待主PLL作为系统时钟成功。
}
return status;
}
(8).RCC-》CR|=1《《16; //设置CR寄存器的第16位为1,HSE 开启
(9)等待HSE时钟就绪,判断CR寄存器的第17位是否为1,返回1准备就绪,如果超时,返回标志位status=1.
(10)如果准备就绪,使能电源接口时钟,RCC-》APB1ENR|=1《《28; //设置APB1ENR寄存器28位为1电源接口时钟使能
(11)开始电源的高性能模式,PWR-》CR|=3《《14; //高性能模式,时钟可到180Mhz
(11)。配置RCC的CFGR寄存器,//HCLK 不分频;APB1 4分频;APB2 2分频
(12).RCC-》CR&=~(1《《24); //关闭主PLL,配置主PLL的一些参数,配置完再重新打开
(13)。//配置主PLL,PLL时钟源来自HSE,RCC-》PLLCFGR=pllm|(plln《《6)|(((pllp》》1)-1)《《16)|(pllq《《24)|(1《《22);
pllm,设置PLLM
(plln《《6),设置PLLN
(((pllp》》1)-1)《《16),设置系统时钟的主PLL分频系数(PLL之后的分频),取值范围:2,4,6,8.(仅限这4个值!)
为何是:(((pllp》》1)-1)《《16)呢?如下解释,当我们取值pllp为2时,可以得到(((pllp》》1)-1)《《16)=0
当pllp取值为4时,(((pllp》》1)-1)《《16)=1,同理,pllp取值为6时,(((pllp》》1)-1)《《16)=3.其实就是在调用函数的时候方便设置。可以看都这个寄存器的位,当第17:16为0时,就是这只pllp为2分频。
(pllq《《24),pllq:USB/SDIO/随机数产生器等的主PLL分频系数(PLL之后的分频),取值范围:2~15.
(1《《22),选择主PLL的时钟源是HSE外部震荡时钟、
所以第13步骤,完成的工作就是如下如所示的设置:
(14)。设置完主PLL之后,重新打开主PLL: RCC-》CR|=1《《24; //打开主PLL
(15)。每次打开PLL之前,都要等待就绪:while((RCC-》CR&(1《《25))==0);//等待PLL准备好
(16)。设置FLASH寄存器参数:
FLASH-》ACR|=1《《8; //指令预取使能。
FLASH-》ACR|=1《《9; //指令cache使能。
FLASH-》ACR|=1《《10; //数据cache使能。
FLASH-》ACR|=5《《0; //5个CPU等待周期。
(17)。设置CFRG(RCC时钟配置寄存器)选择PLL作为系统时钟,RCC-》CFGR&=~(3《《0); //清零 RCC-》CFGR|=2《《0;
(18)。等待主PLL设置为系统主时钟成功:while((RCC-》CFGR&(3《《2))!=(2《《2));
语句含义:当CFGR的3:2位不等于01时就说明主PLL还未就绪,就绪等待。
(19)。最后一步,配置向量表。
通过这样的设置系统时钟的整体配置就OK了。
如果调用函数:u8 Sys_Clock_Set(u32 plln,u32 pllm,u32 pllp,u32 pllq),产生180Mhz的主频
设置参数:外部晶振为25M的时候:plln=360,pllm=25,pllp=2,pllq=8.
5. 时钟配置
STM32F4开发板使用的外部晶振频率是8MHz,下面分步说明如何让其通过这个频率工作到168MHz的主频。
第1步:在stm32f4xx_hal_conf.h文件配置HSE_VALUE
配置的大小要跟板子的实际晶振大小匹配。
#if !defined (HSE_VALUE)
#define HSE_VALUE ((uint32_t)8000000) /*!《 Value of the External oscillator in Hz */
#endif /* HSE_VALUE */
第2步:系统上电后,在启动文件startup_stm32f429xx.s的复位中断服务程序里面会调用函数SystemInit。
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
以往STM32F1和STM32F4系列都会在函数SystemInit里面配置PLL锁相环,使用了HAL后,需要在main函数里面配置。当前SystemInit函数实现的功能如下:
1. /**
2. * @brief Setup the microcontroller system
3. * Initialize the FPU setting, vector table location and External memory
4. * configuration.
5. * @param None
6. * @retval None
7. */
8. void SystemInit(void)
9. {
10. /* FPU settings ------------------------------------------------------------*/
11. #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
12. SCB-》CPACR |= ((3UL 《《 10*2)|(3UL 《《 11*2)); /* set CP10 and CP11 Full Access */
13. #endif
14. /* Reset the RCC clock configuration to the default reset state ------------*/
15. /* Set HSION bit */
16. RCC-》CR |= (uint32_t)0x00000001;
17.
18. /* Reset CFGR register */
19. RCC-》CFGR = 0x00000000;
20.
21. /* Reset HSEON, CSSON and PLLON bits */
22. RCC-》CR &= (uint32_t)0xFEF6FFFF;
23.
24. /* Reset PLLCFGR register */
25. RCC-》PLLCFGR = 0x24003010;
26.
27. /* Reset HSEBYP bit */
28. RCC-》CR &= (uint32_t)0xFFFBFFFF;
29.
30. /* Disable all interrupts */
31. RCC-》CIR = 0x00000000;
32.
33. #if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
34. SystemInit_ExtMemCtl();
35. #endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */
36.
37. /* Configure the Vector Table location add offset address ------------------*/
38. #ifdef VECT_TAB_SRAM
39. SCB-》VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
40. #else
41. SCB-》VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
42. #endif
43. }
第12行:使能FPU单元。
第16 – 31行:复位RCC相关寄存器。
第69 – 73行:设置中断向量表的位置。
第3步:在main函数的外设驱动初始化函数里面完成时钟初始化,主要是PLL锁相环,让芯片最终工作到168MHz。
1. 电源供电
看下面的框图,STM32F429的电源供电,能看到Vdd,Vdda,Vcap,Vss一堆电源标识:
这些常用标识的解释如下:
vss为接地端,其他都是为各功能分别供电的。
2. 上电复位和手动复位
当系统由可靠的电源供电时,一旦通电,电源迅速地达到额定输出电压,一旦断电,电源迅速地下降到0V,并且在接通的时候,电压不会降低。这时能够可靠地使用基于一个电容和一个电阻的低成本硬件复位。这种形式的复位电路称为阻容复位。
如果电源不够可靠,而涉及安全性,这种简单的阻容解决方案就不合适了。
2.1 硬件复位
STM32F429开发板的硬件复位原理图如下:
STM32这款CPU的复位引脚是低电平有效,即RESET为低电平时,CPU处于复位状态。
R21和C37组成简单的RC复位电路。当系统上电瞬间,电容两端电压可以认为是0,CPU处于复位状态。3.3V电源通过电阻给电容充电,当电容的电压升到CPU的高电平门槛电压时,CPU退出复位状态转入运行状态。
在设计电路时,需要选择适当的R值和C值,以保证RESET低电平持续时间满足CPU复位最小脉宽的要求。
当按下RESET轻触开关时,C37两端被短路接地,可实现手动复位CPU。
2.2 软件复位
除了上电和手动复位,程序设计设置中还经常要用到软件复位,即调用一条函数就可以实现复位功能。此函数已经由CMSIS软件包中的core_cm4.h文件提供,函数如下:
/** brief System Reset details Initiates a system reset request to reset the MCU. */__STATIC_INLINE void __NVIC_SystemReset(void){ __DSB(); /* Ensure all outstanding memory accesses included buffered write are completed before reset */ SCB-》AIRCR = (uint32_t)((0x5FAUL 《《 SCB_AIRCR_VECTKEY_Pos) | (SCB-》AIRCR & SCB_AIRCR_PRIGROUP_Msk) | SCB_AIRCR_SYSRESETREQ_Msk ); /* Keep priority group unchanged */ __DSB(); /* Ensure completion of memory access */ for(;;) /* wait until reset */ { __NOP(); }} 软件复位反映到实际硬件上,就是给硬件复位部分发一个复位信号:
3. RCC时钟控制
首先,单片机能正常工作的必要条件之一就是时钟电路,时钟是单片机的脉搏,是单片机的驱动源,单片机工作是在统一的时钟脉冲控制下一拍一拍进行工作的。这个脉冲由单片机控制器中的时序电路发出的。所以单片机就很需要晶振。
3.1 晶振
晶振,全称是石英晶体振荡器,是一种高精度和高稳定度的振荡器。通过一定的外接电路来,可以生成频率和峰值稳定的正弦波。而单片机在运行的时候,需要一个脉冲信号,做为自己执行指令的触发信号,可以简单的想象为:单片机收到一个脉冲,就执行一次或多次指令。
单片机工作时,是一条一条地从ROM中取指令,然后一步一步地执行。单片机访问一次存储器的时间,称之为一个机器周期,这是一个时间基准。—个机器周期包括12个时钟周期。如果一个单片机选择了12兆赫兹晶振,它的时钟周期是1/12us,它的一个机器周期是12×(1/12)us,也就是1us。
3.2 内部时钟和外部时钟的不同
首先,晶振是时钟电路的振荡器,时钟信号由振荡电路产生。
由芯片内部RC时钟电路产生时钟信号,即内部时钟。
由芯片外部晶体,加振荡电路产生时钟信号,即外部时钟。
STM3F429有如下四种时钟源可供使用:
HSI (High-speed internal oscillator) :
HSI是内部的高速RC振荡器,频率16MHz,可被用于系统时钟。优势是低成本,无需外部时钟,快速启动(仅需几个微秒),缺点是精度差,即使经过校准。
HSE (High-speed external oscillator):
HSE是外部的高速振荡器,通过外接时钟源,有源或者无源晶振驱动,时钟范围4-26MHz。优势是精度高,缺点是增加成本。
LSE (Low-speed external oscillator)
LSE是外部的低速振荡器,通过外接时钟源,有源或者无源晶振驱动,一般接32.768KHz,主要用于RTC实时时钟。
LSI (Low-speed internal oscillator)
LSI是内部的低速RC振荡器,频率约是32KHz,主要用于独立看门狗和自动唤醒,也可以用于RTC实时时钟。
通过下面的时钟树再进一步的认识这几个时钟:
3.3 HSE和LSE外部电路的硬件设计
HSE时钟
当前开发板是用的8MHz晶振为HSE提供时钟,硬件设计如下:
晶振和负载电容需要尽可能近地靠近F4的晶振引脚,以减小输出失真和启动稳定时间。负载电容值必须根据选定的晶振进行调节。
对于C109和C111,我们推荐使用高质量陶瓷电容,这种电容是设计用于需要高频率的场合,并且可以满足晶体或谐振器的需求。C109和C111通常具有相同的值。
这里再额外补充一个知识点,HSE旁路时钟和外置晶振区别:一般板子是采用的外置晶振模式,高速外部 (HSE) 时钟可以使用一个4到26MHz 的晶振 / 陶瓷谐振振荡器产生:
而bypass 旁路的意思就是不使用它,绕过它。具体到HSE旁路的话,用户直接提供4-26MHz的时钟源即可,可以使用有源晶振或者FPGA提供时钟等方式:
LSE时钟
当前开发板是用的32768Hz晶振为LSE提供时钟,硬件设计如下:
3.4 时钟具体理解
STM32 也可以说有5个时钟源:HSI、HSE、LSI、LSE、PLL。
①、HSI是高速内部时钟,RC振荡器,频率为16MHz,精度不高。可以直接作为系统时钟或者用作PLL时钟输入。
②、HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~26MHz。(此处以25M为例)
③、LSI是低速内部时钟,RC振荡器,频率为32kHz,提供低功耗时钟。主要供独立看门狗和自动唤醒单元使用。
④、LSE是低速外部时钟,接频率为32.768kHz的石英晶体。RTC
⑤、PLL为锁相环倍频输出。
PLL 为锁相环倍频输出。 STM32F4 有三个 PLL:
1) 主 PLL(PLL)由 HSE 或者 HSI 提供时钟信号,并具有两个不同的输出时钟。第一个输出 PLLP 用于生成高速的系统时钟(最高 180MHz)第二个输出 PLLQ 为 48M 时钟, 用于 USB OTG FS 时钟,随机数发生器的时钟和 SDIO时钟。
2) 第一个专用 PLL(PLLI2S)用于生成精确时钟, 在 I2S 和 SAI1 上实现高品质音频性能。 其中, N 是用于 PLLI2S vco 的倍频系数,其取值范围是: 192~432; R 是 I2S 时钟的分频系数,其取值范围是: 2~7; Q 是 SAI 时钟分频系数,其取值范围是: 2~15; P 没用到。
3) 第二个专用 PLL(PLLSAI)同样用于生成精确时钟,用于 SAI1 输入时钟,同时还为 LCD_TFT接口提供精确时钟。 其中, N 是用于 PLLSAI vco 的倍频系数,其取值范围是: 192~432;Q 是 SAI 时钟分频系数,其取值范围是: 2~15; R 是 LTDC 时钟的分频系数,其取值范围是: 2~7; P 没用到。
主 PLL 时钟第一个高速时钟输出 PLLP 的计算方法:配置180MHz为例:
分析:主 PLL 时钟的时钟源要先经过一个分频系数为 M 的分频器,然后经过倍频系数为 N 的倍频器出来之后还需要经过一个分频系数为 P(第一个输出 PLLP)或者 Q(第二个输出 PLLQ)的分频器分频之后,最后才生成最终的主 PLL 时钟。例如我们的外部晶振选择 25MHz。同时我们设置相应的分频器 M=25,倍频器倍频系数 N=360,分频器分频系数 P=2,那么主 PLL 生成的第一个输出高速时钟 PLLP 为:
PLL=25MHz * N/ (M*P)=25MHz* 360 /(25*2) = 180MHz
配置过程如下图所示:
4. 系统时钟的初始化寄存器源码分析
在系统进入主函数之前,首先会执行SystemInit这个函数对系统进行初始化
看一看这个程序的内容:
源代码如下:
void SystemInit(void){/* FPU 设置------------------------------------------------------------*/#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)SCB-》CPACR |= ((3UL 《《 10*2)|(3UL 《《 11*2)); /* set CP10 and CP11 Full Access */#endif/* 复位 RCC 时钟配置为默认配置-----------*/RCC-》CR |= (uint32_t)0x00000001;//打开 HSION 位RCC-》CFGR = 0x00000000;//复位 CFGR 寄存器RCC-》CR &= (uint32_t)0xFEF6FFFF;//复位 HSEON, CSSON and PLLON 位RCC-》PLLCFGR = 0x24003010; //复位寄存器 PLLCFGRRCC-》CR &= (uint32_t)0xFFFBFFFF;//复位 HSEBYP 位RCC-》CIR = 0x00000000;//关闭所有中断#if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)SystemInit_ExtMemCtl();#endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM *//* 配置中断向量表地址=基地址+偏移地址 ------------------*/#ifdef VECT_TAB_SRAMSCB-》VTOR = SRAM_BASE | VECT_TAB_OFFSET;#elseSCB-》VTOR = FLASH_BASE | VECT_TAB_OFFSET;#endif} 可以看出这段代码的作用是:
1) FPU 设置
2) 复位 RCC 时钟配置为默认复位值(默认开始了 HIS)
3) 外部存储器配置
4) 中断向量表地址配置
做了这些工作,但是在F4的HAL库汇总SystemInit函数,并没有设置系统的主频和外设时钟的频率,所以所以需要自己去写这个函数。
首先先分析一下使用寄存器版本来写的SystemInit函数吧:
//系统时钟初始化函数//plln:主PLL倍频系数(PLL倍频),取值范围:64~432.//pllm:主PLL和音频PLL分频系数(PLL之前的分频),取值范围:2~63.//pllp:系统时钟的主PLL分频系数(PLL之后的分频),取值范围:2,4,6,8.(仅限这4个值!)//pllq:USB/SDIO/随机数产生器等的主PLL分频系数(PLL之后的分频),取值范围:2~15.void Stm32_Clock_Init(u32 plln,u32 pllm,u32 pllp,u32 pllq){ RCC-》CR|=0x00000001; //设置HISON,开启内部高速RC振荡 RCC-》CFGR=0x00000000; //CFGR清零 RCC-》CR&=0xFEF6FFFF; //HSEON,CSSON,PLLON清零 RCC-》PLLCFGR=0x24003010; //PLLCFGR恢复复位值 RCC-》CR&=~(1《《18); //HSEBYP清零,外部晶振不旁路 RCC-》CIR=0x00000000; //禁止RCC时钟中断 Sys_Clock_Set(plln,pllm,pllp,pllq);//设置时钟 //配置向量表 #ifdef VECT_TAB_RAM MY_NVIC_SetVectorTable(1《《29,0x0);#else MY_NVIC_SetVectorTable(0,0x0);#endif } 这个函数需要的参数是4个,分别与系统原理图对应的是,如下图所示:
接下来,查看时钟控制CR寄存器的描述:
(1)开启HISON,开启内部高速RC震荡
(2)时钟配置寄存器CFRG清零
(3)配置时钟控制CR寄存器, 使位HSEON,CSSON,PLLON清零(第16、19、24位)
(4)RCC PLL 配置寄存器 (RCC_PLLCFGR),恢复默认值:
RCC-》PLLCFGR=0x24003010; //PLLCFGR恢复复位值
(5)设置CR寄存器,外部晶振不旁路
(6)RCC 时钟中断寄存器 (RCC_CIR),RCC-》CR=0X00000000; //禁止RCC时钟中断
(7)设置时钟,使用函数Sys_Clock_Set(plln,pllm,pllp,pllq);//设置时钟,函数原型如下:
u8 Sys_Clock_Set(u32 plln,u32 pllm,u32 pllp,u32 pllq)
{
u16 retry=0;
u8 status=0;
RCC-》CR|=1《《16; //HSE 开启
while(((RCC-》CR&(1《《17))==0)&&(retry《0X1FFF))retry++;//等待HSE RDY
if(retry==0X1FFF)status=1; //HSE无法就绪
else
{
RCC-》APB1ENR|=1《《28; //电源接口时钟使能
PWR-》CR|=3《《14; //高性能模式,时钟可到180Mhz
RCC-》CFGR|=(0《《4)|(5《《10)|(4《《13);//HCLK 不分频;APB1 4分频;APB2 2分频。
RCC-》CR&=~(1《《24); //关闭主PLL
RCC-》PLLCFGR=pllm|(plln《《6)|(((pllp》》1)-1)《《16)|(pllq《《24)|(1《《22);//配置主PLL,PLL时钟源来自HSE
RCC-》CR|=1《《24; //打开主PLL
while((RCC-》CR&(1《《25))==0);//等待PLL准备好
FLASH-》ACR|=1《《8; //指令预取使能。
FLASH-》ACR|=1《《9; //指令cache使能。
FLASH-》ACR|=1《《10; //数据cache使能。
FLASH-》ACR|=5《《0; //5个CPU等待周期。
RCC-》CFGR&=~(3《《0); //清零
RCC-》CFGR|=2《《0; //选择主PLL作为系统时钟
while((RCC-》CFGR&(3《《2))!=(2《《2));//等待主PLL作为系统时钟成功。
}
return status;
}
(8).RCC-》CR|=1《《16; //设置CR寄存器的第16位为1,HSE 开启
(9)等待HSE时钟就绪,判断CR寄存器的第17位是否为1,返回1准备就绪,如果超时,返回标志位status=1.
(10)如果准备就绪,使能电源接口时钟,RCC-》APB1ENR|=1《《28; //设置APB1ENR寄存器28位为1电源接口时钟使能
(11)开始电源的高性能模式,PWR-》CR|=3《《14; //高性能模式,时钟可到180Mhz
(11)。配置RCC的CFGR寄存器,//HCLK 不分频;APB1 4分频;APB2 2分频
(12).RCC-》CR&=~(1《《24); //关闭主PLL,配置主PLL的一些参数,配置完再重新打开
(13)。//配置主PLL,PLL时钟源来自HSE,RCC-》PLLCFGR=pllm|(plln《《6)|(((pllp》》1)-1)《《16)|(pllq《《24)|(1《《22);
pllm,设置PLLM
(plln《《6),设置PLLN
(((pllp》》1)-1)《《16),设置系统时钟的主PLL分频系数(PLL之后的分频),取值范围:2,4,6,8.(仅限这4个值!)
为何是:(((pllp》》1)-1)《《16)呢?如下解释,当我们取值pllp为2时,可以得到(((pllp》》1)-1)《《16)=0
当pllp取值为4时,(((pllp》》1)-1)《《16)=1,同理,pllp取值为6时,(((pllp》》1)-1)《《16)=3.其实就是在调用函数的时候方便设置。可以看都这个寄存器的位,当第17:16为0时,就是这只pllp为2分频。
(pllq《《24),pllq:USB/SDIO/随机数产生器等的主PLL分频系数(PLL之后的分频),取值范围:2~15.
(1《《22),选择主PLL的时钟源是HSE外部震荡时钟、
所以第13步骤,完成的工作就是如下如所示的设置:
(14)。设置完主PLL之后,重新打开主PLL: RCC-》CR|=1《《24; //打开主PLL
(15)。每次打开PLL之前,都要等待就绪:while((RCC-》CR&(1《《25))==0);//等待PLL准备好
(16)。设置FLASH寄存器参数:
FLASH-》ACR|=1《《8; //指令预取使能。
FLASH-》ACR|=1《《9; //指令cache使能。
FLASH-》ACR|=1《《10; //数据cache使能。
FLASH-》ACR|=5《《0; //5个CPU等待周期。
(17)。设置CFRG(RCC时钟配置寄存器)选择PLL作为系统时钟,RCC-》CFGR&=~(3《《0); //清零 RCC-》CFGR|=2《《0;
(18)。等待主PLL设置为系统主时钟成功:while((RCC-》CFGR&(3《《2))!=(2《《2));
语句含义:当CFGR的3:2位不等于01时就说明主PLL还未就绪,就绪等待。
(19)。最后一步,配置向量表。
通过这样的设置系统时钟的整体配置就OK了。
如果调用函数:u8 Sys_Clock_Set(u32 plln,u32 pllm,u32 pllp,u32 pllq),产生180Mhz的主频
设置参数:外部晶振为25M的时候:plln=360,pllm=25,pllp=2,pllq=8.
5. 时钟配置
STM32F4开发板使用的外部晶振频率是8MHz,下面分步说明如何让其通过这个频率工作到168MHz的主频。
第1步:在stm32f4xx_hal_conf.h文件配置HSE_VALUE
配置的大小要跟板子的实际晶振大小匹配。
#if !defined (HSE_VALUE)
#define HSE_VALUE ((uint32_t)8000000) /*!《 Value of the External oscillator in Hz */
#endif /* HSE_VALUE */
第2步:系统上电后,在启动文件startup_stm32f429xx.s的复位中断服务程序里面会调用函数SystemInit。
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
以往STM32F1和STM32F4系列都会在函数SystemInit里面配置PLL锁相环,使用了HAL后,需要在main函数里面配置。当前SystemInit函数实现的功能如下:
1. /**
2. * @brief Setup the microcontroller system
3. * Initialize the FPU setting, vector table location and External memory
4. * configuration.
5. * @param None
6. * @retval None
7. */
8. void SystemInit(void)
9. {
10. /* FPU settings ------------------------------------------------------------*/
11. #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
12. SCB-》CPACR |= ((3UL 《《 10*2)|(3UL 《《 11*2)); /* set CP10 and CP11 Full Access */
13. #endif
14. /* Reset the RCC clock configuration to the default reset state ------------*/
15. /* Set HSION bit */
16. RCC-》CR |= (uint32_t)0x00000001;
17.
18. /* Reset CFGR register */
19. RCC-》CFGR = 0x00000000;
20.
21. /* Reset HSEON, CSSON and PLLON bits */
22. RCC-》CR &= (uint32_t)0xFEF6FFFF;
23.
24. /* Reset PLLCFGR register */
25. RCC-》PLLCFGR = 0x24003010;
26.
27. /* Reset HSEBYP bit */
28. RCC-》CR &= (uint32_t)0xFFFBFFFF;
29.
30. /* Disable all interrupts */
31. RCC-》CIR = 0x00000000;
32.
33. #if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
34. SystemInit_ExtMemCtl();
35. #endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */
36.
37. /* Configure the Vector Table location add offset address ------------------*/
38. #ifdef VECT_TAB_SRAM
39. SCB-》VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
40. #else
41. SCB-》VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
42. #endif
43. }
第12行:使能FPU单元。
第16 – 31行:复位RCC相关寄存器。
第69 – 73行:设置中断向量表的位置。
第3步:在main函数的外设驱动初始化函数里面完成时钟初始化,主要是PLL锁相环,让芯片最终工作到168MHz。
举报