STM32学习之系统时钟

以正点原子战舰开发板为例

1. 查看时钟树结构

通过数据手册可以看到时钟树结构如下图:
在这里插入图片描述
根据右下角的图例可以发现这个芯片支持四类时钟,分别是高低速的内外部时钟,我们要配置的就是位于中间的SYSCLK,写着最大72MHz的那个东西,通过对他来进行各种分频后就可以为右边的各种片上内设和片上外设提供时钟了,或许会有疑问,不分频会怎么样?所有的外设都用系统时钟,理论上来说肯定是可以的,但是实际来说也没人这样干,会造成很大的资源浪费,因为有些外设根本用不到这么高频率的时钟,其实这也是右边的外设会挂载到APB1和APB2两条总线上的原因之一,如果全部外设都以这么高的频率跑,说不定芯片的温度就可以煎蛋了,所以我们要让花成花,让树成树,它左边的梯形符号表明了SYSCLK的时钟可以有三个来源,分别是:

  1. 高速内部时钟;
  2. 锁相环时钟;
  3. 高速外部时钟;

战舰开发板用的是高速外部时钟,所以我们后面配置的时候就不能选择HSI,通过硬件原理图可以看到告诉高速外部时钟信号是8Mhz,而且官方推荐使用的最高稳定时钟频率是72MHz,所以直接选择外部高速时钟作为信号输入也不行,所以我们需要选择锁相环时钟信号来作为我们的系统时钟源。
顺便提一下图中的低速时钟,RTC是实时时钟信号,就是用来以现实生活的时分秒的方式计时的,RTC的特殊之处是断电情况下仍可以独立运行,只要芯片的备用电源一直供电,RTC上的时间会一直走,所以就是个钟。可以通过128分频的外部高速时钟或者低速内部时钟和低速外部时钟作为时钟源,外部低速时钟一般都是32.768KHz,原因是2的15次方等于32768,所以用32768分频就可以产生精准的1Hz信号,这就是32.768KHz的来源。

2. 外部时钟的启动过程

从我们朴素的观念来思考这个问题,芯片上电以后开始执行代码,在刚开始执行代码的时候它肯定是不知道外部时钟是否存在的,没有人告诉他要使用外部时钟,所以在一开始的代码执行过程中使用的必然是内部时钟,所以选择外部时钟作为时钟源的启动过程分为下面三步:

  1. 上电后先使用内部时钟让芯片跑起来;
  2. 通过软件配置来选择外部时钟作为时钟源;
  3. 配置成功,选择外部时钟作为时钟源,否则用内部时钟作为时钟源。

3. 初始化过程

在配置前先简单浏览一遍和RCC有关的寄存器有哪些,以及各寄存器大致都是什么意思,这里就简单说一下,具体的可以去看数据手册:

  1. RCC_CR,时钟控制寄存器,表示内外部高速时钟的状态和打开关闭情况;
  2. RCC_CFGR,时钟配置寄存器,选择系统时钟源以及各个分频系数和倍频系数等,这个寄存器也涉及时钟输出和ADC时钟频率设置,跟本文无关就不讨论了;
  3. RCC_CIR,时钟中断寄存器,主要是操作系统进行任务调度使用,这里直接关闭就行了;
  4. RCC_APB2RSTR,APB2 外设复位寄存器,复位APB2总线上的外设;
  5. RCC_APB1RSTR,APB1 外设复位寄存器,复位APB1总线上的外设;
  6. RCC_AHBENR,AHB外设时钟使能寄存器,配置APB1总线上的外设时钟;
  7. RCC_APB2ENR,APB2 外设复位寄存器,配置APB2总线上的外设时钟;
  8. RCC_APB1ENR,APB1 外设复位寄存器,配置APB1总线上的外设时钟;
  9. RCC_BDCR,备份域控制寄存器,对低速时钟的一些配置;
  10. RCC_CSR,控制状态寄存器,记录系统的复位类型和低速时钟配置。

可以看出来和系统时钟配置有关的寄存器主要就是1, 2, 3,其他的虽然说也有用,但是没那么重要了

3.1 复位所有外设

RCC->APB1RSTR = 0x00000000;    // 复位总线APB1上的外设
RCC->APB2RSTR = 0x00000000;    // 复位总线APB2上的外设

复位的目的是为了让系统处于一个确定的状态,不至于被之前的配置所影响。

3.2 关闭外设时钟

    RCC->AHBENR = 0x00000014;        // 睡眠模式时闪存和SRAM时钟使能.其他关闭
    RCC->APB2ENR = 0x00000000;       // 关闭APB1和APB2的外设时钟 
    RCC->APB1ENR = 0x00000000;

在睡眠模式下,CPU仍然有可能需要访问闪存中的程序指令,所以闪存和SRAM的时钟需要使能,比如有可能会遇到需要外部中断唤醒的情况,其他的就和复位作用差不多,防止干扰配置。

3.3 配置默认时钟

这一步就是第2部分提到的,在配置外部时钟之前需要先让芯片跑起来,配置的核心逻辑就是使能高速内部时钟,并将其设置为系统时钟,使得系统有一个基本的时钟来进行后续的配置,同时对与外部时钟有关的内容进行初始化。

    RCC->CR |= 0x00000001;                 // 使能内部高速时钟HSION
    RCC->CFGR &= 0xF8FF0000;               // 复位SW[1:0], SWS[1:0], HPRE[3:0], PPRE1[2:0], PPRE2[2:0], ADCPRE[1:0], MCO[2:0]
    RCC->CR &= 0xFEF6FFFF;                 // 复位HSEON, CSSON, PLLON
    RCC->CR &= 0xFFFBFFFF;                 // 复位HSEBYP
    RCC->CFGR &= 0xFF80FFFF;               // 复位PLLSRC, PLLXTPRE, PLLMUL[3:0] 和 USBPRE/OTGFSPRE
    RCC->CIR = 0x009F0000;                 // 关闭所有RCC中断并清除中断标志

第2行0xF8FF0000的意思是这行代码只复位SW[1:0], SWS[1:0], HPRE[3:0], PPRE1[2:0], PPRE2[2:0], ADCPRE[1:0], MCO[2:0],所以这些内容对应的位都置0,其余位保持1不变,该是什么样还是什么样;
第3行和第4行看起来可以放一起写,但是实际上不行,数据手册说了,只有在只有在外部振荡器关闭的情况下,才能写入HSEBYP位,所以必须分两行来写;
第5行的目的是初始化这些配置,从而等我们正儿八经配置外部时钟作为系统时钟源的时候不会被这些配置干扰;
最后第6行关中断也是为了防止时钟中断对时钟配置产生影响。

4. 配置外部源为系统时钟

配置外部时钟位时钟源的代码如下,参考正点原子的例程;

void sys_clock_set(uint32_t plln)
{
    RCC->CR |= 1 << 16;    			 			// 外部高速时钟使能HSEON
    // 在正点原子的例程中这里会有一个延时,用来修改MDK的bug,由于我不是用的MDK开发,所以删除延时的代码
    while ((RCC->CR & (1 << 17)) == 0);    		//等待外部振荡器稳定
    RCC->CFGR = 0B100 << 10;              		// APB1总线预分频为2,APB2不分频
    plln -= 2;                           		// 抵消2个单位(因为是从2开始的, 设置0就是2)
    RCC->CFGR |= plln << 18;    		 		// 设置PLL值 2~16
    RCC->CFGR |= 1 << 16;                		// 选择外部高速时钟HSE作为锁相环PLL输入时钟

    // FLASH_ACR寄存器的描述详见: <<STM32F10xx闪存编程手册>>
    FLASH->ACR = 1 << 4;        		 		// PRFTBE = 1 开启预取缓冲区
    FLASH->ACR |= 2 << 0;       		 		// LATENCY[2:0] = 2 FLASH两个等待周期

    RCC->CR |= 1 << 24;         		 		// PLLON = 1, 使能PLL
    while (!(RCC->CR >> 25));   		 		// 等待PLL锁定
    RCC->CFGR |= 2 << 0;                 		// SW[1:0] = 2, 选择PLL输出作为系统时钟
    while (((RCC->CFGR >> 2) & 0X03) != 2); 	// 等待PLL作为系统时钟设置成功
    return;
}

第3行,打开外部高速时钟;
第5行,等待外部时钟达到稳定状态;
第6行,设置两条外设总线的分频系数,需要注意MDK的keil有可能不支持这种写法;
第7和8行,在第8行设置倍频系数的时候倍频系数为2到16的时候寄存器要配置的数字是0到14,所以在第7行减去2;
第9行,设置锁相环的输入时钟是外部时钟;
第15和16行,使能并等待锁相环稳定;
第17和18行,选择锁相环作为系统时钟并等待时钟设置成功。
到这里系统时钟就配置完成了,最后,在使用某一具体外设的时候,需要配置对应外设的寄存器,查看外设位于哪根总线上,然后修改对应寄存器AHBENRAPB2ENRAPB1ENR的对应位即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值