嵌入式单片机基础篇(五)

stm32F1以及51单片机时钟详解

第一部分:stm32

1、问题:什么是时钟?时钟的意义
回答:简而言之,时钟就是数据传输的基准,CPU的脉搏
时钟的意义:
一块主板要能进行正确的数据传送以及正常的运行,没有时钟信号是不行的,时钟信号在电路中起着发非常重要的作用。因为在数据传送过程中,只有对时序进行严格的定义和要求,才能保证数据在传输过程不出差错。而在此期间时钟信号充当了这一时序的基准。我们可以用它来确定其它信号的宽度,同时也利用它来保证收发数据双方的同步。
以CPU为例,时钟信号作为其基准,CPU内部的所有信号处理都要以它作为标尺,这样它就确定了CPU指令的执行速度。
STM32 的时钟系统比较复杂,不像简单的 51 单片机一个系统时钟就可以解决一切。 因为首先STM32 本身非常复杂,外设非常的多,但是并不是所有外设都需要系统时钟这么高的频率,比如看门狗以及 RTC 只需要几十 k 的时钟即可。同一个电路,时钟越快功耗越大,同时抗电磁干扰能力也会越弱,所以对于较为复杂的 MCU 一般都是采取多时钟源的方法来解决这些问题。
时钟树解析:(以下内容大部分来源于网络或者stm32F1参考手册)

如上图所示:

  1. STM32 有5个时钟源:HSI、HSE、LSI、LSE、PLL。
    ①、HSI是高速内部时钟,RC振荡器,频率为8MHz,精度不高。  
    ②、HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz。  
    ③、LSI是低速内部时钟,RC 振荡器,频率为 40kHz。独立看门狗的时钟源只能是 LSI,同时 LSI 还可以作为 RTC 的时钟源。
    ④、LSE是低速外部时钟,接频率为32.768kHz的石英晶体。RTC  
    ⑤、PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。
    下面介绍这 5 个时钟源是如何给各个外设以及系统提供时钟的
    1、 MCO 是 STM32 的一个时钟输出 IO(PA8),它可以选择一个时钟信号输出,可以选择为 PLL 输出的 2 分频、HSI、HSE、或者系统时钟。这个时钟可以用来给外部其他系统提供时钟源。
    2、 RTCCLK 从图上可以看出,RTC 的时钟源可以选择 LSI,LSE,以及HSE 的 128 分频。
    3、 USBCLK 的时钟是来自 PLL 时钟源。 STM32 中有一个全速功能的 USB 模块,其串行接口引擎需要一个频率为 48MHz 的时钟源。该时钟源只能从 PLL 输出端获取,可以选择为 1.5 分频或者 1 分频,也就是,当需要使用 USB模块时,PLL 必须使能,并且时钟频率配置为 48MHz 或 72MHz。
    4、SYSCLK 它是供 STM32 中绝大部分部件工作的时钟源。 系统时钟可选择为 PLL 输出、 HSI 或者 HSE。系统时钟最大频率为 72MHz
    5、从时钟图上可以看出,其他所有外设的时钟最终来源都是 SYSCLK。SYSCLK 通过 AHB 分频器分频后送给各模块使用。这些模块包括:
    ①、AHB 总线、内核、内存和 DMA 使用的 HCLK 时钟。
    ②、通过 8 分频后送给 Cortex 的系统定时器时钟,也就是 systick 了。
    ③、直接送给 Cortex 的空闲运行时钟 FCLK。
    ④、送给 APB1 分频器。APB1 分频器输出一路供 APB1 外设使用(PCLK1,最大频率 36MHz),另一路送给定时器(Timer)2、3、4 倍频器使用。
    ⑤、送给 APB2 分频器。APB2 分频器分频输出一路供 APB2 外设使用(PCLK2,最大频率 72MHz),另一路送给定时器(Timer)1 倍频器使用。
    其中需要理解的是 APB1 和 APB2 的区别,APB1 上面连接的是低速外设,包括电源接口、备份接口、CAN、USB、I2C1、I2C2、UART2、UART3 等等,APB2 上面连接的是高速外设包括 UART1、SPI1、Timer1、ADC1、ADC2、所有普通 IO 口(PA~PE)、第二功能 IO 口等。
    SystemInit时钟系统初始化函数剖析
    STM32 时钟系统的配置除了初始化的时候在 system_stm32f10x.c 中的 SystemInit()函数中外,其他的配置主要在 stm32f10x_rcc.c 文件中,里面有很多时钟设置函数,可以打开这个文件浏览一下,基本上看看函数的名称就知道这个函数的作用。在设置时钟的时候,一定要仔细参考 STM32 的时钟图,做到心中有数。这里需要指明一下,对于系统时钟,默认情况下是在 SystemInit 函数的 SetSysClock()函数中间判断的,而设置是通过宏定义设置的。我们可以看看 SetSysClock()函数体:
    tatic void SetSysClock(void)
    {
      #ifdef SYSCLK_FREQ_HSE
       SetSysClockToHSE();
      #elif defined SYSCLK_FREQ_24MHz
       SetSysClockTo24();
      #elif defined SYSCLK_FREQ_36MHz
       SetSysClockTo36();
      #elif defined SYSCLK_FREQ_48MHz
       SetSysClockTo48();
      #elif defined SYSCLK_FREQ_56MHz
       SetSysClockTo56();
      #elif defined SYSCLK_FREQ_72MHz
       SetSysClockTo72();
      #endif
    }
    这段代码非常简单,就是判断系统宏定义的时钟是多少,然后设置相应值。我们系统默认宏定义是 72MHz:
    #define SYSCLK_FREQ_72MHz 72000000
    如果你要设置为 36MHz,只需要注释掉上面代码,然后加入下面代码即可:
    #define SYSCLK_FREQ_36MHz 36000000
    同时还要注意的是,当我们设置好系统时钟后,可以通过变量 SystemCoreClock 获取系统时钟值,如果系统是 72M 时钟,那么 SystemCoreClock=72000000。这是在 system_stm32f10x.c 文件中设置的:
       #ifdef SYSCLK_FREQ_HSE
         uint32_t SystemCoreClock = SYSCLK_FREQ_HSE;
       #elif defined SYSCLK_FREQ_36MHz
         uint32_t SystemCoreClock = SYSCLK_FREQ_36MHz;
       #elif defined SYSCLK_FREQ_48MHz
         uint32_t SystemCoreClock = SYSCLK_FREQ_48MHz;
       #elif defined SYSCLK_FREQ_56MHz
         uint32_t SystemCoreClock = SYSCLK_FREQ_56MHz;
       #elif defined SYSCLK_FREQ_72MHz
         uint32_t SystemCoreClock = SYSCLK_FREQ_72MHz;
       #else
         uint32_t SystemCoreClock = HSI_VALUE;
       #endif
    系统时钟系统初始化重要函数:SystemInit(); 使用V3.5版本的库函数,该函数在系统启动之后会自动调用
    startup_stm32f10x_xx.s文件中:
    ; Reset handler
    Reset_Handler PROC
    EXPORT Reset_Handler [WEAK]
    IMPORT __main
    IMPORT SystemInit
    LDR R0, =SystemInit
    BLX R0
    LDR R0, =__main
    BX R0
    ENDP
    初始化之前首先通过宏定义定义系统时钟频率:
    #define SYSCLK_FREQ_72MHz 72000000
    (系统自动调用SystemInit()函数)
    初始化之后的状态:
    SYSCLK(系统时钟)=72MHz
    AHB 总线时钟(使用 SYSCLK)=72MHz
    APB1 总线时钟(PCLK1)=36MHz
    APB2 总线时钟(PCLK2)=72MHz
    PLL 时钟=72MHz
  2. 系统时钟SYSCLK可来源于三个时钟源:
    ①、HSI振荡器时钟
    ②、HSE振荡器时钟
    ③、PLL时钟
  3. STM32可以选择一个时钟信号输出到MCO脚(PA8)上,可以选择为PLL
    输出的2分频、HSI、HSE、或者系统时钟。
  4. 任何一个外设在使用之前,必须首先使能其相应的时钟。
    几个重要的时钟:
    SYSCLK(系统时钟) :
    AHB总线时钟
    APB1总线时钟(低速): 速度最高36MHz
    APB2总线时钟(高速): 速度最高72MHz
    PLL时钟
    RCC相关配置寄存器
    typedef struct
    {
    __IO uint32_t CR; //HSI,HSE,CSS,PLL等的使能和就绪标志位
    __IO uint32_t CFGR; //PLL等的时钟源选择,分频系数设定
    __IO uint32_t CIR; // 清除/使能 时钟就绪中断
    __IO uint32_t APB2RSTR; //APB2线上外设复位寄存器
    __IO uint32_t APB1RSTR; //APB1线上外设复位寄存器
    __IO uint32_t AHBENR; //DMA,SDIO等时钟使能
    __IO uint32_t APB2ENR; //APB2线上外设时钟使能
    __IO uint32_t APB1ENR; //APB1线上外设时钟使能
    __IO uint32_t BDCR; //备份域控制寄存器
    __IO uint32_t CSR; //控制状态寄存器
    } RCC_TypeDef;
    固件库时钟配置函数:
    1、时钟使能配置:
    RCC_LSEConfig() 、RCC_HSEConfig()、
    RCC_HSICmd() 、 RCC_LSICmd() 、 RCC_PLLCmd() ……

2、时钟源相关配置:
RCC_PLLConfig ()、 RCC_SYSCLKConfig() 、
RCC_RTCCLKConfig() …

3、分频系数选择配置:
RCC_HCLKConfig() 、 RCC_PCLK1Config() 、 RCC_PCLK2Config()…

4、外设时钟使能:
RCC_APB1PeriphClockCmd(): //APB1线上外设时钟使能
RCC_APB2PeriphClockCmd(); //APB2线上外设时钟使能
RCC_AHBPeriphClockCmd(); //AHB线上外设时钟使能

5、 其他外设时钟配置:
RCC_ADCCLKConfig (); RCC_RTCCLKConfig();
6、状态参数获取参数:
RCC_GetClocksFreq();
RCC_GetSYSCLKSource();
RCC_GetFlagStatus()
7、RCC中断相关函数 :
RCC_ITConfig() 、 RCC_GetITStatus() 、 RCC_ClearITPendingBit()…

知识点补充:RCC寄存器(内容来源于网络):

RCC(Reset Clock Controller) —— 复位与时钟控制
一、复位
STM32F10xxx支持三种复位形式,分别为系统复位、上电复位和备份区域复位。
系统复位: 除了时钟控制器的RCC_CSR寄存器中的复位标志位和备份区域中的寄存器以外,系统复位将复位所有寄存器至它们的复位状态。
电源复位: 将复位除了备份区域外的所有寄存器。
备份区域复位: 备份区域拥有两个专门的复位,它们只影响备份区域。

时钟启动过程
1、开机或复位时使用内部时钟
2、用软件进行切换,尝试开启外部时钟
3、如果开启成功,则使用外部时钟,否则使用内部

配置时钟的步骤
1、APB1、APB2的外设接口复位结束(即RESET),关闭APB1、APB2的外设时钟

打开内部8MHz振荡器,复位RCC->CFGR中的SW[1:0]、HPRE[3:0]、PRE1[2:0]、PRE2[2:0]、ADCPRE[2:0]、MCO[2:0]

复位RCC->CR中的HSEON、CSSON、PLLON、HSEBYP

复位RCC->CFGR中的PLLSRC、PLLXTPRE、PLLMUL[3:0]、USBPRE

关闭RCC->CIR中的所有中断

2、使能外部高速时钟晶振HSE

3、等待外部高速时钟晶振工作稳定

4、设置AHB时钟的预分频(在这之前要先执行FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); FLASH_SetLatency(FLASH))

5、设置APB1时钟的预分频

6、设置APB2时钟的预分频

7、设置PLL的时钟源以及PLL的倍频数,然后使能PLL

8、等待PLL工作稳定

9、选择SYSCLK的时钟源

10、判断PLL是否是系统时钟(若选择SYSCLK的时钟源是PLL的话)

11、打开要使用的外设时钟
程序:

void RCC_Init(void)
{RCC->APB1RSTR = 0x00000000;   //APB1、APB2复位结束RCC->APB2RSTR = 0x00000000;RCC->AHBENR   = 0x00000014;   //睡眠模式时闪存和 SRAM 时钟使能,其他关闭(其实可以注释掉,因为AHBENR复位的值就是该值)RCC->APB1ENR  = 0x00000000;   //关闭APB1、APB2的外设时钟RCC->APB2ENR  = 0x00000000; RCC->CR      |= 0x00000001;   //使能内部时钟HSIRCC->CFGR    &= 0xF8FF0000;   //复位RCC->CFGR中的SW[1:0],HPRE[3:0],PRE1[2:0],PRE2[2:0],ADCPRE[2:0],MCO[2:0]RCC->CR      &= 0xFEF2FFFF;   //复位HSEON、CSSON、PLLON、HSEBYPRCC->CFGR    &= 0xFF80FFFF;   //复位RCC->CFGR中的PLLSRC,PLLXTPRE,PLLMUL[3:0],USBPRERCC->CIR     &= 0x00000000;   //关闭所有中断[12:0]RCC->CR      |= (1<<16);      //使能HSEwhile(!(RCC->CR & (1<<17)));  //等待HSE稳定FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);          // 预取指缓存使能FLASH_SetLatency(FLASH_Latency_2);                             //FLASH_Latency_2  2延时周期RCC->CFGR    |= 0x00000400;      //AHB不分频,APB1二分频,APB2不分频RCC->CFGR    |= 0x001D0000;      //配置PLL:HSE为输入时钟,HSE不分频,9倍频输出RCC->CR      |= (1<<24);         //使能PLLwhile(!(RCC->CR & (1<<25)));     //等待PLL锁定RCC->CFGR    |= 0x00000002;      //选择PLL输出作为SYSCLKwhile(!(RCC->CFGR & (2<<2)));    //等待 PLL 作为系统时钟设置成功//下面就是打开所要用的外设时钟(RCC_AHBENR、RCC_APB1ENR、RCC_APB2ENR)
}

仿真结果:

### 第二部分:51单片机
51单片机时钟比较简单,只有一个系统时钟
1、问题:51系列单片机的系统时钟如何产生的呢?
单片机的控制器的定时功能是由时钟和定时电路完成的,它是产生CPU的操作时序。
内部方式时钟电路

外接时钟电路

 XTAL1是芯片内部振荡电路输入端,XTAL2为芯片内部振荡电路输出端    具体的产生有以下两种方式:一:内部方式则是利用芯片内反相器和电阻组成的振荡电路,,在XTAL1和XTAL2引脚上接定时元件,如压电晶体和电容组成的并联谐振电路,则内部可产生与外加晶体同频率的振荡时钟。一般晶体可以在1.2MHZ到12MHZ之间任意选择,电容一般选择在5pf到30pf,对时钟频率有微调作用。
二:外部时钟方式
如果采用外部时钟方式,此时要把XTAL1接到外部始终提供电路,XTAL2接地。这种情况一般是当整个单片机系统已经有时钟源或则在多机系统中取得时钟上的同步。

注:
1、系统时钟是CPU时钟,RCT是实时时钟。
2、系统时钟的目的是高速稳定,而实时时钟目的是低功耗精确。
51单片机时钟工作原理
时钟电路和时序

  1. 时钟电路
    在MCS-51单片机片内有一个高增益的反相放大器,反相放大器的输入端为XTAL1,输出端为XTAL2,由该放大器构成的振荡电路和时钟电路一起构成了单片机的时钟方式。根据硬件电路的不同,单片机的时钟连接方式可分为内部时钟方式和外部时钟方式,如图2.11所示。

    (a)内部方式时钟电路 (b)外接时钟电路


内部时钟原理图 (就是一个自激振荡电路)

在内部方式时钟电路中,必须在XTAL1和XTAL2引脚两端跨接石英晶体振荡器和两个微调电容构成振荡电路,通常C1和C2一般取30pF,晶振的频率取值在1.2MHz~12MHz之间。对于外接时钟电路,要求XTAL1接地,XTAL2脚接外部时钟,对于外部时钟信号并无特殊要求,只要保证一定的脉冲宽度,时钟频率低于12MHz即可。
晶体振荡器的振荡信号从XTAL2端送入内部时钟电路,它将该振荡信号二分频,产生一个两相时钟信号P1和P2供单片机使用。时钟信号的周期称为状态时间S,它是振荡周期的2倍,P1信号在每个状态的前半周期有效,在每个状态的后半周期P2信号有效。CPU就是以两相时钟P1和P2为基本节拍协调单片机各部分有效工作的。
  1. 指令时序
    我们将单片机的基本操作周期称作机器周期,一个机器周期由6个状态组成,每个状态由两个时相P1和P2构成,故一个机器周期可依次表示为S1P1,S1P2,…,S6P1,S6P2,即一个机器共有12个振荡脉冲。为了大家便于分析CPU的时序,在此先对以下几个概念作一介绍。
    (1)振荡周期
    振荡周期指为单片机提供定时信号的振荡源的周期或外部输入时钟的周期。
    (2)时钟周期
    时钟周期又称作状态周期或状态时间S,它是振荡周期的两倍,它分为P1节拍和P2节拍,通常在P1节拍完成算术逻辑操作,在P2节拍完成内部寄存器之间的传送操作。
    (3)机器周期
    一个机器周期由6个状态组成,如果把一条指令的执行过程分作几个基本操作,则将完成一个基本操作所需的时间称作机器周期。单片机的单周期指令执行时间就为一个机器周期。
    (4)指令周期
    指令周期即执行一条指令所占用的全部时间,通常为1~4个机器周期。
    在图2.12中给出了MCS-51单片机的典型取指、执行时序。由图可知,在每个机器周期内,地址锁存信号ALE两次有效,一次在S1P2与S2P1之间,另一次在S4P2和S5P1之间。
    图2.12 MCS-51单片机取指、执行时序
    从图2.12我们可以看出,对于单周期指令,当操作码被送入指令寄存器后,指令的执行从S1P2开始。若对于双字节单周期指令,则在同一机器周期的S4期间读入第二个字节。如果是单字节单周期指令,则在S4期间仍然保持读操作,但所进行的读操作为无效操作,同时程序计数器PC并不加1。
    在图2.12(a)和(b)给出了单字节单周期和双字节单周期指令的时序,这些操作都在S6P2结束时完成指令操作。
    在图2.12(c)中给出了单字节双周期指令时序,在两个机器周期内进行了四次读操作,由于是单字节指令,故后面的三次读操作是无效的。
    在图2.12(d)中给出了访问外部数据存储器指令MOVX的时序,它是一条单字节双周期指令。在执行MOVX指令期间,外部数据存储器被访问且选通时跳过两次取指操作,其中在第一个机器周期S5开始送出片外数据存储器的地址后,进行读、写数据,在此期间并无ALE信号,故第二周期不产生取指操作。

嵌入式单片机基础篇(五)之stm32F1以及51单片机时钟详解相关推荐

  1. 单片机成长之路(51基础篇) - 013 MCS-51单片机控制详解–T2MOD

    T2CON:定时器控制寄存器 寄存器地址0C8H,位寻址0C8H-0CFH. 位地址 CF CE CD CC CB CA C9 C8 位符号 TF2 EXF2 RCLK TCLK EXEN2 TR2 ...

  2. CSS基础篇--CSS/CSS3中的原生变量var详解

    使用语法 首先我们先来看一个例子: html代码: <div class="element">这是一段文字</div> css代码: .element {w ...

  3. Java基础篇(04):日期与时间API用法详解

    本文源码:GitHub·点这里 || GitEE·点这里 一.时间和日期 在系统开发中,日期与时间作为重要的业务因素,起到十分关键的作用,例如同一个时间节点下的数据生成,基于时间范围的各种数据统计和分 ...

  4. 嵌入式单片机基础篇(一)之stm32F1GPIO详解

    嵌入式单片机基础篇(一)之stm32F1GPIO详解 stm32F1GPIO详解 下面以stm32f103zet6以及51单片机为例,讲解单片机最基本部分IO口 第一部分:stm32 STM32 的 ...

  5. 嵌入式单片机基础篇(八)之两只看门狗

    嵌入式单片机基础篇(八)之两只看门狗 独立看门狗与窗口看门狗 第一部分:stm32 .STM32 内部自带了 2 个看门狗:独立看门狗(IWDG)和窗口看门狗(WWDG). (一)独立看门狗 STM3 ...

  6. [Python从零到壹] 五十一.图像增强及运算篇之图像灰度直方图对比分析万字详解

    欢迎大家来到"Python从零到壹",在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界.所有文章都将结合案例.代码和作者的经验讲 ...

  7. Kotlin——中级篇(二): 属性与字段详解

    在前面的章节中,详细的为大家讲解到了Kotlin中对类的类的定义.使用.初始化.初始化.类继承等内容,但是在一个类中,几乎上是不可能不出现属性与字段(field)的,这一篇文章就为大家奉上Kotlin ...

  8. c linux time微秒_学习linux,看这篇1.5w多字的linux命令详解(6小时讲明白Linux)

    用心分享,共同成长 没有什么比每天进步一点点更重要了 本篇文章主要讲解了一些linux常用命令,主要讲解模式是,命令介绍.命令参数格式.命令参数.命令常用参数示例.由于linux命令较多,我还特意选了 ...

  9. Mybatis系列全解(五):全网最全!详解Mybatis的Mapper映射文件

    封面:洛小汐 作者:潘潘 若不是生活所迫,谁愿意背负一身才华. 前言 上节我们介绍了 < Mybatis系列全解(四):全网最全!Mybatis配置文件 XML 全貌详解 >,内容很详细( ...

  10. 单片机检测电机频率c语言,基于51单片机的电动机测速表的设计.doc

    基于51单片机的电动机测速表的设计 基于51单片机的电动机测速表设计 摘 要 本文介绍了采用光电传感器实施电机转速测量的方法.基本原理,完成了一种基于AT89C51单片机平台的电动机测速表的软硬件设计 ...

最新文章

  1. 物联网技术周报第 145 期: ESP8266 和 IFTTT 自制 WiFi 智能秤
  2. python 判断debug
  3. boost::function_types::is_function用法的测试程序
  4. Searchsploit
  5. C/C++中ASCII与Unicode字符串相互转换
  6. Java基础 Day04(个人复习整理)
  7. spring javafx_Oracle Spring Clean JavaFX应该吗?
  8. python spider怎么用_python爬虫入门(七)Scrapy框架之Spider类
  9. php做乘法表,用PHP生成表单和乘法表
  10. 用科学数据求真:月球的激光发射器有用吗?
  11. [Unity] 3D数学基础 - 2D旋转矩阵
  12. postman测试JSON参数接口
  13. 基于Java的网上订餐系统(附:源码 课件)
  14. NoteExpress导入题录失败
  15. 操作系统-比例份额调度
  16. 每日一诗词 —— 临江仙
  17. Python函数语法里的中括号和逗号是什么意思
  18. 假定在使用CSMA/CD协议的10Mbit/s以太网中某个站在发送数据时检测到碰撞,执行退避算法时选择了随机数r=100.试问这个站需要等待多长时间后才能再次发送数据?如果是100Mbit/s的以太网
  19. 做一只展翅翱翔的雄鹰
  20. Docker+NETCore系列文章(三、Docker常用命令)

热门文章

  1. python pickle文件大小_无法在Python中加载以前转储的大尺寸pickle文件
  2. java 三次样条插值_java – 三次样条插值的正确实现
  3. 移动互联网之路——Axure RP 8.0网站与APP原型设计从入门到精通
  4. 回溯算法高效解标准数独(MarkDown)
  5. 车牌识别算法介绍与实践
  6. 知识问答题小程序头脑王者源码
  7. 经纬度坐标和投影坐标的转换
  8. vue+离线百度地图
  9. linux复制压缩包到另一个文件夹,linux复制文件到另一个文件夹或目录
  10. 东芝Toshiba DP-2210 打印机驱动