问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

STM32定时器(TIM)详解:从基本概念到代码实践

创作时间:
作者:
@小白创作中心

STM32定时器(TIM)详解:从基本概念到代码实践

引用
CSDN
1.
https://m.blog.csdn.net/TENET123/article/details/138262182

STM32定时器(TIM)是STM32微控制器中一个非常重要的外设,广泛应用于各种定时、计数和PWM信号生成等场景。本文将详细介绍STM32定时器的基本概念、不同类型定时器的功能特点,以及如何通过定时中断实现精确的时间控制。

1. TIM简述

定时器的基本功能是在预定的时间间隔内产生周期性的中断。例如,定时器可以被设置为每1ms产生一个中断信号,这常用于创建周期性的服务例程,如操作系统的时钟滴答。

STM32的定时器通常包含一个16位的计数器,这意味着它能够计数从0到65535(2^16 - 1)。当计数器从0计数到预设值时,可生成中断或其他事件。在72MHz的时钟频率下,如果定时器的预分频器(Prescaler)设为72-1(即每72个时钟周期计数一次),则计数器每计数到72000就相当于过去了1秒。这样就可以用来测量时间,或创建延时等。并且最大定时为59.65s。

定时器可以从不同的时钟源中选择,例如内部的主时钟或外部时钟源。定时器可以在多种模式下工作,包括简单的定时模式(如计数溢出时产生中断),PWM产生模式(用于调整电压输出,控制电机速度等),输入捕获模式(测量外部事件的时间间隔,如信号的频率)等。

定时器还可以配置为触发ADC(模数转换器)的启动,或与其他定时器同步等。

2. 定时器类型

STM32的定时器主要分为三种类型:

  • 高级定时器:TIM1、TIM8,位于APB2总线上。这类定时器拥有全部的功能,例如高级PWM控制,还支持三相电机的正反变换,复杂的同步控制,以及与其他高级功能的集成,如直接数字控制转换(DCC)。并且额外具有重复计数器、死区生成、互补输出、刹车输入等功能。

  • 通用定时器:TIM2、TIM3、TIM4、TIM5,位于APB1总线上。拥有基本定时器的全部功能,并且额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能。

  • 基本定时器:TIM6、TIM7,位于APB1总线上。拥有定时中断、主从触发DAC的功能。

对于STM32F103C8T6,拥有的定时器资源为:TIM1、TIM2、TIM3、TIM4,所以在使用任何外设时,要先查明这个芯片有没有这个功能。

2.1 基本定时器

基本定时器的核心组件包括预分频器、计数器和自动重装载寄存器。预分频器对72MHz的计数时钟进行预分频,计数器可以对预分频后的计数时钟进行计数,自动重装载寄存器存储计数目标值。当计数器的值等于自动重装值时,就会产生中断信号。

2.2 通用定时器

通用定时器相比基本定时器增加了更多功能。它不仅支持向上计数模式,还支持向下计数模式和中央对齐计数模式。此外,通用定时器还支持外部时钟源,并且具有输入捕获和输出比较电路。


通用定时器结构示意图

2.3 高级定时器

高级定时器相比通用定时器增加了更多功能。它增加了重复次数计数器,可以实现每隔几个计数周期才会发生一次更新事件和更新中断。此外,高级定时器还具有死区生成电路、互补输出和刹车输入等保护机制。

3. 定时中断

定时中断的实现需要配置预分频器、计数器和自动重装载寄存器。预分频器的参数变化不会立刻生效,而是等到本次计数周期完成,产生了更新事件,改变后的参数才会起作用。计数器的计数频率和溢出频率可以通过以下公式计算:

  • 计数器计数频率:CK_CNT = CK_PSC / (PSC + 1)
  • 计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1)

为了防止在运行途中更改造成错误,引入了影子寄存器,目的是让值的变化和更新事件同步发生。

4. 代码示例

4.1 内部时钟定时中断

下面是一个使用内部时钟的定时中断示例。代码主要分为以下几个步骤:

  1. 开启RCC时钟
  2. 选择时基单元的时钟源
  3. 配置时基单元
  4. 配置输出中断控制
  5. 配置NVIC
  6. 运行控制
#include "stm32f10x.h"

//定时中断初始化
void Timer_Init(void)
{
    //开启时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
    
    //配置时钟源
    TIM_InternalClockConfig(TIM2);		//选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
    
    //时基单元初始化
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//计数器模式,选择向上计数
    TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;				//计数周期,即ARR的值
    TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;				//预分频器,即PSC的值
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			//重复计数器,高级定时器才会用到
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);				//将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
    
    //中断输出配置
    TIM_ClearFlag(TIM2, TIM_FLAG_Update);						//清除定时器更新标志位
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);					//开启TIM2的更新中断
    
    //NVIC中断分组
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2
    
    //NVIC配置
    NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;				//选择配置NVIC的TIM2线
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;	//指定NVIC线路的抢占优先级为2
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
    NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设
    
    //TIM使能
    TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}

4.2 外部时钟定时中断

下面是一个使用外部时钟的定时中断示例。代码主要分为以下几个步骤:

  1. 开启RCC时钟
  2. GPIO初始化
  3. 外部时钟配置
  4. 时基单元初始化
  5. 中断输出配置
  6. NVIC中断分组和NVIC配置
  7. TIM使能
#include "stm32f10x.h"

//定时中断初始化
void Timer_Init(void)
{
    //开启时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
    
    //GPIO初始化
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);						//将PA0引脚初始化为上拉输入
    
    //外部时钟配置
    TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F);
    
    //时基单元初始化
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//计数器模式,选择向上计数
    TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;					//计数周期,即ARR的值
    TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;				//预分频器,即PSC的值
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			//重复计数器,高级定时器才会用到
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);				//将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
    
    //中断输出配置
    TIM_ClearFlag(TIM2, TIM_FLAG_Update);						//清除定时器更新标志位
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);					//开启TIM2的更新中断
    
    //NVIC中断分组
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2
    
    //NVIC配置
    NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;				//选择配置NVIC的TIM2线
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;	//指定NVIC线路的抢占优先级为2
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
    NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设
    
    //TIM使能
    TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}

//返回定时器CNT的值
uint16_t Timer_GetCounter(void)
{
    return TIM_GetCounter(TIM2);	//返回定时器TIM2的CNT
}

通过以上代码示例,读者可以了解如何配置STM32定时器实现定时中断功能。这些代码可以作为嵌入式系统开发中的基础参考,帮助开发者快速上手STM32定时器的使用。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号