我是靠谱客的博主 义气便当,最近开发中收集的这篇文章主要介绍基础——MCU的电源,复位和时钟系统(STM32为例)1. 电源供电2. 上电复位和手动复位4. 系统时钟的初始化寄存器源码分析5. 时钟配置,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

目录

1. 电源供电

2. 上电复位和手动复位

2.1 硬件复位

2.2 软件复位

3.1 晶振

3.2 内部时钟和外部时钟的不同

3.3 HSE和LSE外部电路的硬件设计

3.4 时钟具体理解

 

4. 系统时钟的初始化寄存器源码分析

5. 时钟配置

 


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; //复位寄存器 PLLCFGR
RCC->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_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET;
#else
SCB->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。

最后

以上就是义气便当为你收集整理的基础——MCU的电源,复位和时钟系统(STM32为例)1. 电源供电2. 上电复位和手动复位4. 系统时钟的初始化寄存器源码分析5. 时钟配置的全部内容,希望文章能够帮你解决基础——MCU的电源,复位和时钟系统(STM32为例)1. 电源供电2. 上电复位和手动复位4. 系统时钟的初始化寄存器源码分析5. 时钟配置所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(52)

评论列表共有 0 条评论

立即
投稿
返回
顶部