【STM32】定时器介绍&定时中断

使用的单片机机型为STM32F103C8T6

文章目录

  • 定时器
  • 时钟源
  • 定时中断
  • 编程实例
  • 定时器内部时钟
  • 定时器外部时钟源
  • 定时器

    TIM(Timer) 定时器
    可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
    不仅具备基本的定时中断功能,还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能

    根据复杂度和应用场景分为高级定时器、通用定时器、基本定时器三种类型

    本STM32机型的定时器资源:TIM1,TIM2,TIM3,TIM4,即一个高级定时器,三个通用定时器

    基本定时器

    基本定时器只有 内部时钟(系统时钟) 这一时钟源
    时钟信号先到达 触发控制器,控制选择如下二者功能之一:

    1. TRGO 为主模式触发 DAC,用于输出模拟信号
    2. 时基单元:由预分频器,CNT计数器,自动重装寄存器组成。用于计时,计数,触发定时中断
  • 预分频器:用于对时钟源进行分频。如时钟频率为 72MHz,预分频器值为72,则输出到 CNT计数器的频率为72/72=1MHz。
  • CNT计数器:随时钟频率计数加一。如频率为1MHz,则每1us加一
  • 自动重装寄存器:类似阈值,当 CNT计数等于自动重装值时,触发定时器中断
  • 通用定时器

    通用定时器相比基础定时器看上去就复杂很多,主要多了如下功能:

  • 内外时钟源选择:内部时钟、外部输入
  • 输入捕获:测PWM,占空比
  • 输出比较:输出PWM。可用于控制舵机,直流电机
  • 编码器:测量编码器速度,方向
  • 主从触发模式功能:定时器联调
  • 高级定时器

    后续补充。。。。。。。。。。。。。。。。。

    时钟源

    时钟树

    STM32的时钟树由多个时钟源和时钟分频组成,为STM32芯片提供各种时钟信号

    STM32的基础时钟源有4个振荡器时钟:HSI(高速内部时钟)、HSE(高速外部时钟)、LSI(低速内部时钟)、LSE(低速外部时钟)。这是最底层的时钟来源

    其中

  • 40kHz低速内部 LSI 振荡器,可用于驱动独立看门狗,或通过程序选择驱动 RTC(Real-Time Clock 实时时钟),用于从停机/待机模式下自动唤醒系统
  • 32.768kHz低速外部 LSE 振荡器也可驱动 RTC
  • 我们常说的系统时钟,为SYSCLK,由三种不同的时钟源驱动:HSI、HSE、PLL

    此机型的主频为72MHz,但 HSE 最高频率为16MHz,HSI 频率为 8MHz,都达不到主频。那系统主频是怎么来的呢?
    其实就是将 HSI 或 HSE 接入 PLL,通过 PLL 倍频产生的

    系统时钟通过选择器选择时钟源,输入源有 HSI(内部高速时钟)、HSE(外部高速时钟)、PLL锁相环

    通常STM32启动时,先使用 HSI 内部时钟,同时 HSI 接入 PLL锁相环进行倍增,待倍增后的 PPLCLK 稳定后,主频再切换至 72MHz


    外设时钟

    输出到定时器的频率都为72MHz

    APB1预分频虽然会降低频率,但通向定时器2-7,还会进行倍增,又会恢复至72MHz

    APB2同理

    定时中断

    下图为通用定时器的定时中断结构

    分为三个部分:时钟选择,时基单元,中断控制

    时钟选择

    计数器时钟可由下列时钟源提供:

  • RCC内部时钟,频率为72MHz
  • 外部时钟模式2:GPIO控制的外部输入触发 ETR
  • 外部时钟模式1:外部输入脚(TIx),用于输入捕获
  • 内部触发输入(ITRx):使用另一个定时器作为另一个定时器的预分频器,如可以配置一个定时器 Timer1 作为另一个定时器 Timer2 的预分频器,实现定时器联调
  • 时基单元

    预分频器,CNT计数器,自动重装寄存器组成

    预分频器PSC

    预分频器可以将计数器的时钟频率按1到65536之间的任意值进行分频。如系统频率为72MHz,预分频为2,则计数器接收的频率为72/2=36MHz

    如此图示代表有影子寄存器,作用同缓冲器,可以在工作时被修改,新的预分频器参数在下一次更新事件到来时被采用,如下图

    原先,预分频值为0,即不分频,计数器时钟等于系统时钟。但在计数器工作时,修改了预分频值,如果没有影子寄存器,那么新的分频就会立即应用,导致前后计数频率不同。使用影子寄存器,会先保存新的预分频值,在发生一次更新事件后才应用新的预分频值

  • 计数器计数频率:CK_CNT = CK_PSC / (PSC + 1)
  • 自动重装寄存器ARR

    同样有影子寄存器,当 CNT计数器值等于 ARR 时,触发定时器中断

  • 计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1)
  • 如预分频PSC = 7200 – 1,代表计时器频率为72MHz/7200=1KHz,每1ms计时器加一
    ARR = 1000 – 1,即1000次计数器加一才会溢出,即1000个1ms
    最终效果就为计数器每1s溢出一次,频率为1Hz

    CNT计数器

    计数器有三种模式

  • 向上计数:计数器值从0计数加一到ARR,产生中断

  • 向下计数:计数器值从ARR计数减一到0,产生中断

  • 中央对齐:计数器从0计数加一到 ARR,产生中断;然后再计数减一到0,产生中断

  • 计数器时序

    编程实例

    定时器内部时钟

  • 程序目标:实现计时功能,单位为s
  • 程序原理:使用定时器2,时钟源选择内部时钟,配置时基单元,实现每1s产生一次定时器中断,在中断处理函数中对计数加一
  • 接线图如下:

    定时器配置流程参照下图:分为时钟选择,时基单元,中断控制。相关函数声明在stm32f10x_tim.h

    时钟选择

    使用TIM2,因为为外设,需要开启时钟

    //开启TIM2的时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    

    选择内部时钟源

    //配置时钟源
    //配置内部时钟源为TIM2,若不配置,则默认为内部时钟
    TIM_InternalClockConfig(TIM2);
    

    时基单元

    配置 PSC 和 ARR,实现每1s产生一次中断的效果
    采用向上计数

    //初始化时基单元
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;			//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;		//向上计数,从0到重装值
    TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;					//重装值 - 1,因为从0开始,ARR的值
    TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;					//预分频器,PSC的值。如配置为72,即系统频率72MHz / 72 = 1MHz
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;				//重复计数器,高级定时器才会用到
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
    

    中断控制

    //中断部分
    //时基单元初始化完成后会发起一次更新事件,需要清理一下,避免影响复位
    TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    
    //开启TIM2的更新中断
    TIM_ITConfig(TIM2, TIM_FLAG_Update, ENABLE);
    
    //中断优先级组
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    
    //NVIC中断嵌套初始化
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;						//中断通道
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;						//使能开关
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;			//抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;					//响应优先级
    NVIC_Init(&NVIC_InitStructure);
    
    //开启TIM2
    TIM_Cmd(TIM2, ENABLE);
    

    PS:在TIM_TimeBaseInit中,完成时基单元初始化时,会产生一次更新中断,需要手动清除,否则会出现程序复位为1,而不是0的情况

    完整初始化代码如下:

    /**
      * @brief		定时器初始化
      * @parm		无
      * @retval		无
      */
    void Timer_Init(void)
    {
    	//开启TIM2的时钟
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    	//配置时钟源
    	//配置内部时钟源为TIM2,若不配置,则默认为内部时钟
    	TIM_InternalClockConfig(TIM2);
    	
    	//初始化时基单元
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;			//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
    	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;		//向上计数,从0到重装值
    	TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;					//重装值 - 1,因为从0开始,ARR的值
    	TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;					//预分频器,PSC的值。如配置为72,即系统频率72MHz / 72 = 1MHz
    	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;				//重复计数器,高级定时器才会用到
    	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
    	
    	//中断部分
    	//时基单元初始化完成后会发起一次更新事件,需要清理一下,避免影响复位
    	TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    	
    	//开启TIM2的更新中断
    	TIM_ITConfig(TIM2, TIM_FLAG_Update, ENABLE);
    	
    	//中断优先级组
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    	
    	//NVIC中断嵌套初始化
    	NVIC_InitTypeDef NVIC_InitStructure;
    	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;						//中断通道
    	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;						//使能开关
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;			//抢占优先级
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;					//响应优先级
    	NVIC_Init(&NVIC_InitStructure);
    	
    	//开启TIM2
    	TIM_Cmd(TIM2, ENABLE);
    }
    

    中断处理函数的声明在startup_stm32f10x_md.s

    //定时器2中断处理函数
    void TIM2_IRQHandler(void)
    {
    	if(TIM_GetITStatus(TIM2, TIM_FLAG_Update) == SET)//判断是否是更新中断
    		Num++;
    	TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    }
    

    完整程序链接:【STM32】基础定时器

    定时器外部时钟源

  • 程序目标:使用对射式红外传感器控制GPIO口,遮挡时输出低电平,TIM2以该GPIO为外部时钟源,实现计次
  • 程序原理:定时器初始化大抵相同,还需配置GPIO 和 外部时钟源
  • 接线图如下:

    首先开启相关外设时钟

    //开启TIM2的时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    //开启GPIO的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    

    配置GPIO

    //初始化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);
    

    配置外部时钟源

    //配置时钟源
    //配置外部时钟源
    //								不分频				低电平或下降沿触发		  滤波
    TIM_ETRClockMode1Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_Inverted, 0x0F);
    

    后续时基单元和中断控制配置大抵相同

    完整代码如下:

    /**
      * @brief		定时器初始化
      * @parm		无
      * @retval		无
      */
    void Timer_Init(void)
    {
    	//开启TIM2的时钟
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    	//开启GPIO的时钟
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    	
    	//初始化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);
    	
    	//配置时钟源
    	//配置外部时钟源
    	//								不分频				低电平或下降沿触发		  滤波
    	TIM_ETRClockMode1Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_Inverted, 0x0F);
    	
    	//初始化时基单元
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;			//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
    	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;		//向上计数,从0到重装值
    	TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;					//重装值 - 1,因为从0开始,ARR的值
    	TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;					//预分频器,PSC的值。如配置为72,即系统频率72MHz / 72 = 1MHz
    	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;				//重复计数器,高级定时器才会用到
    	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
    	
    	//中断部分
    	//时基单元初始化完成后会发起一次更新事件,需要清理一下,避免影响复位
    	TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    	
    	//开启TIM2的更新中断
    	TIM_ITConfig(TIM2, TIM_FLAG_Update, ENABLE);
    	
    	//中断优先级组
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    	
    	//NVIC初始化
    	NVIC_InitTypeDef NVIC_InitStructure;
    	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;						//中断通道
    	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;						//使能开关
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    	NVIC_Init(&NVIC_InitStructure);
    	
    	//开启TIM2
    	TIM_Cmd(TIM2, ENABLE);
    }
    

    完整程序链接:【STM32】定时器外部时钟源计次


    以上就是本篇博客的所有内容,感谢你的阅读
    如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。

    作者:好想有猫猫

    物联沃分享整理
    物联沃-IOTWORD物联网 » 【STM32】定时器介绍&定时中断

    发表回复