从库函数看STM32时钟启动过程

本文基于ST32F407ZGT6编写
——————————————
时钟系统是单片机的心脏,单片机初始化的第一步就是时钟系统的初始化。本文是基于STM32的库函数对时钟系统启动过程进行分析。

启动过程需要了解到的几个汇编语言:

  1. IMPORT :定义表示这是一个外部变量的标号,表示不是在本程序定义的。
  2. EXPORT :表示本程序里面定义的变量,是提供给其他模块调用的。

这两个关键字自是告诉编译器变量的来源,相当于C语言里的声明(与extern的作用差不多)

  1. LDRSTR ——字和无符号字节加载/存储指令
    LDR指令用于从内存中读取单一字或字节数据存入寄存器中
    STR指令用于将寄存器中的单一字或字节数据保存到内存

具体的例子:
LDR r0,[r1] ;将R1中的值存到r0中
STR r1,[r2] ; 将r1中的值存到r2所指定的地址中

  1. B跳转指令,常用的还有BL、BX、BLX

在startup_stm32f40_41xxx.s汇编启动文件中,有这样一段汇编代码

; Reset handlerSystemInit
;Reset_Handler 子程序开始
Reset_Handler    PROC
	;输出子程序 Reset_Handler 到外部文件
                 EXPORT  Reset_Handler             [WEAK]
;从外部文件中引入 main 函数	
        IMPORT  SystemInit
;从外部文件引入 SystemInit 函数
        IMPORT  __main

;把 SystemInit 函数调用地址加载到通用寄存器 r0
                 LDR     R0, =SystemInit
                 
;跳转到 r0 中保存的地址执行程序(调用 SystemInit 函数)			 
                 BLX     R0
                 
;把 main 函数调用地址加载到通用寄存器 r0
                 LDR     R0, =__main
                 
;跳转到 r0 中保存的地址执行程序(调用 main 函数)				 
                 BX      R0
				 
 ;Reset_Handler 子程序结束				 
                 ENDP

; Dummy Exception Handlers (infinite loops which can be modified)

可以看到,程序是从复位开始跑的;这就是为什么我们下载完程序以后要按一下复位按键的原因。然后可以看到在进入main函数之前还会进入SystemInit函数,时钟初始化就是在这个函数里面进行的。
SystemInit函数:

void SystemInit(void)
{
  /* FPU settings ------------------------------------------------------------*/
	//是否使用FPU(浮点计算)功能,如果使用那么要定义两个变量为1
  #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
    SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */
  #endif
  /* Reset the RCC clock configuration to the default reset state ------------*/
  /* Set HSION bit */
//CR最后一位置1,使能内部高速时钟;设置时钟为内部高速时钟启动
  RCC->CR |= (uint32_t)0x00000001;

  /* Reset CFGR register */
	//复位时钟配置寄存器
  RCC->CFGR = 0x00000000;

  /* Reset HSEON, CSSON and PLLON bits */
	//&=进行清零操作,把CR寄存器的16、19、24位清零
  RCC->CR &= (uint32_t)0xFEF6FFFF;

  /* Reset PLLCFGR register */
	//0x24003010是PLLCFGR的默认复位值,中文参考手册有写
  RCC->PLLCFGR = 0x24003010;

  /* Reset HSEBYP bit */
	//&=清零操作,给CR寄存器第18位清零
  RCC->CR &= (uint32_t)0xFFFBFFFF;

  /* Disable all interrupts */
	//CIR时钟中断寄存器复位
  RCC->CIR = 0x00000000;

#if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
  SystemInit_ExtMemCtl(); 
#endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */
         
  /* Configure the System clock source, PLL Multiplier and Divider factors, 
     AHB/APBx prescalers and Flash settings ----------------------------------*/
 //跳转到SetSysClock()函数
  SetSysClock();

  /* Configure the Vector Table location add offset address ------------------*/
//配置向量表位置,添加偏移地址
#ifdef VECT_TAB_SRAM
  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif
}

这段代码注释已经放在了函数中了
这段代码主要是配置RCC_CR寄存器以及CFGR、PLLCFGR寄存器的复位。
1、先确定单片机是否使用浮点计算功能
2、然后将单片机时钟设计为内部高速时钟启动(HSI),因为单片机刚开始启动时采用内部高速时钟,响应速度快;然后就是把CR几个控制位关闭,以及相关寄存器CFGR、PLLCFGR复位。
3、接着跳转到了SetSysClock()函数:
SetSysClock()函数:

static void SetSysClock(void)
{
#if defined (STM32F40_41xxx) || defined (STM32F427_437xx) || defined (STM32F429_439xx) || defined (STM32F401xx)
/******************************************************************************/
/*            PLL (clocked by HSE) used as System clock source                */
/******************************************************************************/
	/*定义了两个变量:
	StartUpCounter:用于计数,与外部时钟准备时间(0x05000)作比较
	HSEStatus:用于判断外部高速时钟是否已经准备好的标志位
	*/
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  
  /* Enable HSE */
//把CR寄存器16位(HSE ON)置1,打开外部时钟使能位
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);
 
  /* Wait till HSE is ready and if Time out is reached exit */
  do
  {
	
		/*退出循环的两个条件满足其一即可:
		1、CR寄存器第17位置1表示HSE已经准备好了,即HSEStatus不等于0
		2、当	StartUpCounter自增到5000	,定时已到
		*/
    HSEStatus = RCC->CR & RCC_CR_HSERDY;	//读取CR寄存器第17位(HSERDY),判断外部时钟是否准备好了
    StartUpCounter++;
  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

	//确定CR寄存器第17位(HSERDY)已经置1了
  if ((RCC->CR & RCC_CR_HSERDY) != RESET)
  {
    HSEStatus = (uint32_t)0x01;//将HSEStatus取值0x01,相当于设定一个标志位
  }
  else
  {
    HSEStatus = (uint32_t)0x00;
  }

  if (HSEStatus == (uint32_t)0x01)//检查HSEStatus标志位
  {
    /* Select regulator voltage output Scale 1 mode */
		// APB1ENR寄存器第28位(PWREN)置1,电源接口时钟使能
    RCC->APB1ENR |= RCC_APB1ENR_PWREN;
		
		/*
		配置PWR_CR寄存器,这个寄存器不是时钟的寄存器而是电源的寄存器,
		这些位用来控制内部主调压器的输出电压,以便在器件未以最大频率工作时
		使性能与功耗实现平衡具体内容看中文参考手册101页
		这里把PWR_CR寄存器的14-15位(VOS)设置为11,即11:级别模式1(复位值)
		*/
    PWR->CR |= PWR_CR_VOS;

    /* HCLK = SYSCLK / 1*/
		//RCC_CFGR_HPRE_DIV1=0,所以一下相当于将CFGR复位按缺省值配置
		/*CFGR寄存器是时钟配置寄存器,这个寄存器决定了STM32系统的时钟配置,
		内容比较多,建议直接看《STM32中文参考手册118页》
		如果你想要设置时钟为外部时钟还是内部时钟,时钟怎么分频就看这个寄存器了*/
    RCC->CFGR |= RCC_CFGR_HPRE_DIV1;

#if defined (STM32F40_41xxx) || defined (STM32F427_437xx) || defined (STM32F429_439xx)      
    /* PCLK2 = HCLK / 2*/
	//将CFGR的15位置1,即PPRE2为100,PCLK2为HCLK二分频
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
    
    /* PCLK1 = HCLK / 4*/
	//将CFGR的10、12位置1,即PPRE1为101,PCLK1为HCLK四分频
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
		
#endif /* STM32F40_41xxx || STM32F427_437x || STM32F429_439xx */

/*以下代码是F401的,为了方便查看代码我把它注释掉了*/
//#if defined (STM32F401xx)
//    /* PCLK2 = HCLK / 2*/
//    RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
//    
//    /* PCLK1 = HCLK / 4*/
//    RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;
//#endif /* STM32F401xx */
  
	
    /* Configure the main PLL */
		/*这个语句比较复杂,我把它分出来就是把值0x0740 0d88赋值给PLLCFGR
		就是把PLLCFGR的第3、7、8、10、12、14、22、24、25、26位置1(0X0740 5408)
		第22位置1,选择 HSE 振荡器时钟作为 PLL 和 PLLI2S 时钟输入
		其中:PLLQ=7;PLLP=2;PLLN=336;PLLM=8
		计算公式:
		● f(VCO 时钟) = f(PLL 时钟输入) × (PLLN / PLLM)
		● f(PLL 常规时钟输出) = f(VCO 时钟) / PLLP
		● f(USB OTG FS, SDIO, RNG 时钟输出) = f(VCO 时钟) / PLLQ	
		*/
    RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
                   (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);

    /* Enable the main PLL */
	//CR寄存器第24位(PLLON)置1,开启PLL
    RCC->CR |= RCC_CR_PLLON;

    /* Wait till the main PLL is ready */
	//查询CR寄存器第25位(PLLRDY)是否置1,以确定PLL是否已经开始
    while((RCC->CR & RCC_CR_PLLRDY) == 0)
    {
    }


/*以下代码是STM32F427_437x || STM32F429_439xx 的,为了方便查看代码我把它注释掉了*/		
//#if defined (STM32F427_437xx) || defined (STM32F429_439xx)
//    /* Enable the Over-drive to extend the clock frequency to 180 Mhz */
//    PWR->CR |= PWR_CR_ODEN;
//    while((PWR->CSR & PWR_CSR_ODRDY) == 0)
//    {
//    }
//    PWR->CR |= PWR_CR_ODSWEN;
//    while((PWR->CSR & PWR_CSR_ODSWRDY) == 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_5WS;
//#endif /* STM32F427_437x || STM32F429_439xx  */

#if defined (STM32F40_41xxx)     
    /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
    FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;
#endif /* STM32F40_41xxx  */

/*以下代码是F401的,为了方便查看代码我把它注释掉了*/
//#if defined (STM32F401xx)
//    /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
//    FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_2WS;
//#endif /* STM32F401xx */

    /* Select the main PLL as system clock source */
		//把CFGR的后两位清零,第二位置1,系统时钟由HSE更改为PLL
		//如果想改时钟输入在这一步改即可
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    RCC->CFGR |= RCC_CFGR_SW_PLL;

    /* Wait till the main PLL is used as system clock source */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
    {
    }
  }
  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 */
  }
	
	/*******下面还有一大段代码是STM32F411xE的,就不粘贴出来了***********/
}

具体注释已经在代码中
这段代码主要配置了RCC_CR、CFGR、PLLCFGR寄存器;
还有配置PWR_CR寄存器(这个寄存器不是RCC的寄存器而是电源的寄存器)。
这段代码比较长,涉及到相关寄存器每个位的操作,需要一边看代码一边看《STM32中文参考手册》才能搞懂。
关于STM32系统时钟的选择,PLL倍频的内容就在这段代码中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值