打印
[STM32]

立创商城STM32F系列——STM32F4时钟初始化配置

[复制链接]
888|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
小乐Tzz|  楼主 | 2018-8-23 10:36 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
STM32F4时钟初始化配置
/**********************************************************/
STM32F4 时钟系统初始化是在 system_stm32f4xx.c中的 SystemInit()函数中完成的。 对于系统时钟关键寄存器设置主要是在 SystemInit 函数中调用 SetSysClock()函数来设置的。我们可以先看看 SystemInit ()函数体:
void SystemInit(void)
{
   /* FPU settings ------------------------------------------------------------*/
   #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 */
   RCC->CR |= (uint32_t)0x00000001;
   /* Reset CFGR register */
   RCC->CFGR = 0x00000000;
   /* Reset HSEON, CSSON and PLLON bits */
   RCC->CR &= (uint32_t)0xFEF6FFFF;
   /* Reset PLLCFGR register */
   RCC->PLLCFGR = 0x24003010;
   /* Reset HSEBYP bit */
   RCC->CR &= (uint32_t)0xFFFBFFFF;
   /* Disable all interrupts */
   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();
   /* 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
}
SystemInit函数开始先进行浮点运算单元设置,然后是复位PLLCFGR,CFGR寄存器,同时通过设置 CR 寄存器的 HSI 时钟使能位来打开 HSI 时钟。默认情况下如果 CFGR 寄存器复位,那么是选择HSI作为系统时钟,这点大家可以查看RCC->CFGR 寄存器的位描述最低2位可以得知,当低两位配置为 00的时候(复位之后),会选择 HSI振荡器为系统时钟。也就是说,调用 SystemInit 函数之后,首先是选择 HSI 作为系统时钟。


在设置完相关寄存器后,接下来SystemInit函数内部会调用 SetSysClock函数。这个函数比较长,我们就把函数一些关键代码行截取出来给大家讲解一下。这里我们省略一些宏定义标识符值的判断而直接把针对STM32F407 比较重要的内容贴出来:
static void SetSysClock(void)
{
   __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
   /*使能 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;
   }

   if (HSEStatus == (uint32_t)0x01)
   {
     /* Select regulator voltage output Scale 1 mode */
     RCC->APB1ENR |= RCC_APB1ENR_PWREN;
     PWR->CR |= PWR_CR_VOS;

     /* 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;
     /* PCLK2 = HCLK / 2*/
     RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
      
     /* PCLK1 = HCLK / 4*/
     RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;

     /* Configure the main 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)
     {
     }
      
     /* 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;

   
     /*  设置主PLL时钟为系统时钟源  */
     RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
     RCC->CFGR |= RCC_CFGR_SW_PLL;

     /*  等待设置稳定(主PLL作为系统时钟源)*/
     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 */
   }

}
这段代码的大致流程是这样的:先使能外部时钟 HSE,等待 HSE 稳定之后,配置AHB,APB1,APB2 时钟相关的分频因子,也就是相关外设的时钟。等待这些都配置完成之后,打开主PLL时钟,然后设置主PLL作为系统时钟 SYSCLK时钟源。如果HSE 不能达到就绪状态(比如外部晶振不能稳定或者没有外部晶振),那么依然会是HSI作为系统时钟。
在这里要特别提出来,在设置主PLL时钟的时候,会要设置一系列的分频系数和倍频系数参数。大家可以从 SetSysClock函数的这行代码看出:
RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
                    (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
这些参数是通过宏定义标识符的值来设置的。默认的配置在 System_stm32f4xx.c 文件开头的地方配置。对于我们开发板,我们的设置参数值如下:
#define PLL_M       8
#define PLL_Q      7
#define PLL_N      336
#define PLL_P      2
所以我们的主PLL时钟为:
PLL=8MHz * N/ (M*P)=8MHz* 336 /(8*2) = 168MHz
在开发过程中,我们可以通过调整这些值来设置我们的系统时钟。
  这里还有个特别需要注意的地方,就是我们还要同步修改 stm32f4xx.h 中宏定义标识符HSE_VALUE 的值为我们的外部时钟:
#if !defined  (HSE_VALUE)   
  #define HSE_VALUE    ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */
   
#endif /* HSE_VALUE */
这里默认固件库配置的是25000000,我们外部时钟为8MHz,所以我们根据我们硬件情况修改为8000000即可。
讲到这里,大家对 SystemInit 函数的流程会有个比较清晰的理解。那么 SystemInit 函数是怎么被系统调用的呢?SystemInit是整个设置系统时钟的入口函数。这个函数对于我们使用ST提供的 STM32F4 固件库的话,会在系统启动之后先执行main函数,然后再接着执行SystemInit函数实现系统相关时钟的设置。这个过程设置是在启动文件 startup_stm32f40_41xxx.s中间设置的,我们接下来看看启动文件中这段启动代码:
; Reset handler
Reset_Handler    PROC
                  EXPORT  Reset_Handler             [WEAK]
         IMPORT  SystemInit
         IMPORT  __main

                  LDR     R0, =SystemInit
                  BLX     R0
                  LDR     R0, =__main
                  BX      R0
                  ENDP
这段代码的作用是在系统复位之后引导进入main函数,同时在进入main函数之前,首先
要调用 SystemInit系统初始化函数完成系统时钟等相关配置。
最后我们总结一下SystemInit()函数中设置的系统时钟大小:
SYSCLK(系统时钟)                     =168MHz
  AHB总线时钟(HCLK=SYSCLK)            =168MHz
  APB1总线时钟(PCLK1=SYSCLK/4)        =42MHz
  APB2总线时钟(PCLK2=SYSCLK/2)        =84MHz
  PLL主时钟                          =168MHz
/**********************************************************/


【立创商城】STM32F103CBT6 托盘  https://item.szlcsc.com/8796.html

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

70

主题

70

帖子

0

粉丝