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);
    }
}



03-05 23:23