赞
踩
定时器就是计数器,简称TIM,可以对输入时钟进行计数,并在计数值达到设定值时触发中断或触发其他外设。
根据复杂度和应用场景,定时器分为三种,它们的功能从高级到低级向下兼容。高级定时器连接的是性能更高的APB2总线(最大72Mhz),而APB1最大36Mhz。
尽管如此,三类定时器的内部时钟来源都能达到72Mhz,原因见时钟树。
RCC时钟树
1、时钟的分配
RCC时钟树:在STM32中用来产生和配置时钟,并且把配置好的各个外设都发射到各个外设的系统。 时钟是所有外设运行的基础,所以时钟是最先配置的东西。在程序执行时,在执行主程序之前还会执行一个
SystemInit
函数。2、时钟的产生
有4个振荡源,分别是内部的8MHz高速RC振荡器、外部的4-16MHz高速石英振荡器(一般都外接8MHz)、外部的32.768kHz低速晶振振荡器(一般给RTC提供时钟)、内部的40kHz低速RC振荡器(给看门狗WDG提供时钟)。外部的石英振荡器比内部的RC振荡器更加稳定。如果系统非常简单,且不需要过于精确的时钟,就可以使用内部的RC振荡器,这样可以省下外部的晶振电路。
在SystemInit函数中是这样来配置时钟的:首先会启动内部的8MHz高速RC振荡器产生时钟,选择该时钟为系统时钟,暂时以8MHz的内部时钟运行;然后再启动外部的8MHz高速石英振荡器产生时钟,进入PLLMUL锁相环进行倍频,8MHz倍频9倍,得到72MHz,待锁相环输出稳定后,选择锁相环输出为系统时钟。这样就把系统时钟从8MHz切换为了72MHz。如下图所示的电路
图中的CSS称为时钟安全系统,它同样负责切换时钟。CSS可以检测时钟的运行状态,一旦外部时钟失效,它就会自动把外部时钟切换为内部时钟,从而保证程序可以正常运行,不会卡死造成事故。另外在高级定时器的刹车输入功能中,CSS同样负责检测当外部时钟失效时,立即切断输出控制引脚,切断电机输出,防止发生意外。据此可以推测:如果外部晶振出问题,可能会出现程序时钟慢大概10倍的现象。如果外部时钟的硬件电路有问题(晶振短路或连接错误等),系统的时钟就无法切换到72MHz,会保持内部的8MHz运行。
时基单元
时基单元主要包含3个16位的PSC预分频器、CNT计数器、ARR自动重装载寄存器。
工作过程为:
- 时钟来源于内部时钟APB1,先经过PSC预分频(实际分频系数PSC+1)
- 再传给CNT进行向上计数,达到设定值溢出(实际为ARR+1)
- 计数值回到0,然后产生计数器溢出事件,可以选择如下两种之一:
- Update Interrupt 更新中断(中断通往NVIC,配置好通道就能让CPU响应)
- Update 更新事件 (它不触发中断,而是触发其他外设,像基本定时器就是TRGO触发DAC)
因此,在时基单元的控制下,最长可延时Tmax=65536*65536/72M≈59.65s
影子寄存器
这里特别的地方就是,PSC和ARR两个寄存器框带上了阴影,因为它们都是由“影子寄存器”真正操控生效。
- 对于PSC,如果在计数过程想修改分频系数,不是立即生效,虽然控制寄存器中的值已经修改了,但是得等到这轮计数溢出更新事件后再生效,即PSC修改的值先进入到了影子寄存器中进行缓冲等待;
- 对于ARR,影子寄存器的功能同理,不过stm32还专门设置了自动重装载预加载使能位APRE(详见手册),简单来说就是,可以通过设置该位来决定是否预加载,预加载就是缓冲的意思,如果设置不预加载,即在计数过程中修改ARR的值会立即生效。
时基单元
通用定时器的时基单元和基本定时器的区别在于:计数方式上多了两种。
- 向下计数模式:从重装值ARR向下自减到0,然后回到重装值产生计数器溢出事件
- 中央对齐模式:实际上就是先向上计数到ARR-1,产生一个计数器溢出事件;然后向下计数到1,产生一个计数器溢出事件;最后从0开始重新计数
实际上简单且用的最多的还是向上计数,掌握这个就好
时钟来源
输入捕获、输出比较
输入捕获和输出比较都有4个通道(占据4个GPIO口的复用功能)。输入捕获可用于测量输入的方波频率,输出比较可用于PWM波的产生和占空比调节,但是!!同一个定时器的通道不能同时使用这两个功能,因为它们的寄存器是共用的。
高级定时器大部分结构与通用定时器相同,拓展的地方在图上都框了出来。
定时器中最核心的部分是时基单元,图中“运行控制”就是控制寄存器中的一些位,比如用来启动或停止定时器,配置计数方式等,操作这些寄存器就能控制时基单元的运行了。再一个就是计时溢出产生的溢出事件可以选择中断,这里多了“中断输出控制”,是因为在定时器的结构中,不仅计数溢出可以申请中断,还有像时钟来源选择其他时钟触发输入时,输入捕获和输出比较匹配时都可以,所以配置中断需要明确需要的是哪个去触发中断。
- #include "stm32f10x.h"
- #include "tim.h"
-
- uint32_t num = 0;
-
- void Tim_Config(void)
- {
- TIM_TimeBaseInitTypeDef tim_configstruct;
- NVIC_InitTypeDef nvic_structinit;
-
- //配置时钟
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //外设时钟使能
- TIM_InternalClockConfig(TIM2); //选择内部时钟源
-
- //配置时基单元
- tim_configstruct.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
- tim_configstruct.TIM_ClockDivision = TIM_CKD_DIV1; //外部引脚输入时钟分频系数 用于采样滤波
- tim_configstruct.TIM_Period = 10000-1; //1s
- tim_configstruct.TIM_Prescaler = 7200-1;
- tim_configstruct.TIM_RepetitionCounter = 0; //重复次数计数器是高级定时器才有的 这里不用就给0
- TIM_TimeBaseInit(TIM2,&tim_configstruct); //初始化定时器
-
- //初始化后相当于给PSC、ARR写入新的值,原本是有寄存器缓冲的
- //但为了能立刻生效开始计数,初始化后默认置位了标志位进入了中断,所以需要手动清除
- TIM_ClearFlag(TIM2, TIM_IT_Update);
-
- //配置NVIC
- TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //设置中断来源 是由计数溢出产生
- nvic_structinit.NVIC_IRQChannel = TIM2_IRQn; //设置中断通道
- nvic_structinit.NVIC_IRQChannelCmd = ENABLE; //中断通道使能
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
- nvic_structinit.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级
- nvic_structinit.NVIC_IRQChannelSubPriority = 1; //响应优先级
- NVIC_Init(&nvic_structinit);
-
- TIM_Cmd(TIM2,ENABLE); //使能定时器 开始计数
- }
-
- void TIM2_IRQHandler(void) //配置中断函数
- {
- if( TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
- {
- num++;
- TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除中断标志位
- }
- }

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。