STM32时钟和定时器

时钟源

STM32包含了5个时钟源,分别为HSI、HSE、LSI、LSE、PLL。

  • HSI是高速内部时钟、RC振荡器,频率为8MHz;
  • HSE是高速外部时钟,即晶振,可接石英/陶瓷谐振器或接外部时钟源,频率范围为4MHz~16MHz;
  • LSI为低速内部时钟、RC振荡器,频率40kHz;
  • LSE为低速外部时钟,接32.768kHz晶振,作为RTC时钟源;
  • PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz;

定时器TIM2~TIM7挂载在APB1分频器上,TIM1和TIM8挂载在APB2分频器上;APB1上面挂载的是低速外设,APB2上挂载高速外设。APB2可以工作在72MHz下,而APB1最大是36MHz

定时器的产生路线:系统时钟 -> AHB预分频 -> APB2预分频 -> TIM1倍频器 -> 产生TIM1的时钟系统

在system_stm32f10x.c文件下,有默认定义SYSCLK_FREQ_72MHz,同时在SystemInit()函数下调用了SetSysClock(),根据宏定义将时钟设为72MHz。读取SystemCoreClock变量即可获得系统时钟频率

在默认情况下,系统的各个时钟频率如下:

  • SYSCLK:72M
  • AHB:72M
  • APB1(PCLK1):36M
  • APB2(PCLK2):72M
  • PLL:72M

 计时器的类型

  • TIM1和TIM8是高级定时器
  • TIM2-TIM5是通用定时器
  • TIM6和TIM7是基本的定时器

这8个定时器都是16位的,它们的计数器的类型除了基本定时器TIM6和TIM7其他都支持向上、向下、向上/向下这3种计数模式。

计数器的三种计数模式

  • 向上计数模式:从0开始,计到arr预设值,产生溢出事件,返回重新计时
  • 向下计数模式:从arr预设值开始,计到0,产生溢出事件,返回重新计时
  • 中央对齐模式:从0开始向上计数,计到arr产生溢出事件,然后向下计数,计数到1以后,又产生溢出,然后再从0开始向上计数。(此种技术方法也可叫向上/向下计数)

各定时器的主要功能

基本定时器(TIM6、TIM7)的主要功能:

只有最基本的定时功能,基本定时器TIM6和TIM7各包含一个16位自动装载计数器,由各自的可编程预分频器驱动。主要用于产生DAC触发信号。

通用定时器(TIM2~TIM5)的主要功能:

除了基本的定时器的功能外,还具有测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM)

高级定时器(TIM1、TIM8)的主要功能:

高级定时器不但具有基本/通用定时器的所有的功能外,还具有控制交直流电动机所有的功能,比如它可以输出6路互补带死区的信号,刹车功能等等。

详细的定时器设定如下:

  1. 首先要搞清楚定时器的计数时钟频率,在预分频系数≠1的时候,TIM2~7的时钟频率为APB1的频率的2倍,即72MHz,预分频系数的默认值不是1。
  2. 定时器的设置主要包括定时器的初始化和中断的初始化。

定时器的时钟不是直接来自APB1或APB2,而是来自输入为APB1或APB2的一个倍频器,当APB1的预分频系数为1时,这个倍频器不起作用,定时器的时钟频率等于APB1的频率;当APB1的预分频系数为其它数值(即预分频系数为2、4、8或16)时,这个倍频器起作用,定时器的时钟频率等于APB1的频率的两倍

假定AHB=36MHz,因为APB1允许的最大频率为36MHz,所以APB1的预分频系数可以取任意数值;

当预分频系数=1时,APB1=36MHz,TIM2~7的时钟频率=36MHz(倍频器不起作用);

当预分频系数=2时,APB1=36MHz/2=18MHz,在倍频器的作用下,TIM2~7的时钟频率=36MHz;

当预分频系数=4时,APB1=36MHz/4=9MHz,在倍频器的作用下,TIM2~7的时钟频率=18MHz;

当预分频系数=8时,APB1=36MHz/8=4.5MHz,在倍频器的作用下,TIM2~7的时钟频率=9MHz;

当预分频系数=16时,APB1=36MHz/16=2.25MHz,在倍频器的作用下,TIM2~7的时钟频率=4.5MHz;

既然需要TIM2~7的时钟频率=36MHz,为什么不直接取APB1的预分频系数=1呢?这是因为APB1不但要为TIM2~7提供时钟,而且还要为其它外设提供时钟,设置这个倍频器可以在保证其它外设使用较低时钟频率时,TIM2~7仍能得到较高的时钟频率。例如当AHB=72MHz时,APB1的预分频系数必须大于2,因为APB1的最大频率只能为36MHz。如果APB1的预分频系数=2,则因为这个倍频器,TIM2~7仍然能够得到72MHz的时钟频率。能够使用更高的时钟频率,无疑提高了定时器的分辨率,这也正是设计这个倍频器的初衷。

定时器初始化:

首先定义TIM_TimeBaseInitTypeDef类型的结构体,它包含了如下的内容:

typedef struct
{
  uint16_t TIM_Prescaler;
  uint16_t TIM_CounterMode;
  uint16_t TIM_Period;
  uint16_t TIM_ClockDivision;
  uint8_t TIM_RepetitionCounter;
} TIM_TimeBaseInitTypeDef;

第一项 TIM_Prescaler 是定时器预分频值,时钟源经该预分频器才是定时器时钟,它设定TIMx_PSC 寄存器的值。可设置范围为 0 至 65535,实现 1至 65536 分频。

第二项 TIM_CounterMode 为定时器计数模式,它的内容如下:

#define TIM_CounterMode_Up                 ((uint16_t)0x0000)
#define TIM_CounterMode_Down               ((uint16_t)0x0010)
#define TIM_CounterMode_CenterAligned1     ((uint16_t)0x0020)
#define TIM_CounterMode_CenterAligned2     ((uint16_t)0x0040)
#define TIM_CounterMode_CenterAligned3     ((uint16_t)0x0060)

它可以是向上计数、向下计数以及三种中心对齐模式。基本定时器只能是向上计数,即 TIMx_CNT只能从0开始递增,并且无需初始化。

第三项 TIM_Period 为定时器周期,当计数寄存器的值递增到等于该值时,将相关事件标志位置位。范围为0 至 65535。

第四项 TIM_ClockDivision 为时钟分频,设置定时器时钟 CK_INT 频率与数字滤波器采样时钟频率分频比,基本定时器没有此功能,不用设置。

第五项 TIM_RepetitionCounter 为重复计数器,属于高级控制寄存器专用寄存器位,利用它可以非常容易控制输出 PWM 的个数。一般的定时器不需要设置。

循环定时时间的计算:基本定时器只有内部时钟72MHz,定时器的实际时钟=内部时钟 / ( 定时器预分频值 + 1 ),那么如果 TIM_Prescaler 设置为7200-1,则内部时钟源经过定时器预分频后即可得到 (72MHz / (7200-1)+1)=10kHz的频率。然后 TIM_Period 设置为5000-1(因为计数是从0开始,所以要减1),即可得到定时时间为(5000 * (1 / 10kHz))=0.5s的定时器。

除此之外还要设置中断的类型,一般的定时器为更新中断,即由溢出事件产生的中断,设置的方式为:TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE),其中第一项是定时器代号,第二项为类型,这里设定为更新方式,第三项为使能。

TIM3的初始化函数:

void TIM3_Init(u16 arr,u16 psc)
{
     // 定时时间=(arr+1)*(psc+1)/72 单位为us
    TIM_TimeBaseInitTypeDef TIM_STR;
    NVIC_InitTypeDef NVIC_STR;
    // TIM3挂载在APB1上,由于预分频系数默认不是1,所以TIM3的时钟为2*APB1=72M
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
    //初始化定时器
    TIM_STR.TIM_Period=arr;
    TIM_STR.TIM_Prescaler=psc;
    // (arr+1)*(psc+1)/TIM时钟=定时器溢出中断触发周期
    TIM_STR.TIM_ClockDivision=TIM_CKD_DIV1;
    TIM_STR.TIM_CounterMode=TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3,&TIM_STR);
    TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
    // 初始化中断
    // 设定为TIM3中断
    NVIC_STR.NVIC_IRQChannel=TIM3_IRQn;
    // 先占优先级0级
    NVIC_STR.NVIC_IRQChannelPreemptionPriority=0;
    // 从优先级3级
    NVIC_STR.NVIC_IRQChannelSubPriority=3;
    // IRQ通道时能
    NVIC_STR.NVIC_IRQChannelCmd=ENABLE;
    // 中断初始化
    NVIC_Init(&NVIC_STR);
    // 使能定时器
    TIM_Cmd(TIM3,ENABLE);
}

中断服务函数:

TIM3的中断函数为 TIM3_IRQHandler

中断服务函数内包含了:判断是否发生中断、中断发生后执行的内容、清除标志位三部分。

void TIM3_IRQHandler(void)
{
    // 判断是否发生中断
    if(TIM_GetITStatus(TIM3,TIM_IT_Update)!=RESET)
    {
        // 清除标志位
        TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
        // 执行中断内容
    }
}

完成调用

只要在main函数里调用 TIM3_Init() 函数,并填入适当的参数,即可实现精确的定时中断,例如获得1秒,即(arr+1)*(psc+1)/72000000 = 1,则可分解成arr=9999,psc=7199,配置如下:

TIM3_Init(9999,7199);

 

posted @ 2020-02-29 22:02  让我们荡起双桨!  阅读(3103)  评论(0编辑  收藏  举报