TIM定时器中断
简介
TIM(Timer)定时器
定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时(72M/65536/65536=59.65)
不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能
根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型
stm32的定时器可以从宇宙诞生计数到宇宙毁灭哦!!!
定时器类型
STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4
定时中断基本结构
预分频器时序
计数器计数频率:CK_CNT = CK_PSC / (PSC + 1)
计数器时序
计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1 = CK_PSC / (PSC + 1) / (ARR + 1)
计数周期,即ARR的值,预分频器,即PSC的值。
计数器无预装时序
计数器有预装时序
RCC时钟树
定时器定时中断
定时器函数
#include "stm32f10x.h" // Device header
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_TimeBaseInit函数末尾,手动产生了更新事件
//若不清除此标志位,则开启中断后,会立刻进入一次中断
//如果不介意此问题,则不清除此标志位也可
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE );//开启TIM2的更新中断
/*NVIC中断分组*/
NVIC_PriorityGroupConfig (NVIC_PriorityGroup_2 );//配置NVIC为分组2
//即抢占优先级范围:0~3,响应优先级范围:0~3
//此分组配置在整个工程中仅需调用一次
//若有多个中断,可以把此代码放在main函数内,while循环之前
//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
/*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,定时器开始运行
}
/*void TIM2_IRQHandler (void)
{
if(TIM_GetITStatus (TIM2 ,TIM_IT_Update)==SET)
{
}
TIM_ClearITPendingBit (TIM2 ,TIM_IT_Update );
}*/
!!!!!!!!!!!!!!!!打100000个感叹号!!!!!!!!!!!!!!!!!!
定时器为单片机很重要很重要的一个工具,需要好好学习
注意点:
1.配置定时器需要时基单元初始化,如果是内部时钟的话可以不配。
2.配置定时器的步骤还是老样子:定义结构体变量——把所有结构体内容写出来——结构体变量交给xxx。
3.定时器相比于其他多了几个内容:开启更新中断和使定时器开始运行。
4.判断是否是TIM2的更新事件触发的中断需要单独写。可以再写定时器的模板的时候写在最底下然后注释掉,使用的时候复制到main函数中使用,否则将会用到extern。
主函数
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "LED.h"
#include "key.h"
#include "OLED.h"
#include "Timer.h"
uint16_t Num;
int main(void)
{
OLED_Init();
OLED_ShowString(1, 1, "Num:"); //1行1列显示字符串Num:
timer_init() ;
while(1)
{
OLED_ShowNum (1,5,Num,5 );
}
}
void TIM2_IRQHandler (void)
{
if(TIM_GetITStatus (TIM2 ,TIM_IT_Update)==SET)//判断是否是TIM2的更新事件触发的中断
{
Num++;//Num变量自增,用于测试定时中断
TIM_ClearITPendingBit (TIM2 ,TIM_IT_Update );//清除TIM2更新事件的中断标志位
//中断标志位必须清除
//否则中断将连续不断地触发,导致主程序卡死
}
}
定时器外部中断
定时器函数
#include "stm32f10x.h" // Device header
void timer_init(void)
{
/*开启时钟*/
RCC_APB1PeriphClockCmd (RCC_APB1Periph_TIM2 ,ENABLE );//开启TIM2的时钟
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA ,ENABLE );
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_ETRClockMode2Config (TIM2,TIM_ExtTRGPSC_OFF ,TIM_ExtTRGPolarity_NonInverted,0x0F);
//选择外部时钟模式2,时钟从TIM_ETR引脚输入
//注意TIM2的ETR引脚固定为PA0,无法随意更改
//最后一个滤波器参数加到最大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_TimeBaseInit函数末尾,手动产生了更新事件
//若不清除此标志位,则开启中断后,会立刻进入一次中断
//如果不介意此问题,则不清除此标志位也可
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE );//开启TIM2的更新中断
/*NVIC中断分组*/
NVIC_PriorityGroupConfig (NVIC_PriorityGroup_2 );//配置NVIC为分组2
//即抢占优先级范围:0~3,响应优先级范围:0~3
//此分组配置在整个工程中仅需调用一次
//若有多个中断,可以把此代码放在main函数内,while循环之前
//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
/*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,定时器开始运行
}
uint16_t timer_gercounter(void)
{
return TIM_GetCounter (TIM2 );
}
/*void TIM2_IRQHandler (void)
{
if(TIM_GetITStatus (TIM2 ,TIM_IT_Update)==SET)
{
}
TIM_ClearITPendingBit (TIM2 ,TIM_IT_Update );
}*/
注意!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1.外部中断和定时器中断并没有多大的区别,只是多用了gpio
2.外部中断只能使用PA0接口
3.选择外部时钟模式2,时钟只能从TIM_ETR引脚输入
4.需要加一个滤波来消除抖动
主函数
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "LED.h"
#include "key.h"
#include "OLED.h"
#include "Timer.h"
uint16_t Num;
int main(void)
{
OLED_Init();
OLED_ShowString(1, 1, "Num:"); //1行1列显示字符串Num:
OLED_ShowString(2, 1, "CNT:");
timer_init() ;
while(1)
{
OLED_ShowNum (1,5,Num,5 );
OLED_ShowNum (2,5,timer_gercounter (),5 );
}
}
void TIM2_IRQHandler (void)
{
if(TIM_GetITStatus (TIM2 ,TIM_IT_Update)==SET)//判断是否是TIM2的更新事件触发的中断
{
Num++;//Num变量自增,用于测试定时中断
TIM_ClearITPendingBit (TIM2 ,TIM_IT_Update );//清除TIM2更新事件的中断标志位
//中断标志位必须清除
//否则中断将连续不断地触发,导致主程序卡死
}
}
总结
1.定时器再stm32的学习中尤其重要,需要好好的揣摩
2.定时器的功能可以实现很多有意思的想法,以后有时间可以试一下