微控制器
综合设计与实训实验名称:实验七定时器中断实验
实验七:定时器中断实验
1 实训任务
(1) 设置定时器时钟,自动重装载值,分频系数和计数方式;
(2) 设置定时器中断优先级;
(3) 通过编写延时函数实现定时器中断。
1.1 实验说明
STM32的通用定时器是由一个通过可编程预分频器(PSC)驱动的16位自动装载计数器(CNT)构成。
STM32的通用定时器的用途:测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM)等。
使用定时器预分频器和RCC时钟控制器预分频器,可以使脉冲长度和波形周期在几个微秒到几个毫秒间调整。
STM32F10x的通用TIMx(TIM2、TIM3、TIM4和TIM5)定时器功能包括:
(1)6位向上、向下、向上/向下自动装载计数器(TIMx_CNT)。
(2) 16位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数为1~65535之间的任意数值。
(3) 4个独立通道(TIMx_CH1~4),这些通道可以用来作为:
A.输入捕获
B.输出比较
C.PWM生成(边缘或中间对齐模式)
D.单脉冲模式输出
(4) 可使用外部信号(TIMx_ETR)控制定时器和定时器互连(可以用1个定时器控制另外一个定时器)的同步电路。
(5) 如下事件发生时产生中断/DMA:
A.更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
B.触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
C.输入捕获
D.输出比较
E.支持针对定位的增量(正交)编码器和霍尔传感器电路
F.触发输入作为外部时钟或者按周期的电流管理
定时器的时钟来源有4个:
(1)内部时钟(CK_INT)
(2) 外部时钟模式1:外部输入脚(TIx)
(3)外部时钟模式2:外部触发输入(ETR)
(4) 内部触发输入(ITRx):使用A定时器作为B定时器的预分频器(A为B 提供时钟)。
这些时钟,具体选择哪个可以通过TIMx_SMCR寄存器的相关位来设置。
这里的CK_INT时钟是从APB1倍频来的,除非APB1的时钟分频数设置为1,否则通用定时器TIMx的时钟是APB1时钟的2倍,当APB1的时钟不分频的时候,通用定时器TIMx的时钟就等于APB1的时钟。
这里还要注意的就是高级定时器的时钟不是来自APB1,而是来自APB2。
本实验使用定时器3产生溢出中断,在中断服务函数里面翻转LED上的电平,来指示定时器中断的产生。
定时器相关的库函数主要集中在固件库文件stm32f10x_tim.h和stm32f10x_tim.c文件中。
1.2 实验步骤
(1) 在实训平台上将PE4和PE5分别连接LED灯;
(2) 复制上一个实验工程修改名称并保存为定时器中断实验;
(3) 新建timer.c和timer.h文件,添加至工程中;
(4) 编写timer.h文件,声明定时器3初始化函数;
(5) 编写timer.c文件,编写定时器3初始化函数,设置分频系数、计数方式、自动重装载计数周期值和时钟分频因子;
(6) 编写main函数,程序编译正确;
2 程序设计
(1) 新建文件夹并命名为定时器中断实验,复制粘贴之前的实验文件,将工程文件改名为TIMER.uvprojx,同时添加timer.c和timer.h文件至工程中。
(2) 编写timer.h文件。
图8.2 timer.h文件
(3) 编写timer.c文件。
a. 让TIM3时钟使能
TIM3挂载在APB1下,通过APB1总线下的使能函数来使能。
图8.3 时钟使能
b. 初始化定时器参数, 设置自动重装值,分频系数,计数方式
库函数中定时器的初始化参数是通过初始化函数TIM_TimeBaseInit实现的,V oid TIM_TimeBaseInit(TIM_TypeDef*TIMx,
TIM_TimeBaseInitTypeDef*TIM_TimeBaseInitStruct
第一个参数是确定哪一个定时器,第二个参数是定时器初始化参数结构体指针
结构体类型为TIM_TimeBaseInitTypeDef,这个结构体的定义为:
图8.4 结构体定义
这个结构体一共有5个成员变量,对于通用定时器只有前面4个参数有用,
最后一个参数TIM_RepetitionCounter在高级定时器才会用到。
c. 设置TIM3_DIER允许更新中断
寄存器的相应位使能更新中断,在库函数里面定时器中断使能是通过TIM_ITConfig函数来实现的:
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_tTIM_IT, FunctionalState NewState);
第一个参数是选择定时器,取值为TIM1~TIM17。
第二个参数非常关键,是用来指明使能的定时器中断的类型,定时器中断的类型有很多种,包括更新中断TIM_IT_Update,触发中断TIM_IT_Trigger,以及输入捕获中断等等。
第三个参数是设定失能还是使能。
最终函数为TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE );
d.TIM3中断优先级设置
在定时器中断使能之后,因为要产生中断,必不可少的要设置NVIC相关寄存器,设置中断优先级。
图8.5 中断优先级设置
e.使能TIM3
配置完后要开启定时器,通过TIM3_CR1的CEN位来设置。
在固件库里面使能定时器的函数是通过TIM_Cmd函数来实现的:
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)
此时定时器3初始化函数为:
图8.6初始化函数
TIM3_Int_Init()函数就是上边介绍的几个步骤,该函数的2个参数用来设置TIM3的溢出时间。
系统初始化的时候在默认的系统初始化函数SystemInit函数里面已经初始化APB1的时钟为2分频,所以APB1的时钟为36M,而从STM32的内部时钟树图得知:当APB1的时钟分频数为1的时候,TIM2~7的时钟为APB1的时钟,而如果APB1的时钟分频数不为1,那么TIM2~7的时钟频率将为APB1时钟的两倍。
因此,TIM3的时钟为72M,再根据我们设计的arr和psc的值,就可以计算中断时间了。
计算公式如下:
Tout= ((arr+1)*(psc+1))/Tclk;
其中:
Tclk:TIM3的输入时钟频率(单位:Mhz)
Tout:TIM3的溢出时间(单位:us)
f. 编写中断服务函数
通过该函数来处理定时器产生的相关中断。
在中断产生后,通过状态寄存器的值来判断此次产生的中断属于什么类型。
然后执行相关的操作,这里使用的是溢出中断,所以在状态寄存器SR的最低位。
在处理完中断之后应该向TIM3_SR 的最低位写0,来清除该中断标志。
在固件库函数里面,用来读取中断状态寄存器的值判断中断类型的函数是:ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t)
该函数的作用是判断定时器TIMx的中断类型,TIM_IT是否发生中断。
固件库中清除中断标志位的函数是:
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT)
该函数的作用是清除定时器TIMx的中断TIM_IT标志位。
中断服务函数为:
图8.7 中断服务函数
在图8.6中断服务函数中,中断后的指令为控制LED的翻转。
每次中断后,判断TIM3的中断类型,如果中断类型正确(溢出中断),则执行LED的取反。
图8.8 主函数
(4) 编写main()函数
这里的代码和之前大同小异,此段代码对TIM3进行初始化后,进入死循环等待TIM3溢出中断,当TIM3_CNT的值等于TIM3_ARR的值的时候,就会产生TIM3的更新中断,然后在中断里面取反LED1,TIM3_CNT再从0开始计数。
根据上面的公式,我们可以算出中断溢出时间为500ms。
Tout=((4999+1)*(7199+1))/72=500000us=500ms
3硬件原理图设计
图8.1 LED电路原理图
4总结
实验仿真结果:
实验心得:通过本次实验,我掌握了用定时器中断控制LED灯的闪烁。
所有的中断都有固定的写法:先使用伪命令定义中断入口地址:#pragma vector=T1_VECTOR,然后定义函数名称__interrupt void Timer1_ISR(void)。
通过检测OVFIM(T1CTL 第4位)来判断是否发生了溢出中断,最后通过软件清除中断标志位。
提出的问题:溢出更新中断和通道捕获中断的区别?
解释:
溢出更新中断:计数器计数到你设定的值ARR ,溢出后,产生中断。
通道捕获中断:定期器捕获到你设定的边沿信号(上升沿/下降沿)时产生的中断。
以水龙头接水类比,更新中断相当于水桶水满的中断,捕获中断相当于水位监测报警,比如水半桶了要干嘛,满了要干嘛,水位监测设置最大和水桶水满一样。
捕获是监测电平变化。