知识回顾
通用定时器分为四个部分:
1,选择时钟
2,时基电路
3,输入捕获
4,输出比较
本节定时器中断主要涉及到定时器框图上边两个部分,即选择时钟和时基电路
定时器时钟选择
选择默认的内部时钟,配置时基电路(配置预分频系数,重装载值)
计数器时钟来源
1,内部时钟(CK_INT),默认
2,外部时钟模式1:外部输入脚(TIx)
3,外部时钟模式2:外部触发输入(ETR)
时钟选择配置寄存器TIMx_SMCR
TIMx_SMCR-SMS[2:0]:,默认000 关闭从模式,预分频器由内部时钟驱动
内部时钟选择:
AHB时钟经过APB1预分频
如果APB1预分频为1,定时器时钟由APB1*1输出
如果APB1预分频不为1,定时器时钟由APB1*2输出
除非APB1分频系数为1,否则通用定时器时钟等于APB1的2倍
根据系统时钟知识:
APB1时钟为AHB时钟(由AHB时钟分频系数为1得到)时,CK_INT = APB1时钟
APB1时钟为AHB时钟不为1分频得到时,CK_INT = APB1时钟 * 2
因为选中的是内部时钟(CK_INT),CK_PCS = CK_INT
CK_PSC % N(N为CK_PSC寄存器值+1得到),得到CK_CNT-定时器最终时钟
SYSCLK=72M
AHB时钟=72M
APB1时钟=36M
所以APB1的预分频系数=AHB/APB1=2
所以,通用定时器时钟CK_INT=2*36M=72M
定时器中断时序
向上计数为例,时钟分频因子为1(加一之后等于1)
APB1时钟=CK_INT
CNT_EN高电平-定时器使能
定时器时钟CK_CNT(CK_INT % N 得到)
计数器向上计数,到36(到重装载值,产生中断),重新计数
计数器溢出,跳变
更新事件产生,跳变
更新中断标志置1(需软件清零)
寄存器和库函数配置
1,计数器当前值寄存器CNT
16位寄存器,记录计数器当前值
对应框图部分:
2,预分频寄存器TIMx_PSC
3,自动重装载寄存器TIMx_ARR
16位寄存器,设置自动重装载值
4,控制寄存器1 TIMx_CR1
位4-计数方向配置位DIR:
0:向上计数
1:向下计数
位0-使能配置CEN:
0:禁止计数器
1:开启计数器
5,DMA中断使能寄存器
位0-更新中断使能UIE:
0:禁止更新中断
1:允许更新中断
定时器中断相关库函数
1,定时器参数初始化
// stm32f10x_tim.c中TIM_TimeBaseInit定时器初始化函数定义:
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
// TIM_TimeBaseInitTypeDef:
typedef struct
{
uint16_t TIM_Prescaler; // 预分频系数
uint16_t TIM_CounterMode; // 计数模式
uint16_t TIM_Period; // 自动装载值
uint16_t TIM_ClockDivision; // 输入捕获使用
uint8_t TIM_RepetitionCounter; // 高级定时器使用
} TIM_TimeBaseInitTypeDef;
// IS_TIM_COUNTER_MODE
#define IS_TIM_COUNTER_MODE(MODE) (((MODE) == TIM_CounterMode_Up) || \
((MODE) == TIM_CounterMode_Down) || \
((MODE) == TIM_CounterMode_CenterAligned1) || \
((MODE) == TIM_CounterMode_CenterAligned2) || \
((MODE) == TIM_CounterMode_CenterAligned3))
// IS_TIM_CKD_DIV
#define IS_TIM_CKD_DIV(DIV) (((DIV) == TIM_CKD_DIV1) || \
((DIV) == TIM_CKD_DIV2) || \
((DIV) == TIM_CKD_DIV4))
初始化配置对应框图:
2,定时器使能函数TIM_Cmd
作用 : 操作控制寄存器1 TIMx_CR1的位0,使能定时器
// stm32f10x_tim.c中TIM_Cmd定时器初始化函数定义:
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
3,定时器中断使能函数
作用 : 操作DMA中断使能寄存器TIMx_DIER使能相应的定时器中断
// stm32f10x_tim.c中TIM_Cmd定时器初始化函数定义:
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
4,状态标志位的获取和清除
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);
定时器中断实验的步骤
1,使能定时器时钟
RCC_APB1PeriphClockCmd();
2,初始化定时器,配置ARR(自动重装载寄存器),PSC(预分频系数)
TIM_TimeBaseInit
3,开启定时器中断,配置NVIC中断优先级
TIM_ITConfig()
NVIC_Init();(主函数要设置中断优先级分组)
4,使能定时器
TIM_Cmd()
5,编写中断服务函数:
TIMx_IRHandler();
实验内容
配置定时器中断,实现每500ms中断一次,中断服务函数控制LED1反转
主函数控制LED0每间隔300ms闪烁
实现500ms一次中断:
定时器从计数开始到触发中断所需事件由两个参数控制:
ARR:自动重装载值
PSC:定时器时钟
定时器时钟: APB1时钟经过倍频得到(当AHB1分频为1倍,其他为2倍)
定时器一个周期(计数器+1需要的时间)时间 = (PSC+1) / Tclk
一共需要多少个周期:ARR+1
溢出时间 Tout = (ARR + 1) (PSC + 1) / Tclk
当前实验使用系统初始化函数来初始化APB1,预分频系数为2,CK_INT=72M
考虑将PSC+1 = 7200 这样(PSC + 1) / Tclk = 7200/72000000 = 0.1s 便于计算,
所以PSC=7199
500ms中断间隔的实现
500 = ( ARR + 1 ) (PSC + 1) / 72000000
500 = ( ARR + 1 ) * 0.1
ARR=4999
所以设置ARR = 4999 , PSC=7199 实现定时器每500ms进入一次中断
项目初始化
新建文件
HARDWARE/TIMER/timer.c
HARDWARE/TIMER/timer.h
添加文件到项目目录,并将timer.h路径添加到配置path中
加入定时器相关库函数文件stm32f10x_tim.c
timer.c中include timer.h 然后编译
timer.h
#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"
// arr 自动重装载值 psc 定时器时钟
void TIM3_Int_Init(u16 arr,u16 psc);
#endif
timer.c
#include "timer.h"
#include "led.h"
void TIM3_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 1, 使能定时器3时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
// 2,定时器初始化配置
TIM_TimeBaseStructure.TIM_Period = arr; //自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //预分频系数
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 与此实验关系不大,随意设置一个
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// 定时器中断使能
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //更新中断使能
//NVIC中断优先级初始化设置 - 需要先在主函数中配置中断优先级分组
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM3, ENABLE); //使能定时器
}
//中断服务函数
void TIM3_IRQHandler(void)
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //更新中断发生
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //置位更新状态-清除标志位
LED1=!LED1; // 反转LED1
}
}
主函数main.c
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "timer.h"
int main(void)
{
delay_init(); //延时函数初始化
LED_Init(); //LED初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组配置为2
TIM3_Int_Init(4999,7199);// 初始化TIM3定时器
while(1)
{
LED0=!LED0;
delay_ms(200); //每200msLED0反转
}
}
实验结果
实验实现每间隔500msLED1闪烁,每200msLED0闪烁