STM32F103五分钟入门系列定时器中断

Posted 自信且爱笑‘

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32F103五分钟入门系列定时器中断相关的知识,希望对你有一定的参考价值。

学习板:STM32F103ZET6

参考:

51单片机(四)定时器中断(+数码管—24小时制钟表)

STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯

STM32F103五分钟入门系列(十)NVIC中断优先级管理

前言

前面用SysTick滴答定时器实现中断,本博总结一下用32的几个定时器去实现中断。中断这块内容在32中占据很大篇幅,好多东西就算学到了以后也很少用到,所以本博删繁就简,在内容上总结的不会很全面,不过在之后的各类实验总结中,用到定时器的某个功能,会单独拎出来总结一遍,毕竟定时器这块不是一两篇博客能总结清楚的,只能一点一点的学习和掌握。

一、STM32F103定时器类型和功能

1、类型

不同型号的32单片机有不同数量的定时器,具体板子有几个定时器,就去对应芯片的参考手册中察看(在定时器章节都有说明)

或者去《选型手册》中察看:

其类型:

类型位数模式DMA请求捕获/比较通道互补输出特殊应用场景
高级定时器 (TIM1、TIM2)16向上、向下、向上和向下可以4带死区控制盒紧急刹车,PWM点击控制
通用定时器(TIM2~TIM5)16向上、向下、向上和向下可以4定时、计数、PWM输出、输入捕获、输出比较
基本定时器(TIM6、TIM7)16向上、向下、向上和向下可以0DAC

2、高级定时器功能(TIM1、TIM2)

1、16位向上、向下、向上/下自动装载计数器
2、16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65535之间的任意
数值
3、 多达4个独立通道:
─ 输入捕获
─ 输出比较
─ PWM生成(边缘或中间对齐模式)
─ 单脉冲模式输出
4、 死区时间可编程的互补输出
5、 使用外部信号控制定时器和定时器互联的同步电路
6、允许在指定数目的计数器周期之后更新定时器寄存器的重复计数器
7、刹车输入信号可以将定时器输出信号置于复位状态或者一个已知状态
8、 如下事件发生时产生中断/DMA:
─ 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
─ 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
─ 输入捕获
─ 输出比较
─ 刹车信号输入
9、 支持针对定位的增量(正交)编码器和霍尔传感器电路
10、触发输入作为外部时钟或者按周期的电流管理

3、通用定时器功能(TIM2~TIM5)

1、16位向上、向下、向上/向下自动装载计数器
2、 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65536之间的任意
数值
3、4个独立通道:
─ 输入捕获
─ 输出比较
─ PWM生成(边缘或中间对齐模式)
─ 单脉冲模式输出
4、 使用外部信号控制定时器和定时器互连的同步电路
5、 如下事件发生时产生中断/DMA:
─ 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
─ 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
─ 输入捕获
─ 输出比较
6、 支持针对定位的增量(正交)编码器和霍尔传感器电路
7、 触发输入作为外部时钟或者按周期的电流管理

4、基本定时器(TIM6、TIM7)

1、16位自动重装载累加计数器
2、16位可编程(可实时修改)预分频器,用于对输入的时钟按系数为1~65536之间的任意数值
分频
3、 触发DAC的同步电路
4、 在更新事件(计数器溢出)时产生中断/DMA请求

5、定时器框图

1、三个定时器框图

高级定时器框图:

通用定时器框图:

基本定时器框图:

可以看到高级定时器框图和通用定时器框图基本一样,而基本定时器框图只保留了定时,所以三个框图以通用定时器为例,做一个简单总结。

2、通用定时器框图详述

通用定时器框图分为四部分,下图所示:

(1)定时器时钟来源(重要

计数器时钟可由下列时钟源提供:
①内部时钟(CK_INT)
② 外部时钟模式1:外部输入脚(TIx)
③ 外部时钟模式2:外部触发输入(ETR)
④ 内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。

1)由内部时钟(CK_INT) 提供

之前博客STM32F103五分钟入门系列(六)时钟框图+相关寄存器总结+系统时钟来源代码(寄存器)STM32F103五分钟入门系列(七)SystemInit()函数、SetSysClock()函数
总结过时钟框图,再来看一下定时器相关的时钟:

从上图得到以下信息:
①定时器2~7挂载在APB1总线上,定时器1、8挂载在APB2总线上
从底层看到:

②默认状态下,APB1预分频系数为2,由下图红色箭头所示,则到达定时器2~7的时钟应该在APB1的基础上×2,即36MHZ×2=72MHZ

PAB2预分频系数为1(图中蓝色标注覆盖绿色标注为默认状态下(72MHZ系统时钟时)各类时钟状态,之前总结过),所以默认状态下:

定时器2~7时钟=SYSCLK=72MHZ
定时器1、8时钟=SYSCLK=72MHZ

③当APB1预分频系数不为1,定时器2~7时钟为AHB输出时钟*2;当APB2预分频系数不为1,定时器1、8时钟为AHB输出时钟 *2;

2)由外部输入脚(TIx) 提供

通过定义引脚,捕获到PWM输入作为定时器时钟,输入捕获后边会有专门的一篇博客来总结。

3)由外部触发输入(ETR) 提供

在《STM32F103ZET6》中搜索引脚,可以得到对应外部触发输入的引脚。

4)由内部触发输入(ITRx)提供

由其他定时器输出的信号作为本定时器的时钟源。

(2)定时器工作

从上图可以看到,时钟(前面所讲的四种时钟来源,默认为内部时钟)经过PSC预分频后作为定时器时钟,重装载值后,就可以进行计数。所以配置定时器时,除了模式设置外,还得设置定时器时钟(即预分频)和重装载值

定时器计数有三种模式:向上、向下、向上和向下

向上:从零开始计数,到重装载值后,从零开始重新计数,并产生一个溢出事件

向下:从重装载开始向下计数到0,再从重装载值开始重新计数并产生一个溢出事件。

向上和向下:计数器从0开始计数到重装载值-1,产生一个计数器溢出事件,在向下计数到1并且产生一个计数器溢出事件,再从0开始向上计数…(模式1、2、3)

二、定时器常用库函数

1、定时器初始化函数 TIM_TimeBaseInit()

主要看一下参数:

(1)第一个参数TIMx


表示哪个定时器

(2)第一个参数TIM_TimeBaseInitStruct

为一个结构体,打开看看:

有五个成员:

①TIM_Prescaler:表示预分频系数,为16位数,即可以是1~(2^16)-1。即下图红色箭头所示:

②TIM_CounterMode
表示计数的几种模式

TIM_CounterMode_Up
表示向上计数到重装载值后,从0开始重新计数,并发生溢出事件

TIM_CounterMode_Down
表示向下计数到0值后,从重装载开始重新计数,并发生溢出事件

TIM_CounterMode_CenterAligned1
向上和向下计数(中央对齐模式),只在计数器向下计数时才发生溢出事件。即计数器从0开始执行到重装载值-1后,开始减到0,同时发生溢出事件;计数到0后,再开始增到重装载值-1,但此时不发生溢出事件。

TIM_CounterMode_CenterAligned2
向上和向下计数(中央对齐模式),只在计数器向上计数时才发生溢出事件。即计数器从0开始执行到重装载值-1后,开始减到0,此时不发生溢出事件;计数到0后,再开始增到重装载值-1,同时发生溢出事件。

TIM_CounterMode_CenterAligned3
向上和向下计数(中央对齐模式),计数器向上和向下计数时都发生溢出事件。即计数器从0开始执行到重装载值-1后,开始减到0,同时发生溢出事件;计数到0后,再开始增到重装载值-1,同时发生溢出事件。

③TIM_Period

这个是设置重装载值的。

例:

默认状态下,内部时钟即AHB输出的时钟为72MHZ(外接晶振不同,情况不同),如果将预分频系数设置为7200,则定时器时钟周期为72000000/7200=10000HZ=10kHZ

则定时器每计数一次的时间为1/10k=0.1ms

如果想定时1s,则让计数器计数:1s/0.1ms=10000,即重装载值为9999(从0~9999再回到0

设置TIM_Prescaler=7200,TIM_Period=9999

当然也可以是其它的数,根据这个思路设置即可。

④TIM_ClockDivision
这个参数是在输入捕获中应用,如下图红色箭头所示。后边会有一篇博客单独总结输入捕获。

⑤TIM_RepetitionCounter
这个参数是在高级定时器TIM1、TIM8中应用
当中断服务函数中代码太多,当下次中断到来之时,本次中断服务函数还没有执行完,此时该参数就派上用场了。
当设置TIM_RepetitionCounter=1时,原本的一个计数周期发生一次中断,变为2个计数周期发生一次中断。
当设置TIM_RepetitionCounter=2时,原本的一个计数周期发生一次中断,变为3个计数周期发生一次中断。

当设置TIM_RepetitionCounter=9时,原本的一个计数周期发生一次中断,变为10个计数周期发生一次中断。

代码实现:(如上面的例子,1s一次中断,采用定时器2、向上计数)

	 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	 TIM_TimeBaseInitStructure.TIM_Prescaler=7200;
	 TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//采用向上计数
	 TIM_TimeBaseInitStructure.TIM_Period=9999;
	 //TIM_TimeBaseInitStructure.TIM_ClockDivision=		//输入捕获
	// TIM_TimeBaseInitStructure.TIM_RepetitionCounter=	//高级定时器
	 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	 

2、定时器使能函数TIM_Cmd()

这个函数比较简单,第一个参数是选择哪个定时器,第二个参数是ENABLE或DISABLE

如使能定时器2:

	TIM_Cmd(TIM2,ENABLE);

3、定时器中断使能函数TIM_ITConfig()

该函数使能定时器中断,也比较简单。

第一个参数是选择定时器,TIM1~TIM17

第二个参数是选择类型
更新中断TIM_IT_Update
触发中断TIM_IT_Trigger
输入捕获中断TIM_IT_CC1~TIM_IT_CC4

第三个参数是ENABLE或DISABLE

如使能定时器2更新中断:

	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);

4、状态标志位获取函数TIM_GetFlagStatus()和TIM_GetITStatus()

(1)TIM_GetFlagStatus()

参数
第一个参数选择哪个定时器
第二个参数:

返回

返回0或非0

(2)TIM_GetITStatus()


参数

第一个参数选择哪个定时器
第二个参数

返回

返回0或非0

(3)两函数的不同点

参数上TIM_GetFlagStatus()函数多了下图所示内容

这是输入捕获和输出比较这块用到的东西,是判断是否重复捕获。

判断上TIM_GetITStatus()多了对DIER寄存器的判断

对DIER寄存器判断,即判断是否被使能。例如是否使能更新中断、是否允许捕获/比较中断1~4等等。

所以TIM_GetITStatus()函数更严谨,以后尽量用这个函数;当然如果获取捕获/比较重复标记1~4通道,只能用TIM_GetFlagStatus()函数,毕竟只有它支持这项功能。

5、状态标志位清除函数TIM_ClearFlag()和TIM_ClearITPendingBit()

(1)TIM_ClearFlag()

参数和TIM_GetFlagStatus()函数参数一样

(2)TIM_ClearITPendingBit()

参数和TIM_GetITStatus()函数一样

(3)两函数使用的注意点

两函数都是对SR寄存器的操作,使对应位置0。需要注意的是清除捕获/比较1~4重复捕获标记时,一定要使用TIM_ClearFlag()函数

三、定时器编程的代码顺序

例:设置定时器2为更新事件中断,每1s中断一次,预分频系数可以是7200,重装载着可以是9999.

1、使能定时器时钟RCC_APB1PeriphClockCmd()

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

2、初始化定时器TIM_TimeBaseInit()

	 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	 TIM_TimeBaseInitStructure.TIM_Prescaler=7200;
	 TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//采用向上计数
	 TIM_TimeBaseInitStructure.TIM_Period=9999;
	 //TIM_TimeBaseInitStructure.TIM_ClockDivision=		//输入捕获
	// TIM_TimeBaseInitStructure.TIM_RepetitionCounter=	//高级定时器
	 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);

3、开启定时器中断

	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);

4、设置NVIC中断优先级分组NVIC_PriorityGroupConfig()

 
	 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

5、初始化NVIC NVIC_Init()

	 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);

6、使能定时器TIM_Cmd()

	TIM_Cmd(TIM2,ENABLE);

7、编写中断服务函数(startup_stm32f10x_hd.s文件定义)

	 void TIM2_IRQHandler()
	 {
	 }

四、例1-利用定时器中断写一个LED闪烁实验

默认状态下,定时器内部时钟为72MHZ,若这里采用7200预分频系数(最大65535),则计数器时钟为:72MHZ/7200=10KHZ

即每计一次数的时间为0.1ms,若计数时间为x,则计数次数为x/0.1(因为这个值减1要作为重装载值的,重装载寄存器为16位寄存器。所以x/0.1<=65536,即x<=6553.6,最多可计数时间6.5s,当然可以调高预分频系数,增长最大计数时间)

还可以利用STM32F103五分钟入门系列(九)延时函数(自己重写的底层)(上限:477218ms和477218588us)中的编程思想,这个计数时间上限会提高到很高。

综上,在定时器初始化时,TIM_Prescaler=7200,延时时间传递进来ms,然后给TIM_Period赋值:TIM_Period=10*ms-1

完整代码:

Tim_led.h
#ifndef _TIM2_DELAY_
#define _TIM2_DELAY_

#include "stm32f10x.h"

void TIM2_LED(u16 time);
#endif
//Tim_led.c
#include "Tim2_led.h"
#include "stm32f10x.h"
#include "led.h"
#include "sys.h"

void TIM2_LED(u16 ms)
{
	TIM_TimeBaseInitTypeDef	TIM_TimeBaseInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period=10*ms-1;
	TIM_TimeBaseInitStructure.TIM_Prescaler=7200;
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
	
	 
	 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()
 {
	 PEout(5)= ~PEout(5);
	 PBout(5)=!PBout(5);
	 	
	TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
 }
#include "stm32f10x.h"
#include "sys.h"
#include "led.h"
#include "Tim2_led.h"
 int main(void)
 {	
	 
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	LED_Init();
	
	 TIM2_LED(2000);
	while(1)
	{

	}
 }

以上是关于STM32F103五分钟入门系列定时器中断的主要内容,如果未能解决你的问题,请参考以下文章

STM32F103五分钟入门系列(十五)输入捕获(精雕细琢-.-)

STM32F103五分钟入门系列外部中断大汇总

STM32F103五分钟入门系列(十五)输出比较(PWM输出)+各类测试

STM32F103五分钟入门系列NVIC中断优先级管理

STM32F103五分钟入门系列(十六)输入捕获(精雕细琢-.-)

STM32F103五分钟入门系列时钟框图+相关寄存器总结+系统时钟来源代码(寄存器)