定时器中断实验
一. 通用定时器的回顾
二. 常用寄存器和库函数配置
三. 编写定时器中断实验
1.1 内部时钟选择
内部时钟来源由AHB经过APB1的预分频产生时钟供定时器来用即产生内部时钟
除非APB1的分频系数是1,否则通用定时器的时钟等于APB1时钟的2倍。
默认调用SystemInit函数情况下:
SYSCLK = 72M
AHB时钟=72M
APB1时钟=36M
所以APB1的分频系数=AHB/APB1时钟=2
所以,通用定时器时钟CK_INT = 2*36M = 72M
1.2 计数器模式
向上计数模式(时钟分频因子=1)
首先使能定时器,由图看出自动重装载的值为36,当计数器寄存器读取到36时,会产生溢出事件,同时产生更新事件和更新中断标志。
2.1 定时器常用寄存器
1. 计数器当前值寄存器CNT
16位
2. 预分频寄存器TIMx_PSC
3. 自动重装载寄存器TIMx_ARR
4. 控制寄存器TIMx_CR1
主要用到位4和位0,位4控制方向;位0控制使能。
5. DMA中断使能寄存器TIMx_DIER
2.2 常用库函数
定时器参数初始化
void TIM_TimeBaseInit(TIM_TypeDef* TIMx,TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
typedef struct
{
uint16_t TIM_Prescaler;
uint16_t TIM_CounterMode;
uint16_t TIM_Period;
uint16_t TIM_ClockDivision;
uint8_t TIM_RepetitionCounter;
} TIM_TimeBaseInitTypeDef;
TIM_TimeBaseStructure.TIM_Period = 4999;
TIM_TimeBaseStructure.TIM_Prescaler =7199;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; T
IM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
·定时器使能函数:
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)
·定时器中断使能函数:
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
·状态标志位获取和清除
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);
2.3 定时器中断实现步骤
1. 使能定时器时钟
RCC_APB1PeriphClockCmd();
2. 初始化定时器,配置ARR,PSC
TIM_TimeBaseInit();
3. 开启定时器中断,配置NVIC
void TIM_ITConfig();
NVIC_Init();
4. 使能定时器
TIM_Cmd();
5. 编写中断服务函数
TIMx_IRQHandler();
3.1 编写
程序要求
通过定时器中断配置,每500ms中断一次,然后中断服务函数中控制LED实现LED1状态取反(闪烁)。
Tout(溢出时间)=(ARR+1)(PSC+1)/Tclk
编写timer.c
#include "timer.h"
#include "led.h"
//通用定时器中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器3!
void TIM3_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 计数到5000为500ms
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 10Khz的计数频率
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
TIM_ITConfig( //使能或者失能指定的TIM中断
TIM3, //TIM2
TIM_IT_Update ,
ENABLE //使能
);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIMx外设
}
void TIM3_IRQHandler(void) //TIM3中断
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx的中断待处理位:TIM 中断源
LED1=!LED1;
}
}
编写led.c
#include "led.h"
//初始化PB5和PE5为输出口.并使能这两个口的时钟
//LED IO初始化
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD, ENABLE); //使能PA,PD端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //LED0-->PA.8 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOA.8
GPIO_SetBits(GPIOA,GPIO_Pin_8); //PA.8 输出高
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //LED1-->PD.2 端口配置, 推挽输出
GPIO_Init(GPIOD, &GPIO_InitStructure); //推挽输出 ,IO口速度为50MHz
GPIO_SetBits(GPIOD,GPIO_Pin_2); //PD.2 输出高
}
编写main.c
#include "led.h"
#include "delay.h"
#include "sys.h"
#include "timer.h"
int main(void)
{
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
LED_Init(); //初始化与LED连接的硬件接口
TIM3_Int_Init(4999,7199);//10Khz的计数频率,计数到5000为500ms
while(1)
{
LED0=!LED0;
delay_ms(200);
}
}