你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32F2系列系统时钟默认配置

[复制链接]
STMCU小助手 发布时间:2021-12-2 14:28

有个项目要用到STM32F207Vx单片机,找到网上的例子照猫画虎的写了几个例子,比如ADC,可是到了ADC多通道转换的时候就有点傻眼了,这里面的时钟跑的到底是多少M呢?单片机外挂的时钟是25M,由于该单片机时钟系统较为复杂,有内部高/低、外部高/低 、PLL锁相环时钟,又有AHB总线时钟、APB1/2时钟,而例子中很少讲到系统时钟的默认配置是怎么配置呢?那么就发点时间研究下这个单片机内部的复杂时钟系统吧。

下图是STM32F2系列的时钟树结构图:

1、内部高速时钟HSI、外部高速时钟HSE和PLL时钟PLLCLK时钟都接到了SW开关处,通过SW选择哪一路作为SYSCLK,SYSCLK经过AHB分频器进行分频得到HCLK,APB1和APB2是挂在总线AHB上的,通过APB1和APB2分频得出fpclk1和fpclk2。

2、PLL输入时钟源主要是靠外部高速时钟和内部高速时钟作为时钟源,通过PLLCFGR寄存器的bit22来选择具体哪一路作为时钟源。选择好了时钟源进入/M分频器,也就是PLLM进行分频,送入VCO,在通过xN,进行倍频,也就是PLLN1)通过/P进行分频(PLLP)得到PLLCLK;(2)通过/Q分频(PLLQ),得到PLL48CK。

然后边看代码边对照结构图进行分析,看软件如何给单片机配置系统时钟的。


301052464747191.jpg


然后找到启动代码“startup_stmf32xx.s”,该代码是用汇编写的,可以看到,在调用main函数之前,是先调用了SystemInit函数的,该函数是在“system_stm32f2xx.c”中

  1. ; Reset handlerReset_Handler    PROC
  2.                  EXPORT  Reset_Handler             [WEAK]
  3.         IMPORT  SystemInit
  4.         IMPORT  __main
  5.                  LDR     R0, =SystemInit
  6.                  BLX     R0
  7.                  LDR     R0, =__main
  8.                  BX      R0
  9.                  ENDP
复制代码

代码如下,变量直接赋个16进制的数,都不知道是啥意思,目的是干什么的,不知道,所以看下面代码时最好对照STM32F2x用户手册。当然这个只是一个初始化,待会主要看SetSysClock();这个函数,在调用该函数之前,我们知道单片机是先启用了内部高速时钟等一些配置。


  1. void SystemInit(void)
  2. {
  3.   /* Reset the RCC clock configuration to the default reset state ------------*/
  4.   /* Set HSION bit */
  5.   RCC->CR |= (uint32_t)0x00000001; //RCC_CR复位值0x0000_xx83,内部高速时钟使能,也就是说上电开始就使用内部高速时钟,16MHZ

  6.   /* Reset CFGR register */
  7.   RCC->CFGR = 0x00000000;    //通过开关SW选择内部高速时钟作为系统时钟16MHZ
  8.                                                         //AHB prescaler 不分频
  9.                                                       //APB Low speed prescaler (APB1) 不分频,fplck1 = 16MHZ
  10.                                                         //APB high-speed prescaler (APB2)不分频,fplck2 = 16MHZ
  11.                               //MCO1和MCO2时钟输出等配置可参考用户手册Page95

  12.   /* Reset HSEON, CSSON and PLLON bits */
  13.   RCC->CR &= (uint32_t)0xFEF6FFFF;

  14.   /* Reset PLLCFGR register */
  15.   RCC->PLLCFGR = 0x24003010; //RCC_CFGR复位值是0x2400_3010

  16.   /* Reset HSEBYP bit */
  17.   RCC->CR &= (uint32_t)0xFFFBFFFF;  //对bit18 HSEBYP 设置为0,外部高速时钟被禁止

  18.   /* Disable all interrupts */
  19.   RCC->CIR = 0x00000000;  //所有时钟中断都被禁止

  20. #ifdef DATA_IN_ExtSRAM
  21.   SystemInit_ExtMemCtl();
  22. #endif /* DATA_IN_ExtSRAM */
  23.          
  24.   /* Configure the System clock source, PLL Multiplier and Divider factors,
  25.      AHB/APBx prescalers and Flash settings ----------------------------------*/
  26.   SetSysClock();

  27.   /* Configure the Vector Table location add offset address ------------------*/
  28. #ifdef VECT_TAB_SRAM
  29.   SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
  30. #else
  31.   SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
  32. #endif
  33. }
复制代码


在SystemInit(void)函数中在配置完一些参数后,还调用了SetSysClock()函数,该函数代码如下

  1. static void SetSysClock(void)
  2. {
  3. /******************************************************************************/
  4. /*            PLL (clocked by HSE) used as System clock source                */
  5. /******************************************************************************/
  6.   __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  7.   
  8.   /* Enable HSE */
  9.   RCC->CR |= ((uint32_t)RCC_CR_HSEON);  //外部高速时钟使能,25MHZ

  10.   /* Wait till HSE is ready and if Time out is reached exit */  //外部时钟使能后,得需要一点时间到达各个端口
  11.   do
  12.   {
  13.     HSEStatus = RCC->CR & RCC_CR_HSERDY; //如果RCC_CR_HSERDY为0,说明外部时钟还没准备好,1说明外部时钟已准备好
  14.     StartUpCounter++;//对读的次数进行累加,当累加次数到达1280次时,就意味着启动时间超时
  15.   } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

  16.   if ((RCC->CR & RCC_CR_HSERDY) != RESET)//这里判断上面的do while循环是因哪个条件结束的
  17.   {
  18.     HSEStatus = (uint32_t)0x01;   //说明时钟已准备好了,才结束do whlie循环
  19.   }
  20.   else
  21.   {
  22.     HSEStatus = (uint32_t)0x00;  //说明是因为超时了而退出do while循环
  23.   }

  24.   if (HSEStatus == (uint32_t)0x01)
  25.   {
  26.     /* HCLK = SYSCLK / 1*/
  27.     RCC->CFGR |= RCC_CFGR_HPRE_DIV1; //AHB不分频,AHB出来后时钟就是sysclk=120M
  28.       
  29.     /* PCLK2 = HCLK / 2*/
  30.     RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;//APB2 2分频,fpclk2 = sysclk/2 = 60M
  31.    
  32.     /* PCLK1 = HCLK / 4*/
  33.     RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; //APB1 4分频,fplck1 = sysclk/4 = 30M

  34.     /* Configure the main PLL */ //主要对PLL和PPI2S 进行配置
  35.     RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
  36.                    (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
  37. //配置完后RCC->PLLCFGR = 0x05403c19,然后对照寄存器RCC_PLLCFGR查看哪些位对应哪些功能
  38. //通过bit5~bit0可计算出PLLM=25,那么input VCO = PLL input clock /PLLM = 25M/25 = 1M,对应时钟结构图中的/M
  39. //通过bit14~bit6可计算出倍频因子PLLN = 240,那么VCO output clock = PLLN * input VCO = 240 * 1 = 240M,对应时钟结构图中的xN
  40. //bit17~bit16可计算出分频因子PLLP = 2,那么PLLCLK = VCO output clock /PLLP = 240/2 = 120M
  41. //bit22是选择给PLL输入的时钟源,输入时钟源有外部和内部高速时钟,这里选择的是外部高速时钟即PLL input clock = HSE =25M
  42. //bit27~24可计算出分频因子PLLQ = 5,那么PLL48CK = VCO output clock/PLLQ = 240/5 = 48M
  43.         
  44.     /* Enable the main PLL */
  45.     RCC->CR |= RCC_CR_PLLON; //使能PLL

  46.     /* Wait till the main PLL is ready */
  47.     while((RCC->CR & RCC_CR_PLLRDY) == 0)
  48.     {
  49.     }
  50.         //到这里RCC->CR已配置完,最终值0x03036783
  51.         //通过查看用户手册知道,内部高速时钟、外部高速时钟、PLL时钟都已开启
  52.         
  53.     /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
  54.     FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_3WS;

  55.     /* Select the main PLL as system clock source */
  56.     RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
  57.     RCC->CFGR |= RCC_CFGR_SW_PLL;
  58.         
  59.         //到这里RCC->CFGR已配置完,最终值是0x0000_940A
  60.         //通过查看用户手册,知道,PLL时钟作为系统时钟即120M
  61.         //AHB不分频,即HCLK = 120M
  62.         //APB1 4分频,即fpclk1 = 120/4=30M
  63.         //APB2 2分频,即fpclk2 = 120/2=60M

  64.     /* Wait till the main PLL is used as system clock source */
  65.     while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
  66.     {
  67.     }
  68.   }
  69.   else
  70.   { /* If HSE fails to start-up, the application will have wrong clock
  71.          configuration. User can add here some code to deal with this error */
  72.   }

  73. }
复制代码


OK,分析完这段代码后,调用系统固件函数后,现在知道了时钟树结构图中右边出来的时钟是多少MHz了吧。

总结:

1、使用的是外部时钟25MHZ,通过PLL进行分频倍频分频得到PLLCLK 120M,PLLCLK作为系统时钟SYSCLK。

2、APB1出来是30M,也就是FPCLK1。

3、APB2出来是60M,也就是FPCLK2。





收藏 评论0 发布时间:2021-12-2 14:28

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版