STM32F10X 时钟相关代码及分析

来源:互联网 发布:电视直播软件下载 编辑:程序博客网 时间:2024/05/19 04:05

今天学习STM32F107VC时钟相关寄存器以及控制,配合源代码,数据手册,写了点学习笔记供以后参考。

很重要的示意图:

时钟树


代码已加注释,如下:

//引自: system_stm32f10x.c 文件void SystemInit (void){  /* Reset the RCC clock configuration to the default reset state(for debug purpose) */  /* Set HSION bit */  //先将HSION 内部高速时钟使能,HSIRDY指示内部8MHz RC振荡器是否就绪  RCC->CR |= (uint32_t)0x00000001;   /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */#ifndef STM32F10X_CL //如果不是互联网型  //RCC->CFGR &= 0xF8FF0000  意思是清除低16位(SW,HPRE,PPRE1,PPRE2,ADCPRE),第24~26位(MC0)  RCC->CFGR &= (uint32_t)0xF8FF0000; #else   //STM32F107VC 是互联网型芯片,只是将MC0的位数多加了一位,由之前的第24~26位变成第24~27位。MC0[2:0] -> MC0[3:0]  //所以这里清除第24~27位和低16位  RCC->CFGR &= (uint32_t)0xF0FF0000; #endif /* STM32F10X_CL */       //这里值得注意,按最初的想法是这里可以合并起来做清除,而不用RCC->CR &= XXXX 然后又 RCC->CR &= XXXX,之所以这样做的原因  //是有些位必须在关闭关闭时钟时才能写入。如:PLLXTPRE PLLMUL PLLSRC USBPRE PLLON等  /* Reset HSEON, CSSON and PLLON bits */  //必须关闭HSEON后才能写HSEBYP位,所以这里将Reset HSEBYP bit单独拿出来放在下面。  RCC->CR &= (uint32_t)0xFEF6FFFF;   /* Reset HSEBYP bit */  RCC->CR &= (uint32_t)0xFFFBFFFF;  /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */  //清除第16~22位 注意这几个寄存器位很关键,必须将对应的时钟关闭后才能配置  //PLLSRC 选择时钟源作为PLL的时钟,PLLXTPRE 当且仅当PLLSRC选择HSE作为时钟时有效,PLLMUL 为倍频系数位,控制在2~16倍,但PLL的输出频率最大不能超过72M  //USBPRE 预分频后频率为48MHz或24MHz  RCC->CFGR &= (uint32_t)0xFF80FFFF; #ifndef STM32F10X_CL  /* Disable all interrupts and clear pending bits  */  //关闭所有时钟中断,并清除所有中断标志位 (CSSC PLL HSER HIS LSE LSI)  RCC->CIR = 0x009F0000; #else //F107VC指向下面的代码  /* Reset PLL2ON and PLL3ON bits */  //其实就是关闭PLL2和PLL3  RCC->CR &= (uint32_t)0xEBFFFFFF;   /* Disable all interrupts and clear pending bits  */  //将CSSC PLL3 PLL2 HSE HIS LSE LSI的中断标志位都清除,这里和C51有点不同,中断标志位寄存器分离了,  //如: HSI_RDYF  HSIRDYC,前者是只读,由硬件置位;后者只写,软件置位HSIRDYC来控制清除HSI_RDYF位。  RCC->CIR = 0x00FF0000;   /* Reset CFGR2 register */  RCC->CFGR2 = 0x00000000; //清空PREDIV PLL2MUL PLL3MUL等#endif /* STM32F10X_CL */      /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */  /* Configure the Flash Latency cycles and enable prefetch buffer */  SetSysClock();}//这个函数其实就是根据宏的定义调用对应的时钟设置函数,代码里面定义了SYSCLK_FREQ_72MHz,所以默认是调用SetSysClockTo72()static void SetSysClock(void){#ifdef SYSCLK_FREQ_HSE  SetSysClockToHSE();#elif defined SYSCLK_FREQ_24MHz  SetSysClockTo24();#elif defined SYSCLK_FREQ_36MHz  SetSysClockTo36();#elif defined SYSCLK_FREQ_48MHz  SetSysClockTo48();#elif defined SYSCLK_FREQ_56MHz  SetSysClockTo56();  #elif defined SYSCLK_FREQ_72MHz  SetSysClockTo72();#endif  /* If none of the define above is enabled, the HSI is used as System clock    source (default after reset) */ }//肯定是执行这个宏里面的代码#elif defined SYSCLK_FREQ_72MHz/**  * @brief  Sets System clock frequency to 72MHz and configure HCLK, PCLK2   *          and PCLK1 prescalers.   * @note   This function should be used only after reset.  * @param  None  * @retval None  */static void SetSysClockTo72(void){  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;    /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/      /* Enable HSE */      RCC->CR |= ((uint32_t)RCC_CR_HSEON); //开启外部高速振荡器   /* Wait till HSE is ready and if Time out is reached exit */  do  {    HSEStatus = RCC->CR & RCC_CR_HSERDY; //等待HSE进入稳定状态,就绪后会将HSERDY置1    StartUpCounter++;    } while((HSEStatus == 0) && (StartUpCounter != HSEStartUp_TimeOut)); //HSEStartUp_TimeOut 0x0500  //执行到这里有可能是超时,也有可能是HSE稳定了  if ((RCC->CR & RCC_CR_HSERDY) != RESET)   {    HSEStatus = (uint32_t)0x01; //这里表明却是稳定了  }  else  {    HSEStatus = (uint32_t)0x00;  }    //注意只有在系统时钟(SYSCLK)小于24MHz并且没有打开AHB的预分频器(即HCLK必须等于  //SYSHCLK)时,才能执行预取缓冲器的打开和关闭操作,所以一般是在初始化时进行操作。  if (HSEStatus == (uint32_t)0x01) //HSE 外部高速时钟晶振已经开启并且稳定  {    /* Enable Prefetch Buffer */    FLASH->ACR |= FLASH_ACR_PRFTBE; //开启 Flash 预取缓冲区//2等待周期,当 48MHz < SYSCLK ≤ 72MHz    /* Flash 2 wait state */    FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);    FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;    //AHB桥的时钟等于SYSCLK 72MHz//设置AHB预分频,CFGR[7:4] -> HPRE[3:0] |= 0x00000000,SYSCLK不分频    /* HCLK = SYSCLK */RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;       //APB2桥的时钟等于AHB时钟 72MHz//设置PPRE2预分频,CFGR[13:11] -> PPRE2[2:0] |= 0x00000000,HCLK不分频    /* PCLK2 = HCLK */    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;     //APB1桥的时钟等于AHB时钟的一半 36MHz,低速APB桥//设置PPRE1预分频,CFGR[10:8] -> PPRE1[2:0] |= 0x00000400,使用HCLK二分频    /* PCLK1 = HCLK/2 */    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; #ifdef STM32F10X_CL //STM32F107X     /* Configure PLLs ------------------------------------------------------*/    /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */    /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */        RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL |                              RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC);  //从数据手册的时钟树图上可以看出,PLL2唯一的时钟源就是PREDIV2预分频后的HSE,这里典型值取5分频,PREDIV2 = 25 / 5 = 5MHz//PLL2通过PLL2MUL倍频寄存器提高工作频率,典型值为8倍频,PLL2CLK = 40 MHz。//PLL的时钟源可以由PREDIV1和HSI二分频提供,这里选用PREDIV1。PREDIV1自身的时钟源又可以由HSE直接提供和PLL2提供,这里选用PLL2提供。//设置PREDIV2、PREDIV1、PREDIV1SRC预分频和PLL2MUL倍频寄存器值//HSE频率为25MHz,PREDIV2 5分频后传给PLL2的频率为5MHz,然后PLL2MUL 选择为8倍频,PLL2的频率为5 × 8 = 40MHz//使用PLL2为PREDIV1的时钟源,并且PREDIV1 5分频,PREDIV1的频率为8MHz    RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 |                             RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5);      /* Enable PLL2 */    RCC->CR |= RCC_CR_PLL2ON; //开启PLL2时钟    /* Wait till PLL2 is ready */    while((RCC->CR & RCC_CR_PLL2RDY) == 0) //等待稳定    {    }        /* PLL configuration: PLLCLK = PREDIV1 * 9 = 72 MHz */     RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL);//RCC_CFGR_PLLXTPRE_PREDIV1 0x00000000 不对输入时钟进行分频//选择PREDIV1为PLL的时钟源,并且设置PLL倍频为9倍频,所以频率为 72MHz    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 |                             RCC_CFGR_PLLMULL9); #else        /*  PLL configuration: PLLCLK = HSE * 9 = 72 MHz *///先清除PLLMUL 倍频系数位,PLLSRC 输入时钟源位,PLLXTPRE HSE分频位    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |                                        RCC_CFGR_PLLMULL));//使用HSE为PLL的输入时钟源,并且不对HSE分频,另外PLLMUL倍频系数选择9倍频,外部高速时钟被限定为8MHz频率    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9); //#endif /* STM32F10X_CL */    /* Enable PLL */    RCC->CR |= RCC_CR_PLLON; //将PLL开启    /* Wait till PLL is ready */    while((RCC->CR & RCC_CR_PLLRDY) == 0) //等待PLL就绪    {    }        /* Select PLL as system clock source *///将PLL配置为系统时钟SYSCLK频率    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));    RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;         /* Wait till PLL is used as system clock source *///检查SWS位是不是是10,如果是就表明是PLL作为系统时钟    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)     {    }  }  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 */        /* Go to infinite loop */    while (1)    {    }  }}#endif


总的来说,控制STM32F107 互联网系列的时钟可以分为下面几步:

第一步、 开启外部高速HSE时钟,需要其等待其稳定 (因为后面PLL2要使用其作为时钟源)

第二步、开启Flash闪存预取缓冲区 (这个东西还没有很了解)

第三步、设置AHB、APB1、APB2的预分频寄存器和PLL相关寄存器 (主要是选定PLL2 、PLL时钟源)

第四步、开启PLL2和PLL并选用PLL作为系统时钟源



原创粉丝点击