1. 时钟就是单片机的心脏。单片机根据时钟频率来控制每个部件的工作,时钟是单片机的脉搏,决定了每条命令运行的速率,没有时钟单片机将停止工作。
如何理解“时钟决定了单片机每条命令运行的速率”?
首先需要去理解单片机中的时钟周期、机器周期和指令周期。
1.1 时钟周期:单片机的基本时间单位,是CPU执行基本操作的最小时间单位。每个时钟周期内,单片 机完成一个基本动作,时钟周期决定了单片机的运行速度。
时钟周期由晶振频率所决定,比如8MHz的MCU,其对应的时钟周期为(1/(8MHz))= 125ns。
1.2 机器周期(CPU周期):在单片机中,将一条指令的执行过程分为不同的阶段,每个阶段只完成一项基本操作,比如取指令、存储器读、存储器写等。
每一个基本操作都有若干CPU最基本动作所组成。完成一个基本操作所需要的时间即为机器周期,通常将从内存中读取一个指令字的最短时间定义为CPU周期。
1.3 指令周期:单片机执行一条指令所消耗的时间,即从取指(Fetch)、译码(Decode)、执行(Execute)所需要的全部时间。一个指令周期包含若干机器周期。
(图片参考于)
2. STM32的时钟系统:
提到STM32时钟系统,不得不拿出祖传的时间系统框图:
如上图,STM32的系统时钟可以来自3个方向HSI振荡器时钟、HSE振荡器时钟、PLL时钟。
本文主要介绍STM32的系统时钟,关于看门狗时钟、USB时钟、RTC时钟暂不介绍,感兴趣的可以对照此框图自行配置(当理解系统时钟后,其他时钟的配置也大同小异)。
STM32有3个时钟总线:AHB(Advanced High-performance Bus Clock)、APB1(Advanced Peripheral Bus 1 Clock)、APB2(Advanced Peripheral Bus 2 Clock)。单片机所有的片上外设都挂载在这3个时钟总线上。
AHB时钟总线:连接处理器核心(如Cortex-M3)、高速外设(核心存储器、DMA、SDIO、FSMC)等,通常由系统时钟经过AHB分频器得到。
APB1时钟总线:连接低俗外设(如TIM2~7,WWDG、SPI、USART2~5、I2C1~2、CAN1、DAC等),由AHB时钟经过APB1分频器得到。
APB2时钟总线:连接高速外设(如TIM1、TIM8、AFIO、GPIOA~G、ADC1~3、USART1、SPI1)等,通常由AHB时钟经过APB2分频器得到。
STM32系统时钟的配置过程即为:选择系统时钟源(HSI、HSE、PLL)、设置系统时钟(分频器)、设置AHB时钟、设置APB1时钟、设置APB2时钟。看门狗时钟、USB时钟、RTC时钟的配置过程类似系统时钟配置过程,具体的可根据时钟框图进行配置。
PLL锁相环可以理解为倍频器,比如8MHz,2倍频后便是16MHz。
分频器是降低频率,比如72MHz,2分频后便是36MHz。
3. STM32系统时钟具体配置过程:
通常是在启动文件中调用 SystemInit()配置系统时钟,一般这个函数已经写好了的,用户只需要配置不同的宏定义便可以改变系统时钟频率。
用户也可以自己去操作这些寄存器,更详细的配置所需时钟频率。如果是自行操作寄存器配置时钟,则必须在main初始化其他外设之前就配置好自己的时钟。
假设现在使用STM32外部晶振频率为8MHz,需要配置系统系统来源为PLL,频率为40MHz,则重新配置时钟的过程为:
① 开启内部时钟(因为现在需要配置的时钟为外部时钟,所有在此过程中先切换为内部时钟,避免单片机没有时钟而无法工作)
② 复位之前的时钟配置(避免之前的配置影响后面配置)
③ 禁止、清除中断(时钟配置过程中不允许有中断)
④ 开启外部时钟
⑤ 配置系统时钟、AHB时钟、APB1和APB2时钟
⑥ 配置PLL锁相环
⑦ 设置PLL为系统时钟源
4.主要代码如下:
#include "stm32f10x.h"
#include "delay.h"
#include "led.h"
#include "pwm.h"
/*ÅäÖÃϵͳʱÖÓ£º*/
void SystemClock_Init()
{
// RCC_DeInit();
RCC->CR |= (1 << 0); //¿ªÆôÄÚ²¿Ê±ÖÓ
/*Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits*/
RCC->CFGR &= (uint32_t)0xF8FF0000;
RCC->CR &= (uint32_t)0xFEF6FFFF; //¹Ø±ÕÍⲿ¸ßËÙʱÖÓ¡¢¹Ø±ÕʱÖÓ¼ì²â¡¢¹Ø±ÕPLLËøÏà»·
RCC->CR &= ~(1 << 18); //ÉèÖÃÍⲿ¾§Õñ²»ÅÔ·£¬¼´ÍⲿʱÖÓÐèÒª¾¹ýÕñµ´Æ÷
/* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
RCC->CFGR &= (uint32_t)0xFF80FFFF;
RCC->CIR &= 0x009F0000; //½ûÖ¹ºÍÇå³ýÖжÏ
RCC->CR |= (1 << 16); //¿ªÆôÍⲿʱÖÓ
while ((RCC->CR & (1 << 17)) == 0); //µÈ´ýʱÖÓ¾ÍÐ÷
RCC->APB1ENR |= (1 << 28); //µçÔ´½Ó¿ÚʱÖÓʹÄÜ£¬Ï൱ÓÚ¿ªÆôAPB1ʱÖÓ
RCC->CFGR |= (0 << 17);
RCC->CFGR |= ((0 << 4) | (5 << 8) | (4 << 11)); //ÅäÖÃϵͳʱÖÓ¡¢APB1¡¢APB2ʱÖÓ
RCC->CFGR |= (1 << 16); //ÅäÖÃÍⲿ¸ßËÙʱÖÓHSEΪPLLʱÖÓÔ´
// RCC->CFGR |= 0x07 << 18; //PLLµÄ±¶ÆµÊýΪ9
RCC->CFGR |= 0x03 << 18; //PLLµÄ±¶ÆµÊýΪ2
RCC->CR |= (1 << 24); //ʹÄÜPLL
while ((RCC->CR & (1 << 25)) == 0); //µÈ´ýPLL¾ÍÐ÷
RCC->CFGR &= ~(3 << 0); //ÇåÁã
RCC->CFGR |= (2 << 0); //ÉèÖÃPLLΪϵͳʱÖÓ
while((RCC->CFGR & (3 << 2)) != (2 << 2)); //µÈ´ýPLL×÷ΪϵͳʱÖÓ¾ÍÐ÷
}
void SystemClock_Init2()
{
RCC_DeInit();
RCC_HSEConfig(RCC_HSE_ON);
if(RCC_WaitForHSEStartUp() == SUCCESS)
{
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_3);
RCC_PLLCmd(ENABLE);
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while(RCC_GetSYSCLKSource() != 0x08);
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK2Config(RCC_HCLK_Div2);
RCC_PCLK1Config(RCC_HCLK_Div4);
}
}
RCC_ClocksTypeDef RCC_Clock_Struct;
int main(void)
{
int i;
SystemClock_Init();
// SystemClock_Init2();
LED_Init();
GPIO_ResetBits(GPIOC, GPIO_Pin_0);
RCC_GetClocksFreq(&RCC_Clock_Struct);
while (1)
{
GPIO_SetBits(GPIOC, GPIO_Pin_0);
for (i = 0; i < 9000000; i++);
GPIO_ResetBits(GPIOC, GPIO_Pin_0);
for (i = 0; i < 9000000; i++);
}
}
图片中有注释,复制的代码注释每次都乱码:
为什么这样去配置寄存器,其实是根据寄存器每一位的功能来配置,这个需要去查看STM32F103中文参考手册、数据手册中寄存器说明(寄存器配置其实就是给相应的位置写上不同的功能参数,最终都是通过操作寄存器而去操作外设)。
5.运行结果:
同样的代码,在不同的频率下执行的速率不同。比如在本文的LED点亮循环中,所有配置不变,将系统时钟频率从16MHz,修改至72MHz的过程中,LED的闪灭速度逐步增加。即频率越高,指令执行速度越快。
6.总结:
单片机的时钟是系统心脏,控制着每条指令执行的速度,没有时钟单片机将无法有序工作,产生意想不到的错误。所以在配置系统时钟的过程中,一定要根据单片机所允许的频率和数据手册去进行合理配置。