本届实验是基于江科协的定时器中断配置实验改编而来。中断定时器与外部中断的最大的区别在于定时器中断是可以在设定的时间内进行一次中断而外部中断是需要去触发一个外部中断触发条件如对应的EXTI0需要低电平触发,那么要触发它就需要将它对应的引脚置于低电平才能触发。
具体如下图所示:
特别提醒:
只有在定时器用于基础的计时的功能单纯到时间去执行中断事件的时候才不需要使能引脚,而如果需要该定时器中断具有输出比较与输入捕获的功能的话,才需要使能对应的定时器通道引脚。
一、配置步骤:
(1)定时器的初始化函数
(这里以TIM2为例)
备注:STM32F103系列的默认的系统时钟为72MHZ,挂载在APB1上的TIM2的定时器时钟源的默认频率为72MHZ
开启时钟;
配置时钟源;
时基单元初始化;
从上往下的参数依次是
系统时钟分频、计数模式、PSC预分频系数的值、ARR自动重装载值
中断输出配置;
开启定时器的更新中断;
中断优先级NVIC分组NVIC配置;;
定时器使能;
(2)在主函数里面编写定时器中断服务函数:
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //判断是否是TIM2的更新事件触发的中断
{
//事件//
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除TIM2更新事件的中断标志位
//中断标志位必须清除
//否则中断将连续不断地触发,导致主程序卡死
}
}
二、示例:
Timer.c
#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,定时器开始运行
}
Timer.h
#ifndef __TIMER_H
#define __TIMER_H
void Timer_Init(void);
#endif
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
uint16_t Num; //定义在定时器中断里自增的变量
int main(void)
{
/*模块初始化*/
OLED_Init(); //OLED初始化
Timer_Init(); //定时中断初始化
/*显示静态字符串*/
OLED_ShowString(1, 1, "Num:"); //1行1列显示字符串Num:
while (1)
{
OLED_ShowNum(1, 5, Num, 5); //不断刷新显示Num变量
}
}
/**
* 函 数:TIM2中断函数
* 参 数:无
* 返 回 值:无
* 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
* 函数名为预留的指定名称,可以从启动文件复制
* 请确保函数名正确,不能有任何差异,否则中断函数将不能进入
*/
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //判断是否是TIM2的更新事件触发的中断
{
Num ++; //Num变量自增,用于测试定时中断
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除TIM2更新事件的中断标志位
//中断标志位必须清除
//否则中断将连续不断地触发,导致主程序卡死
}
}
解决: 复位后从1开始计数
原因是,中断函数在初始化之后立刻就执行了,原因就出在定时器配置函数
跳转到定义到最后可以看到
此处含义为: 生成一个“更新事件”以重新加载 Prescaler 和 Repetition 计数器值,立刻。说明从这里开始就进入了一次中断(原因:预分频器是有一个缓冲寄存器的,写值件只有在更新事件时,才会起作用,为了让值立刻起作用,就在这里用上述的语句实现手动生成一个更新事件,这样预分频器的值就有效了,但同时,它的副作用就是,更新事件和更新中断是同时发生的,更新中断会置更新中断标志位,当初始化完后,更新中断就会立刻进入),所以在复位后开始计数的变量num的值是从1开始的。
解决办法为:在开启定时器前初始化之后加上下面这段就可以了