手把手教你开发stm32——系统时钟(基于hal库)

目录:

  • 1.stm32时钟系统概述
    • 1.1.时钟系统的概念及意义
    • 1.2.常见振荡器简介
    • 1.3.stm32中时钟源的介绍
  • 2.stm32时钟配置
  • 3.SysTick定时器讲解
    • 3.1.SysTick定时器简介
    • 3.2.SysTick定时器工作原理
    • 3.3.systick每1s中触发一次中断代码实现
    • 3.4.systick相关寄存器分析
  • 4.HAL_Delay()函数的实现

1.stm32时钟系统概述

1.1.时钟系统的概念及意义

概念:
时钟系统是由振荡器(信号源)、定时唤醒器、倍频器、分频器等组成的电路。常用的信号源有晶体振荡器和RC振荡器。
手把手教你开发stm32——系统时钟(基于hal库)_第1张图片
意义:
时钟是嵌入式系统的脉搏,处理器啮合在时钟的驱动下完成指令执行,状态转换等等动作,外设部件在时钟的驱动下完成各种工作,比如串口数据的发送、A/D转换、定时器计数等等。因此时钟对于计算机系统是至关重要的,通常时钟系统出现问题也是致命的,比如振荡器不起振、振荡不稳、停振等问题。
一般来说,CPU内核所能接受的振荡频率是比较高的,比如说stm32f103c8t6这个芯片就能够承受72MHZ的频率,但是由于外部石英晶振和内部RC晶振都只是8MHZ的频率,显然不满足我们CPU所需要的频率,所以我们一般需要PLL倍频器来进行频率的倍频,将外部石英晶振或者内部RC晶振的频率提高,以达到CPU需要的频率。
但是呢,由于高速时钟源只能有一路输入,所以,很多外设并不能承受72MHZ的频率,因此,就会有分频器来降低频率,以达到各个外设都能使用的程度,这也就是为什么一个时钟电路需要加上倍频器和分频器的原因了。

1.2.常见振荡器简介

概念:
振荡器是用来产生重复电子讯号的电子元件。其构成的电路叫振荡电路,能将直流电转换为具有一定频率交流信号输出的电子电路或装置。
分类:
振荡器主要可以分为两类:
一类是RC、LC振荡器,这两种振荡器主要是由电阻电容等电子元器件来构成的;RC振荡器采用RC网络作为选频相网络的振荡器。LC振荡器是采用LC振荡回路作为移相和选频网络的正反馈振荡器。
另一类是晶体振荡器,主要是由石英晶体来进行起振从而来输出一定的频率的。

RC振荡器:
RC振荡器是由电阻电容构成的振荡电路,能将直流电转换成具有一定频率的交流信号输出的电子电路或装置。
优点:实现的成本比较低,毕竟就是一个电阻电容。
缺点:是由于电阻电容的精度问题所以RC振荡器的振荡频率会有误差,同时受到温度、湿度的影响较大。
因此我们在stm32中给CPU提供频率的时候,一般都不选用这个RC振荡器作为稳定时钟源,一般都是在很短的时间内提供时钟源以替代晶体振荡器。

晶体振荡器:
石英晶体振荡器是高精度和高稳定度的振荡器,被广泛应用于彩电、计算机、遥控器等各类振荡电路中,以及通信系统中用于频率发生器、为数据处理设备产生时钟信号和为特定系统提供基准信号。
优点:是相对来说振荡频率一般都比较稳定,同时精度较高。
缺点:价格稍微贵一点,并且还需要接两个起振电容。
因此我们在stm32正常运行过程中一般都使用晶体振荡器来提供稳定的振荡频率提供给CPU。

一般而讲,我们在给单片机上电的时候,此时由于晶体振荡器起振需要一定的时间,所以此时就会让RC振荡器来顶替一段时间来作为振荡源的输入,当晶体振荡器起振以后,就会将晶体振荡器作为稳定的振荡源输入。

1.3.stm32中时钟源的介绍

stm32中主要有四个时钟源

  • HSI:高速内部时钟,RC振荡器,频率为8MHZ
  • HSE:高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4~16MHZ,我们stm32f103c8t6用的是8MHZ。
  • LSI低速内部时钟,RC振荡器,频率为32kHZ左右。供独立看门狗和自动唤醒单元使用。独立看门狗的具体内容我们会在后续的章节进行介绍。
  • LSE:低速外部时钟,接频率为32.768kHZ的适应晶体,这个主要是RTC的时钟源。RTC是指时基单元,主要是给单片机计时用的。比如说我们的电脑每次关机以后,再次开机电脑的时间会随着北京时间变化,这就是时基单元的功劳。
    手把手教你开发stm32——系统时钟(基于hal库)_第2张图片
    这个就是stm32f103内部的时钟树,我们一块一块来看。
  1. 第一块就是内部高速时钟源,主要就是由RC振荡器组成的,其振荡频率为8MHZ。
  2. 第二块就是外部高速时钟源,可以通过OSC_OUT和OSC_IN来外接振荡晶体,提供稳定的振荡源。
  3. 第三块就是外部低速时钟,我们可以看到可以通过OSC32_IN和OSC32_OUT来接外部振荡晶体,主要就是给RTC提供时钟频率的。
  4. 第四块就是LSI,是芯片内部由RC振荡器构成的振荡源,主要就是给独立看门狗来提供时钟频率的。
  5. 第五块是倍频器,我们上面的文章讲过,由于外部高速时钟和内部高速时钟提供的时钟源频率不够,所以我们需要使用倍频器将频率提高。
  6. 第六块就是时钟源选择模块,主要就是选择外部高速时钟、内部高速时钟或者倍频后的时钟来给CPU和其他外设提供时钟频率,我们一般都是选择PLLCLK,这样倍频后的时钟就可以输入进去了。

2.stm32时钟配置

我们来配置stm32的系统时钟一般都是在cubemx中直接配置的,如果我们不进行时钟配置的话,很多时候时钟系统就默认外部高速时钟或者内部高速时钟直接输入,这样大概时钟频率在8MHZ,我们知道stm32f103的CPU最大承受的时钟频率为72MHZ,这样就会大大浪费CPU的资源,导致资源的浪费,因此,我们需要进行配置,将输入时钟从倍频器输入,这样就可以使得时钟频率变快。

我们首先来进行引脚的配置
手把手教你开发stm32——系统时钟(基于hal库)_第3张图片
然后我们进行时钟的配置
手把手教你开发stm32——系统时钟(基于hal库)_第4张图片
从图中我们可以看到,第一块和第二块都是选择HSI(内部高速时钟)来进行输入的,我们可以看到,第三块就只有8MHZ的时钟频率,而下面写着最大72MHZ,这样就是很浪费CPU资源的,所以我们需要进行配置,来使时钟频率变高。
手把手教你开发stm32——系统时钟(基于hal库)_第5张图片
我们需要对第一块、第二块进行配置。

  • 首先我们需要将第一块时钟源的输入设置为外部高速晶振,具体原因我们前面也说到过,就是因为晶体振荡器的比较稳定,然后我们需要配置PLLMul,这是用来进行倍频操作的。我们将倍频系数设置为9,这样就可以产生最大72MHZ的时钟频率了。
  • 然后我们配置第二块,设置为锁相环输入,将倍频后的时钟频率进行输入。
  • 从第三块我们可以看到,HCLK已经变成了72MHZ的时钟频率了。
  • 但是我们看到第四块的时候我们可以明显的看到,这个时钟变红了,这是因为APB1只能承受最大36MHZ的时钟频率,因此我们需要再次进行配置。
    手把手教你开发stm32——系统时钟(基于hal库)_第6张图片
    我们将APB1 Prescaler配置为/2,这样我们就看到时钟配置正常了。

通过以上的操作,我们就将stm32的系统时钟配置好了。

3.SysTick定时器讲解

3.1.SysTick定时器简介

概念:
定时器,能够定时、计数的器件称为定时器
SysTick,称作系统滴答定时器,简称滴答定时器。是一个定时设备,位于cortex-M3的内核中,是属于内核外设。
系统定时器一般用于操作系统,用于产生时基,维持操作系统的心跳。根据这个中断,系统就可以实现时间片的计算从而切换进程。
工作原理:
滴答定时器是一个24位定时器,也就是最多能技术2^24。在使用的时候,我们一般给计数器送一个初始的技术支持,计数器向下计数(滴答定时器只能向下计数),每来一个时钟信号,计数初值就减一,计数值减到0的时候,就会发出一次中断。然后重新从计数初值再减一计数,循环不断。

3.2.SysTick定时器工作原理

手把手教你开发stm32——系统时钟(基于hal库)_第7张图片
手把手教你开发stm32——系统时钟(基于hal库)_第8张图片

通过上面的图片我们就可以看出来,Systick是属于内核外设的,因此systick只会挂载到AHB总线上的,既不属于APB1总线,也不属于APB2总线。
然后,我们一般都不会将AHB总线进行分频的操作,并且cortex系统时钟前的8分频既可以使用,也可以不使用,我们在实际配置过程中是没有使用8分频的,因此我们系统时钟的频率为72MHZ。
手把手教你开发stm32——系统时钟(基于hal库)_第9张图片
systick的工作原理如上图所示
systick在工作时会有一个重载数值寄存器和定时计数器,我们可以进行重载数值寄存器初始值的设置,这个值是我们用户自己去设置的。
当我们设置完成重载数值寄存器的值以后,重载数值寄存器会将计数值写入定时计数器,然后,每来一个时钟脉冲以后,定时计数器的值就会自动进行减一的操作,当定时计数器的值减到0以后,就会触发一次中断响应,然后供用户执行相关操作。
当定时计数器的值减到0以后,重载数值寄存器的值会重新加载到定时计数器中,又会开启一轮新的计数。
比如说我们想要1ms触发一次中断,我们就以72MHZ的时钟频率来说,那么每1/72MHZs就会产生一次时钟脉冲,当我们要1ms触发一次中断,那么就用0.001s/1/72MHZs就可以得到重载计数器的值,我们将这些值写入到重载数值寄存器中,就可以实现1ms触发一次中断的效果了。

3.3.systick每1s中触发一次中断代码实现

手把手教你开发stm32——系统时钟(基于hal库)_第10张图片
手把手教你开发stm32——系统时钟(基于hal库)_第11张图片
手把手教你开发stm32——系统时钟(基于hal库)_第12张图片
然后导出工程

下面一步我们来分析代码
手把手教你开发stm32——系统时钟(基于hal库)_第13张图片
我们从main.c函数中找到系统时钟配置函数,转到这个函数。
手把手教你开发stm32——系统时钟(基于hal库)_第14张图片
我们可以看到,这个函数的主体内容就是用来配置系统时钟的,上面的块包括选择外部高速时钟、进行分频操作,内部锁相环选择等内容;下面的块主要就是用来配置各个总线时钟分频系数的,我们可以看到,这个结构体的设置和我们在cubemx中用图形化设置的界面是相同的。

手把手教你开发stm32——系统时钟(基于hal库)_第15张图片

然后我们需要在这个地方再调用一下SysTick_Config();这个函数来配置一下重载数值寄存器(因为我在自动导出的文件中找了半天,没有找到配置重载数值寄存器的函数,所以我们就自己来配置一下吧);括号里面填入我们计算好的自动重载值,这样就可以配置完成了。(这样就可以实现1ms触发一次中断的状态了)。
手把手教你开发stm32——系统时钟(基于hal库)_第16张图片
然后我们在hal_cortex.c中找到SYSTICK_CALLback,这个函数就是用来进行中断操作的。我们可以在main函数中进行这个函数的重写。
手把手教你开发stm32——系统时钟(基于hal库)_第17张图片
通过对这个函数的重写,我们就可以实现1s使PA1的电平翻转一次,实现LED灯亮灭的切换。

3.4.systick相关寄存器分析

systick主要用到下面四个寄存器:

  • systick控制及状态寄存器(SYSTICK_CTRL)
  • systick重装载数值寄存器(SYSTICK_LOAD)
  • systick当前数值寄存器(SYSTICK_VAL)
  • systick校准数值寄存器(SYSTICK_CALIB)

首先我们来分析控制及状态寄存器(由于systick是属于内核外设,因此,在stm32中文手册中并没有systick相关寄存器的介绍,systick相关寄存器的介绍是在内核手册中才有介绍的。)
控制及状态寄存器主要是用来配置systick的相关状态的,比如说,AHB上的分频系数、是否开启计数到0的中断、是否使能systick外设等功能。手册上的功能框图如下图:
手把手教你开发stm32——系统时钟(基于hal库)_第18张图片

其次我们来分析重装载数值寄存器
这个寄存器就是我们用来设置重载数值寄存器的值的,我们从前面就说过,systick需要从重载寄存器将重载值放置入定时计数器中,这样就可以进行递减计数了。我们知道,systick的递减计数器的值最大为24位,所以,这个寄存器也就用了24位来组成重装载数值寄存器。
手把手教你开发stm32——系统时钟(基于hal库)_第19张图片
然后我们来分析当前值寄存器
当前值寄存器就是用来记录当前值的一个寄存器,我们前面讲过,当每次都有一个脉冲输入的时候,定时计数器的值都会进行减1的操作,所以当前值寄存器就是用来记录目前定时计数器还有多少数可以用来进行减1操作,当定时计数器的值减到0以后,就会触发中断来进行中断操作。

最后还有一个数值校准寄存器,这个寄存器是用来进行数值校准的,我们在实际运用过程中是不会经常用到这个寄存器的,目前来讲,我从来都没用到过这个寄存器,所以我们不需要太过于了解这个寄存器。
手把手教你开发stm32——系统时钟(基于hal库)_第20张图片
手把手教你开发stm32——系统时钟(基于hal库)_第21张图片
以上就是对systick整个寄存器的一个描述。

4.HAL_Delay()函数的实现

HAL_Delay()函数的实现其实和systick定时器有着密切的关系。
首先我们在主函数中随便写一个HAL_Delay()函数。
手把手教你开发stm32——系统时钟(基于hal库)_第22张图片
然后我们追进去看
**手把手教你开发stm32——系统时钟(基于hal库)_第23张图片
然后我们就可以看到,其实这个HAL_Delay()函数其实就是采用了一个while循环进行死等的操作。
我们首先看到,在进入这个函数以后,会在1给tickstart附一个HAL_GetTick的值,然后我们在2处不断将新的HAL_GetTick的值与初始值进行比较,当新的值和初始值差值等于我们给到的Delay的毫秒数以后,跳出循环,继续执行。
所以,HAL_GetTick这个函数就比较重要了,我们追进去看。
手把手教你开发stm32——系统时钟(基于hal库)_第24张图片
我们追进去看以后,发现这个函数每次执行的操作就是返回一个数值,这个数值就是我们在while死循环里面等待相减的数值。
然后我们全局搜索uwTick这个变量。
手把手教你开发stm32——系统时钟(基于hal库)_第25张图片
我们就可以发现,这个变量每次都会通过HAL_IncTick这个函数来进行加uwTickFreq这个变量,我们追进去看这个变量。
手把手教你开发stm32——系统时钟(基于hal库)_第26张图片
我们追到最后,会发现这个HAL_IncTick函数就是给uwTick进行自加1的操作,所以HAL_IncTick这个函数就是关键了。
其实这个函数就是我们一开始就发现了,这个函数其实就在Systick_Handler()这个函数中。
手把手教你开发stm32——系统时钟(基于hal库)_第27张图片

所以,最终结论就是,HAL_Delay这个函数,和系统时钟中断有着密不可分的关系,也就是说:每次systick中的定时计数器数值减到0以后,就会触发一次中断,从而使得HAL_Delay函数进行1ms的延时,这样就可以进行传参延时,这也就是官方提供的HAL_Delay是毫秒延时器的原因了。

以上就是我对于系统时钟的一些学习和了解,欢迎大家进行批评指正!!!

你可能感兴趣的:(手把手教你学stm32,stm32,单片机,嵌入式硬件)