STM32学习13 定时器1中断

本文参考江科大视频,侵删!

一、定时器整体框架

1、分类与总线

定时器核心作用:

定时中断:固定周期执行任务(流水灯、周期检测、计时);

计数功能:统计外部脉冲次数;

PWM输出:驱动电机、调光、调速;

2、核心内部模块

1)预分频器PSC:对定时器时钟分频,降低计数频率;

2)计数器CNT:16位寄存器,从0开始向上/向下计数;

3)自动重载寄存器ARR:计数最大值,CNT=ARR时触发溢出事件;

4)中断/事件输出:溢出可产生中断、触发信号(TRGO,用于定时器级联);

5)从模式控制器:选择时钟来源(内部时钟、其他定时器、外部引脚);

二、核心公式、参数计算

定时器基准时钟:CK_PSC:72MHz,经过预分频器后得到计数时钟。

1、计数器时钟频率,含义:1s内计数多少

CK_CNT=CK_PSC/PSC+1,PSC的取值范围0~65535;

2、单次溢出时间(定时周期)

计数器从0计数到ARR,共计数ARR+1次

计数器溢出频率CK_CNK_OV =CK_CNT/ARR+1 =CK_PSC/(PSC+1)/(ARR+1);

则周期是:T=(PSC+1)/CK_PSC *(ARR+1) =(PSC+1)*(ARR+1)/72*1000000(单位s);

举例1ms定时中断:

设:PSC+1 =72,ARR+1=1000。也就是以1us计数1000个。

3、常用定时器参数表

|-------|--------|-------|
| 定时时间 | PSC +1 | ARR+1 |
| 1ms | 72 | 1000 |
| 5ms | 72 | 5000 |
| 10ms | 72 | 10000 |
| 100ms | 720 | 10000 |

注:PSC、ARR都是16位无符号数,最大值65535,单定时器最长定时:

Tmax=65535*6555/72000000 =59.65s,超过这个时长,必须用定时器级联。

三、三种计数模式

配置定时器初始化函数,TIM_CounterMode,日常只用向上计数,另外2种较少使用。

1、向上计数(TIM_CounterMode_Up)

CNT从0到ARR,溢出后清零重新计数,最常用。

2、向下计数(TIM_CounterMode_Down)

CNT从ARR到0,溢出后重装;

3、中心对齐(TIM_CounterMode_CenterAligned)

0→ARR→0,往返计数,主要用于PWM波形对称场景。

四、时钟来源

1、内部时钟(CK_INT)--普通定时默认

芯片内部72MHz时钟,最常用;配置:不开启任何从模式,默认就是内部时钟。

2、内部触发ITRx(定时器级联)

一个定时器的溢出信号,作为另一个定时器的时钟。

3、外部引脚时钟(ETR)

外部引脚输入脉冲,定时器统计脉冲个数(脉冲计数功能)。

4、通道输入捕获时钟

多用于测速、测波形频率等。

五、定时中断完整配置流程

以TIM2,定时1ms为例

1、开启定时器总线时钟:TIM2~TIM7在APB1,TIM1/TIM8在APB2,不开时钟,外设不工作。

2、时基单元初始化(PSC、ARR、计数模式、分频),决定定时周期。

3、选择时钟源

普通定时:默认内部时钟;级联/计数:配置从模式+触发源

4、使能对应中断

溢出中断:TIM_IT_Update 。函数:TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);

5、NVIC中断优先级配置

选择分组2,抢占优先级和子优先级按需配置。

6、使能定时器:TIM_Cmd(TIM2,ENABLE);

7、编写中断服务函数

1)判断中断标志;2)执行任务代码;3)必须清除中断标志,否则死循环进中断。

cpp 复制代码
void TIM2_IRQHandler()
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
	{
		;//执行的代码,禁止长延时、复杂运算、大量函数调用
		
		//手动清除中断挂起标志
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}
}

8、注意事项

1)忘记清除中断标志→不停进中断,程序卡死;

2)中断内写延时/死循环→系统响应慢、中断可能丢失;

3)重复设置自动分组→优先级错乱、中断异常;

9、常用功能区分

1)单纯定时、周期执行任务:内部时钟+向上计数+更新中断;

2)超长定时:定时器级联(ITR内部触发);

3)统计外部脉冲个数:外部时钟模式;

4)电机调速、LED调光:PWM输出(通道比较模式);

5)测量外部高/低电平时间、频率:输入捕获;

10)中断源 TIM_ITConfig(TIM2,中断源,ENABLE);

PWM输出、输入捕获用TIM_IT_CCx;

定时器级联、外部触发同步:用TIM_IT_Trigger;

电机控制、刹车保护:TIM_IT_COM/TIM_IT_Break;

|-----------------|---------------|---------------------|--------------|
| 参数 | 含义 | 触发时机 | 适应场景 |
| TIM_IT_Update | 更新/溢出中断 | 计数器CNT溢出 (到ARR自动重装) | 普通定时中断、周期任务 |
| TIM_IT_CC1~CC4 | 捕获/比较通道1~4中断 | 输入捕获、PWM匹配事件 | 测量脉冲、PWM输出控制 |
| TIM_IT_COM | 换相中断 | 高级TIMER互补通道切换 | 电机控制、三相PWM |
| TIM_IT_Trigger | 触发中断 | 收到外部触发信号(ETR、ITR) | 定时器级联、外部同步 |
| TIM_IT_Break | 刹车中断 | 外部刹车信号触发 | 电机急停、安全保护 |
| | | | |

六、代码部分

练习:以周期1s为例,Num 1秒钟累加一次

1、Time初始化

cpp 复制代码
#include "stm32f10x.h"                  // Device header

uint8_t IT_Flag;
void Timer1_Init()
{
    //开启总线时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
    //使用内部时钟配置定时器内部计数时钟源(可以不写,默认就是内部时钟)
	TIM_InternalClockConfig(TIM2);
	//配置时基单元
	TIM_TimeBaseInitTypeDef TIM_InitStructure;
	TIM_InitStructure.TIM_ClockDivision =TIM_CKD_DIV1;
	TIM_InitStructure.TIM_CounterMode =TIM_CounterMode_Up;
	TIM_InitStructure.TIM_Period =7200-1;
	TIM_InitStructure.TIM_Prescaler =10000-1;
	TIM_InitStructure.TIM_RepetitionCounter =0x00;
	TIM_TimeBaseInit(TIM2,&TIM_InitStructure);
     //中断
	TIM_ClearFlag(TIM2,TIM_IT_Update);
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
	 //配置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);
    //使能定时器
	TIM_Cmd(TIM2,ENABLE);
}
//标志位复位
uint8_t GetITFlag()
{
	if (IT_Flag ==1)
	{
		IT_Flag =0 ;
		return 1;	
	}
		return 0 ;
}
//中断服务函数
void TIM2_IRQHandler()
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update)!=RESET)
	{
		IT_Flag =1;
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);	
	}
}

注:时基单元中,TIM_InitStructure.TIM_ClockDivision =TIM_CKD_DIV1;是引脚采样分频,不影响CNT走数快慢。TIM_CKD_DIV1/TIM_CKD_DIV2/TIM_CKD_DIV4只针对输入捕获、ETR外部滤波采样时钟,用来抗干扰,不影响定时时间。

2、头文件

cpp 复制代码
#ifndef __TIMER1_H
#define __TIMER1_H

void Timer1_Init();
uint8_t GetITFlag();

#endif

3、main函数

cpp 复制代码
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer1.h"

uint16_t Num;

int main()
{
	OLED_Init();
	OLED_ShowString(1,1,"Num:");
	Timer1_Init();
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	while(1)
	{
		if(GetITFlag() ==1)
		{
			Num++;		
		}
		OLED_ShowNum(1,5,Num,5);
	}
}

练习2:利用中断,实现1s钟

步骤1:初始化GPIOA,端口Pin_0

cpp 复制代码
#include "stm32f10x.h"                 

void LED_Init()
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	GPIO_InitTypeDef  GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0 ;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz ;
	GPIO_Init(GPIOA,& GPIO_InitStructure);
	GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2);

}

步骤2:配置定时器及中断函数

cpp 复制代码
#include "stm32f10x.h"                  // Device header

uint16_t Time_Cnt;
void Timer1_Init()
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	TIM_InternalClockConfig(TIM2);
		
	TIM_TimeBaseInitTypeDef TIM_InitStructure;
	TIM_InitStructure.TIM_ClockDivision =TIM_CKD_DIV1;
	TIM_InitStructure.TIM_CounterMode =TIM_CounterMode_Up;
	TIM_InitStructure.TIM_Period =72-1;
	TIM_InitStructure.TIM_Prescaler =1000-1;
	TIM_InitStructure.TIM_RepetitionCounter =0x00;
	TIM_TimeBaseInit(TIM2,&TIM_InitStructure);
	TIM_ClearFlag(TIM2,TIM_IT_Update);
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
		
	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);

	TIM_Cmd(TIM2,ENABLE);
}
//中断服务函数
void TIM2_IRQHandler()
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update)!=RESET)
	{
		Time_Cnt ++;
		if(Time_Cnt >=1000)
		{
			Time_Cnt =0;
			GPIO_WriteBit(GPIOA,GPIO_Pin_0,(BitAction)(1-GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_0)));			
		}					
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);	
	}
}

步骤3:main函数

cpp 复制代码
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer1.h"
#include "LED.h"

extern uint16_t Time_Cnt;
int main()
{
	OLED_Init();
	Timer1_Init();
	LED_Init();
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    OLED_ShowString(2,1,"Time_Cnt:");
	while(1)
	{
		OLED_ShowNum(2,10,Time_Cnt,5);
	}
}

七、与外部中断对比

|----------|----------|-----------|-------|
| 类型 | 触发方式 | 用途 | 优先级参考 |
| EXTI外部中断 | 引脚电平跳变触发 | 按键、红外遮挡检测 | 抢占1 |
| 定时器中断 | 固定时间自动触发 | 周期定时、定时控制 | 抢占2 |

本文完。

相关推荐
我命由我123451 小时前
72 法则极简理解
经验分享·笔记·学习·职场和发展·求职招聘·职场发展·学习方法
qq_263_tohua2 小时前
第112期 CNN学习,不错的文章
学习
我想我不够好。2 小时前
满级的惩戒在范围
学习
xian_wwq3 小时前
【学习笔记】OpenAI 兼容 API 服务化:从协议到 LLM Gateway(19/35)
笔记·学习
QK_003 小时前
阿波罗学习--day1
stm32·单片机
辞旧 lekkk4 小时前
【Qt系统相关】鼠标事件
linux·开发语言·qt·学习·计算机外设·萌新
czhaii4 小时前
单片机光学自动打靶机寻点流程分析
单片机·嵌入式硬件·硬件工程
是摆烂第一名呀4 小时前
【rk3506】U-Boot串口时钟未配置,导致串口彻底失效
linux·arm开发·驱动开发·嵌入式硬件
子非鱼94274 小时前
10-Flutter调用鸿蒙原生能力前置课:MethodChannel和PlatformView准备
学习·flutter·华为·harmonyos