乐趣区

关于stm:STM32时钟体系

STM32 的时钟总体设计

  • 时钟源:纯外部(不精准)、内外部(时钟产生的振荡电路在外部,然而晶振在内部)、纯内部(间接从内部接入一个时钟)。
  • 单片机外部有齐全独立的多个时钟。
  • PLL 时钟:锁相环电路。性能就是进行倍频。
  • 外部设有两套独立时钟:HSx(高速时钟源)和 LSx(低速时钟源)。
  • 纯外部:HSI、LSI。
  • 内外部;HSE、LSE。
  • 纯内部:OSC_IN、OSC32_IN(应用较少)
  1. 红方框中 SYSCLK 是零碎时钟,是共给 CPU 的时钟(SYSCLK 是多少 CPU 主频就是多少);之后的局部是配置各个模块的时钟。
  2. 上电复位后,默认用的是外部时钟(红线门路)(外部时钟不精确)

STM32 时钟寄存器

  • STM32 所有的时钟上电默认是敞开的(即寄存器默认都是 0)。
  • 下列寄存器图中上面的 r、w 是可读可写的意思(r:可读,由硬件置位。w:可写)(rw:既可读又可写)

时钟管制寄存器(RCC_CR)

  • PLLRDY:PLL 时钟就绪标记,PLL 锁定后由硬件置一。(PLL 倍频不是一瞬间实现的,须要肯定的工夫)(0:PLL 未锁定。1:PLL 锁定)
  • PLLON:PLL 使能,由软件置一和清零。当 PLL 时钟被用作或被抉择将要作为零碎时钟时,该位不能被清零。(0:PLL 敞开。1:PLL 使能)
  • HSERDY:内部高速时钟就绪标记。由硬件置一来批示内部 4 -16MHz 振荡器曾经稳固。在 HSEON 位清零后,该位须要 6 个内部 4 -25MHz 振荡器周期清零。(0:未就绪。1:就绪)
  • HSEON:内部高速时钟使能。由软件置一和清零。(0:HSE 振荡器敞开。1:HSE 振荡器关上)
  • HSIRDY:外部高速时钟就绪标记。由硬件置一来批示外部 8MHz 振荡器曾经稳固。在 HSION 位清零后,该位须要 6 个外部 8MHz 振荡器周期清零。(0:外部 8MHz 振荡器未就绪,1:外部 8MHz 振荡器就绪)
  • HSION:外部高速时钟使能。由软件置一和清零。(0:外部 8MHz 振荡器敞开。1:外部 8MHz 振荡器关上)

时钟配置寄存器 (RCC_CFGR)

留神与上图的时钟树绝对应。

  • USBPRE:USB 预分频。由软件置一或清零来产生 48MHz 的 USB 时钟。在 RCC_APB1ENR 寄存器中使能 USB 时钟之前,必须保障该位曾经无效。如果 USB 时钟被使能,该位不能被清零。(0:PLL 时钟 1.5 倍分频作为 USB 时钟。1:PLL 时钟间接作为 USB 时钟)
  • PLLMUL:PLL 倍频系数。由软件设置来确定 PLL 倍频系数。只有在 PLL 敞开的状况下才可被写入(留神:PLL 的输入频率不能超过 72MHz)
  • PLLXTPRE:HSE 分频器作为 PLL 输出。(软件置一和清零抉择 HSE 是否分频)
  • PLLSRC:抉择 PLL 输出时钟源。(0:抉择 HSI 经 2 分频后作为 PLL 输出时钟。1:抉择 HSE 时钟作为 PLL 输出时钟)
  • ADCPRE:ADC 预分频。由软件设置。
  • PPRE2:高速 APB 预分频设置 (APB2)。由软件设置预分频系数。
  • PPRE1:低速 APB 预分频设置 (APB1)。由软件设置预分频系数。
  • HPRE:AHB 预分频。由软件设置预分频系数。
  • SW:零碎时钟切换(即时钟树中的 SW 地位)。(00:HSI)(01:HSE)(10:PLL)(11:不可用)

时钟中断寄存器 (RCC_CIR)

  • PLLRDYC:革除 PLL 就绪中断(1:革除 PLL 就绪中断标记位 PLLRDYF)
  • HSERDYC:革除 HSE 就绪中断(1:革除 HSE 就绪中断标记位 HSERDYF)
  • HSIRDYC:革除 HSI 就绪中断(1:革除 HSI 就绪中断标记位 HSIRDYF)
  • LSERDYC:革除 LSE 就绪中断(1:革除 LSE 就绪中断标记位 LSERDYF)
  • LSIRDYC:革除 LSI 就绪中断(1:革除 LSI 就绪中断标记位 LSIRDYF)
  • PLLRDYIE:PLL 就绪中断使能(1:HSE 就绪中断使能)
  • HSIRDYIE:HSI 就绪中断使能(1:HSI 就绪中断使能)
  • LSERDYIE:LSE 就绪中断使能(1:LSE 就绪中断使能)
  • LSIRDYIE:LSI 就绪中断使能(1:LSI 就绪中断使能)
  • PLLRDYF:PLL 就绪中断标记(在 PLL 就绪且 PLLRDYIE 位被置一时,由硬件置一)
  • HSERDYF:HSE 就绪中断标记(在内部低速时钟就绪且 HSERDYIE 位被置一时,由硬件置一)
  • HSIRDYF:HSI 就绪中断标记
  • LSERDYF:LSE 就绪中断标记
  • LSIRDYF:LSI 就绪中断标记

代码示例

// 寄存器宏定义
// RCC 寄存器基地址为 0x40021000
#define RCC_BASE    0x40021000            // RCC 局部寄存器的基地址
#define RCC_CR        (RCC_BASE + 0x00)    // RCC_CR 的地址
#define RCC_CFGR    (RCC_BASE + 0x04)

#define FLASH_ACR    0x40022000

// 用 C 语言来拜访寄存器的宏定义
#define rRCC_CR        (*((volatile unsigned int *)RCC_CR))
#define rRCC_CFGR    (*((volatile unsigned int *)RCC_CFGR))
#define rFLASH_ACR    (*((volatile unsigned int *)FLASH_ACR))

// 函数作用:时钟源切换到 HSE 并且使能 PLL,将主频设置为 72MHz
void Set_SysClockTo72M(void)
{
    unsigned int rccCrHserdy = 0;
    unsigned int rccCrPllrdy = 0;
    unsigned int rccCfrSwsPll = 0;
    unsigned int faultTime = 0;


    rRCC_CR = 0x00000083;        // 复位时钟,设置为初始状态(上电复位用的 HSI 时钟)// 开启内部时钟
    rRCC_CR &= ~(1<<16);      // 敞开 HSEON
    rRCC_CR |= (1<<16);            // 关上 HSEON,让 HSE 工作

    do
    {rccCrHserdy = rRCC_CR & (1<<17);    // 检测第 17 位是否为 1(检测内部高速时钟是否准备就绪)faultTime++;// 超时计数
    }
    while ((faultTime<0x0FFFFFFF) && (rccCrHserdy==0));

    // 内部时钟开启胜利
    if ((rRCC_CR & (1<<17)) != 0)
    {
        rFLASH_ACR |= 0x10;
        rFLASH_ACR &= (~0x03);
        rFLASH_ACR |= (0x02);

        // 到这里 HSE 就准备就绪了,上面再去配 PLL 并且期待他准备就绪
        
        // 复位配置寄存器 RCC_CFGR(APB、AHB 不分频,时钟切换不可用)rRCC_CFGR &= (~((0x0f<<4) | (0x07<<8) | (0x07<<11)));
        // AHB 和 APB2 未分频,APB1 被 2 分频,所以最终:AHB 和 APB2 都是 72M,APB1 是 36M
        rRCC_CFGR |= ((0x00<<4) | (0x04<<8) | (0x00<<11));

        
        rRCC_CFGR &= (~((1<<16) | (1<<17)));       // 清零 bit17 和 bit16
        rRCC_CFGR |= ((1<<16) | (0<<17));        // 抉择 HSE 作为 PLL 输出,HSE 不分频

        // 设置 PLL 倍频系数为 9
        rRCC_CFGR &= (~(0x0f<<18));               // 清零 bit18-21
        rRCC_CFGR |= (0x07<<18);                // 9 倍频

        
        
        // 关上 PLL 开关
        rRCC_CR |= (1<<24);

        // do while 循环期待 PLL 时钟稳固
        faultTime = 0;
        do
        {rccCrPllrdy = rRCC_CR & (1<<25);    // 检测第 25 位是否为 1
            faultTime++;// 检测时间
        }
        while ((faultTime<0x0FFFFFFF) && (rccCrPllrdy==0));
        

        if ((rRCC_CR & (1<<25)) == (1<<25))
        {
          // 到这里阐明 PLL 时钟曾经稳固了,能够用了,上面就能够切了
            
            // 切换 PLL 输入为 SYSCLK
            rRCC_CFGR &= (~(0x03<<0));   
            rRCC_CFGR |= (0x02<<0);    

            faultTime = 0;
            do
            {rccCfrSwsPll = rRCC_CFGR & (0x03<<2);    // 检测第 25 位是否为 1
                faultTime++;// 检测时间
            }
            while ((faultTime<0x0FFFFFFF) && (rccCfrSwsPll!=(0x02<<2)));
            
               if ((rRCC_CFGR & (0x03<<2))== (0x02<<2))
            {// 到这里咱们的时钟整个就设置好了,能够完结了}
            else
            {
                // 到这里就阐明 PLL 输入作为 SYSCLK 不胜利
                while (1);
            }

        }
        else
        {
            // 到这里就阐明 PLL 启动时出错了,PLL 不能稳固工作
            while (1);
        }

    }
    else
    {
        // HSE 配置超时,阐明 HSE 不可用,个别硬件就有问题要去查
        while (1);
    }
}



退出移动版