STM32F103 时钟树以及系统时钟配置

        1.时钟树

        

STM32的时钟树大致可分为3部分:

  •  陶瓷晶振输入,进入预分频器
  • 预分频器输出信号给到PLL倍频器
  • 最终AHB桥总线速度,以及各总线的速度

        晶振信号输入到芯片后有两条支路可以走,第一条是直接以原始频率不经过PLL倍频器,直接作为系统时钟(SYSCLK)。第二条是从预分频器流入,随后将输出信号输入PLL倍频器,最终得到X*HSE的SYSCLK(X为倍频倍率)。假如输入为8MHz,那么X为9时才能达到最大频率72MHz。

        1.1 PLLXTPRE   

        PLLXTPRE(预分频器)的作用是为了将大于16Mhz的信号进行"/2"操作,因为在STM32中晶振的输入频率最大不超过16MHz,所以当大于16MHz时需要进行分频操作。在市面上可以有些最小系统板用的是24MHz晶振,这是因为他们为了从成本考虑,24MHz的晶振比常用8MHz要便宜。当使用24MHz时,经过的路径是这样的,所以信号进过预分频器最终输出频率是12MHz

 

        8MHz时路径是这样的,所以信号进过预分频器最终输出频率是8MHz

        1.2 PLLSRC         

        PLLSRC的作用是选择系统时钟源,从图中可以看到有两条输入支路,一条是HSI经过"/2"的预分频后信号输入PLLSRC,一条是HSE经过预分频后输出的信号。

        

        1.3 PLLMUL       

        PLLMUL 的作用是将PLLSRC的输出信号进行x倍的倍频,x可以为1-16。

        经过被倍频的信号仍未作为SYSCLK,它还需要进过一个选择器,该选择器从图中可以看到有多条输入支路

        一条是HSI(8MHz)原始信号直接输入作为SYSCLK,HSI拥有一个很大的缺陷,那就是无论进过如何校准信号质量依然很差。

        一条是HSE原始信号直接输入作为SYSCLK,HSE信号一般都为陶瓷晶振信号。

        一条是PLLMUL倍频后信号作为SYSCLK。

        除却上文所说的三条输入支路,还有两个旁路,一个SW,一个是CSS。其中前者的作用是开关,意义就在于选择SYSCLK的最终信号源。后者的作用是时钟安全系统,作用是当HSE时钟源出现问题后,强制将SYSCLK的信号源切换为HSI。

        1.4 总线桥       

        

        只要理解了SYSCLK的时钟频率是如何产生的,那么关于总线桥的频率就不难理解了。STM32F103的常用频率是72MHz,所以这里就以72MHz为例。在得到SYSCLK信号后会优先经过AHB桥进行预分频,此处默认为1,即不分频,随后流入APB桥、滴答定时器(SYSTICK)以及DMA总线。APB桥又分为APB1和APB2,同时还可以进行一个预分频。APB1在32中的定义是低速总线桥,从图中可以看到它的最大支持频率是36MHz,所以在流入APB1时需要进行一个"/2"的预分频。在APB1桥下控制着TIM2,3,4子对象。APB2在32中的定义是高速总线桥,从图中可以看到它的最大支持频率是72MHz,所以在流入APB2时不需要进行预分频,即分频系数为1。在其下面拥有TIM1以及ADC子对象,也就是这两者的最大速度为72MHz。

        除却各总线桥预分频器,图中还有多个与门,与门的作用就是是否输出该总线的信号。

        2. 系统时钟配置

        2.1 直接上代码

void SystemInit (void)
{
  /* 启动HSI时钟 */
  RCC->CR |= (uint32_t)0x00000001;

  /* 复位 SW, AHB, APB1, APB2, ADCPRE and MCO bits */
  RCC->CFGR &= (uint32_t)0xF8FF0000;
  
  /* 复位 HSEON, CSSON and PLLON bits */
  RCC->CR &= (uint32_t)0xFEF6FFFF;

  /* 复位 HSEBYP bit */
  RCC->CR &= (uint32_t)0xFFFBFFFF;

  /* 复位 PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
  RCC->CFGR &= (uint32_t)0xFF80FFFF;

  /* 清除所有时钟中断标志位  */
  RCC->CIR = 0x009F0000;

  /* 配置系统时钟频率、AHB、APB2和APB1预分频器 */
  /* 配置闪存延迟周期并启用预取缓冲区 */
  SetSysClockTo72();
}
static void SetSysClockTo72(void)
{
  uint32_t StartUpCounter = 0, HSEStatus = 0;
  
  /* 配置 SYSCLK, HCLK, PCLK2 and PCLK1  ---------------------------*/    
  /* 使能 HSE */    
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);
 
  /* 等待HSE晶振就绪 */
  do
  {
    HSEStatus = RCC->CR & RCC_CR_HSERDY;    //读CR寄存器中就绪位
    StartUpCounter++;                       //超时计数
  } while((HSEStatus == 0) && (StartUpCounter != 0x0500));     //等待超时,以及标志位置位

  if ((RCC->CR & RCC_CR_HSERDY) != RESET)   //此处语句的作用是检测HSE是否就绪
  {
    HSEStatus = (uint32_t)0x01;     //就绪标志 1
  }
  else
  {
    HSEStatus = (uint32_t)0x00;     //不就绪标志 0
  }  

  if (HSEStatus == (uint32_t)0x01)  //HSE就绪
  {
    /* SYSCLK预分频系数为1,即不分频 */
    RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
      
    /* APB2预分频系数为1,即不分频 =SYSCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
    
    /* APB1预分频系数为2,即 APB2 = SYSCLK/2 */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;

    /*  初始化HSE预分频器,时钟源选择器,PLL倍频器 */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
                                        RCC_CFGR_PLLMULL));

    /*时钟源选择为 HSE,进行9倍频 SYSCLK = HSE*9 = 8MHz*9 = 72MHz */
    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);  

    /* 使能PLL倍频器 */
    RCC->CR |= RCC_CR_PLLON;

    /* Wait till PLL is ready */
    while((RCC->CR & RCC_CR_PLLRDY) == 0)   //等待PLL倍频器就绪
    {
    }
    
    /* 复位时钟源选择,并将时钟源选择为PLL */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;    

    /* 等待时钟源选择完成置位 */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
    {
    }
  }
}

        2.2 代码解析

        RCC->CR |= (uint32_t)0x00000001

         此处代码的作用是启动芯片内置HSI时钟源,确保芯片能正常运行,以及触发CSS时钟安全警报后能正常切换。

        RCC->CFGR &= (uint32_t)0xF8FF0000

        将RCC->CFGR寄存器内的值复位为初始状态,即复位

SW:系统时钟切换 -> HSI作为系统时钟

HPRE: AHB 预分频 -> SYSCLK不分频
PPRE1:低速 APB1预分频 -> HCLK 不分频
PPRE2:高速 APB2预分频 -> HCLK不分频
ADCPRE:ADC 预分频 -> PCLK2 2分频后作为 ADC 时钟
MCO 微控制器时钟输出:没有时钟输出

        RCC->CR &= (uint32_t)0xFEF6FFFF

       将 HSEON, CSSON and PLLON 复位为初始状态,即

HSEON :外部高速时钟使能 -> HSE振荡器关闭
CSSON:时钟安全系统使能 -> 时钟监测器关闭
PLLON:PLL 使能 -> PLL关闭

        RCC->CR &= (uint32_t)0xFFFBFFFF;

        复位 HSEBYP 状态。为什么这里是复位不与上面的复位一次进行呢,是因为在官方文档中写明了,在复位HSEBYP前需要先只有在外部1- 25MHz振荡器关闭的情况下,该位才可以写入。在上文我们将HSE时钟关闭了。此处复位是为了告诉芯片外部1-25MHz振荡器没有旁路。所谓HSE旁路模式,是指无需陶瓷晶振作为系统时钟,直接从外界导入时钟信号。犹如芯片内部的驱动组件被旁路了。

        RCC->CFGR &= (uint32_t)0xFF80FFFF

        复位 PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE 状态

PLLSRC:PLL输入时钟源 -> HSI 时钟 2 分频后作为 PLL 输入时钟
PLLXTPRE: HSE 分频器作为 PLL输入 -> HSE 不分频
PLLMUL: PLL倍频系数 -> PLL 2 倍频输出
USBPRE: USB预分频 -> PLL 时钟 1.5 倍分频作为 USB 时钟

        此处的复位配置为什么不能与 RCC->CFGR &= (uint32_t)0xF8FF0000 一起写入寄存器呢,同样的此处复位的值在写入对应寄存器之前必须要先关闭PLL倍频器,在复位CR寄存器是我们才将PLL关闭。

        RCC->CIR = 0x009F0000

        此处语句的作用就单纯的是复位所有中断标志位。从寄存器名字也可以体现出"IR",全英文不就是interrupt。为什么要清除中断标志位,是因为在RCC系统时钟初始化时都会产生大量的中断,例如 HSE就绪就会将对应标志位置1,PLL就绪也同样,想要清楚就绪标志位,就只能通过该寄存器清除。

        RCC->CR |= ((uint32_t)RCC_CR_HSEON)

        使能HSE外部时钟源,这里的使能作用就跟配置GPIO一样,在配置每一个外设时应当先将其使能。

  do

  {

    HSEStatus = RCC->CR & RCC_CR_HSERDY;    //读CR寄存器中就绪位

    StartUpCounter++;                       //超时计数

  } while((HSEStatus == 0) && (StartUpCounter != 0x0500));     //等待超时,以及标志位置位

        此处代码的作用就是等待HSE就绪, StartUpCounter++这里能够完成自增的原因就是最前面 RCC->CR |= (uint32_t)0x00000001 使能了HSI时钟,保证了系统能正常运作,如果前面没有使能HSI则此处不能完成自增操作。如果迟迟都等不到HSE就绪,那么系统就会以HSI时钟作为SYSCLK运作。

        RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1

        此处就是配置AHB桥预分频器,此处不分频

        RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1

        此处就是配置APB2桥预分频器,此处不分频

        RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2

        此处就是配置APB1桥预分频器,此处2分频,在官方文档的时钟树中标明了该APB1总线最高只支持到36MHZ,而我们的AHB桥有72MHz,所以要达到官方限制需进行2分频。

        

        RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL))

        此处语句初始化PLL时钟源选择器,HSE预分频器,PLL倍频器

PLLSRCPLL输入时钟源 -> HSI时钟2分频后作为PLL输入时钟

PLLXTPREHSE分频器作为PLL输入 -> HSE不分频

PLLMULPLL倍频系数 -> PLL 2倍频输出

        RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9) 

        此处语句将时钟源选择为 HSE,进行9倍频 SYSCLK = HSE*9 = 8MHz*9 = 72MHz

        RCC->CR |= RCC_CR_PLLON

        使能PLL倍频器。

        RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW))

        RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL

        此处两个语句是为了先复位系统时钟源并将系统时钟源选择为PLL。

        剩下的两个 while() 都是为了检测对应配置是否配置完成,如果配置完成硬件会将对应为置1。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

河狸子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值