前言

看懂MCU系统框架和时钟树,是使用任何一款单片机的基础,尤其是时钟树部分,涉及到后续各个外设的工作频率以及systick滴答定时器的定时周期。


一、STM32F1系统架构

STM32F1包含4个主动单元和4个被动单元
4个主动单元
M3内核DCode总线(D-bus)、M3内核系统总线(S-bus)、通用DMA1、通用DMA2
4个被动单元
内部SRAM、内部Flash、FSMC、AHB到APB的(它连接所有的APB外设)

其中,AHB称为高级高性能总线,APB成为高级外围总线。
M3内核的最大时钟频率为72Mhz,而AHB总线最大时钟频率为72Mhz,APB1总线最大时钟频率为36Mhz,APB2总线最大时钟频率为72Mhz。
由于不同外设的工作时钟频率需求不同,所以分了两条外设总线来满足不同外设。

二、系统架构图

系统架构图
如何查看系统框架图:
1、首先找到总线矩阵
2、以总线矩阵为中心,找到4个主动单元
3、找到4个被动单元

三、认识时钟树

首先,什么是时钟?
时钟
时钟是具有周期性的脉冲信号,最常用的是占空比50%的方波。它如同人体的心脏,为单片机片上外设运行提供基本节拍。这种周期性信号为CPU执行指令、存储器访问、外设操作等提供了时间基准。
​​时钟树​​是单片机时钟系统的核心架构,它如同精密的"血液循环系统",将时钟信号从源头分配到芯片的各个外设单元。时钟树的设计直接影响系统的稳定性、功耗和性能,是单片机架构中最关键的部分之一。

1.时钟源

时钟树的四个主要时钟源​​:包括LSI(内部低速时钟)、LSE(外部低速时钟)、HSI(内部高速时钟)和HSE(外部高速时钟)。其中LS代表低速,HS代表高速;"i"表示内部(internal),"e"表示外部(external)。
时钟源
外部晶体振荡器的稳定性和准确性较好,但需要增加成本;内部RC振荡器不需要额外增加成本,但稳定性一般。通常项目开发对成本控制要求较为严格时,优先考虑内部RC振荡器,尤其是针对家用产品。

2.时钟树简图

高速时钟:
高速时钟树
注意,当使用内部高速时钟(HSI)作为时钟源时,系统时钟sysclk最大时钟频率为8Mhz/2*16 = 64Mhz。

低速时钟:
低速时钟树
低速内部时钟LSI通常作为独立看门狗IWDG的时钟源

3.时钟树原图

打开参考手册6.2节
时钟树

4.SYSTICK滴答定时器

SysTick,即滴答定时器,包含在Cortex内核里面,核心是一个24位的递减计数器。
通过对AHB总线(HCLK)8分频后作为SysTick滴答定时器的时钟源,或者将HCLK不分频、直接作为SysTick滴答定时器的时钟源。可通过SysTick_CLKSourceConfig函数配置时钟源,并通过SysTick_Config函数配置定时器周期。
通常将SysTick滴答定时器配置为毫秒级,并将项目中的状态机部分会放到滴答定时器中断内执行。

四、手把手配置时钟(以标准库为例)

1、从STM32启动文件(startup_stm32f10x_hd_vl.s)可以看出芯片复位后进入了SystemInit函数。

; Reset handler
Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
     IMPORT  __main
     IMPORT  SystemInit
                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

2、通过一层层检索,找到SetSysClock函数(system_stm32f1xx.c),需要注意的是,在官方例程里,默认时钟源为外部高速时钟HSE,若板子上不带外部晶振,直接烧录该程序是导致执行异常。这里将时钟源配置为内部高速时钟HSI,并将HCLK配置为64Mhz

/**
  * @brief  Configures the System clock frequency, AHB/APBx prescalers and Flash
  *         settings.
  * @note   This function should be called only once the RCC clock configuration
  *         is reset to the default reset state (done in SystemInit() function).
  * @param  None
  * @retval None
  */
static void SetSysClock(void)
{
  __IO uint32_t StartUpCounter = 0, HSIStatus = 0;
  
  /* SYSCLK, HCLK, PCLK configuration ----------------------------------------*/
  /* Enable HSI */    
  RCC->CR |= ((uint32_t)RCC_CR_HSION);//使能内部高速时钟HSI
 
  /* Wait till HSI is ready and if Time out is reached exit */
  do
  {
    HSIStatus = RCC->CR & RCC_CR_HSIRDY;
    StartUpCounter++;  
  } while((HSIStatus == 0) && (StartUpCounter != HSI_STARTUP_TIMEOUT));//等待HSI稳定

  if ((RCC->CR & RCC_CR_HSIRDY) != RESET)
  {
    HSIStatus = (uint32_t)0x01;
  }
  else
  {
    HSIStatus = (uint32_t)0x00;
  }  

  if (HSIStatus == (uint32_t)0x01)
  {
    /* Enable Prefetch Buffer and set Flash Latency */
    FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;
 
    /* HCLK = SYSCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;//AHB总线1分频
      
    /* PCLK = HCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE_DIV2;//低速APB1总线2分频,64 /2 = 32Mhz

    /* PLL configuration */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL));//复位时钟配置
    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSI_DIV2 | RCC_CFGR_PLLMULL16); //8Mhz /2 *16 = 64Mhz
            
    /* 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 */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;    //选择PLL时钟作为HCLK

    /* Wait till PLL is used as system clock source */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)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 */
  }  
}

3、配置SYSTICK滴答定时器,这部分在main函数里

uint32_t SystemCoreClock    = 64000000;
  /* Setup SysTick Timer for 1 msec interrupts.
     ------------------------------------------
    1. The SysTick_Config() function is a CMSIS function which configure:
       - The SysTick Reload register with value passed as function parameter.
       - Configure the SysTick IRQ priority to the lowest value (0x0F).
       - Reset the SysTick Counter register.
       - Configure the SysTick Counter clock source to be Core Clock Source (HCLK).
       - Enable the SysTick Interrupt.
       - Start the SysTick Counter.
    
    2. You can change the SysTick Clock source to be HCLK_Div8 by calling the
       SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8) just after the
       SysTick_Config() function call. The SysTick_CLKSourceConfig() is defined
       inside the misc.c file.

    3. You can change the SysTick IRQ priority by calling the
       NVIC_SetPriority(SysTick_IRQn,...) just after the SysTick_Config() function 
       call. The NVIC_SetPriority() is defined inside the core_cm0.h file.

    4. To adjust the SysTick time base, use the following formula:
                            
         Reload Value = SysTick Counter Clock (Hz) x  Desired Time base (s)
    
       - Reload Value is the parameter to be passed for SysTick_Config() function
       - Reload Value should not exceed 0xFFFFFF
   */
   SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);//配置HCLK作为作为滴答定时器的时钟源
  if (SysTick_Config(SystemCoreClock / 1000))//配置滴答定时器的定时周期,并打开滴答定时器中断
  { 
    /* Capture error */ 
    while (1);
  }

总结

理解系统框架和时钟树是STM32开发的基础,尤其时钟配置直接影响外设工作频率和功耗。实际项目中需权衡成本与稳定性选择合适的时钟源,并合理配置SYSTICK定时器。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐