您的位置:首页 > 其它

定时器中断实验

2016-10-18 17:08 295 查看

知识回顾

通用定时器原理

通用定时器分为四个部分:

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闪烁
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: