通用定时器中断实验详解

目录

通用定时器中断实验

定时器中断时钟源解析

教你如何看逻辑信号图

向上计数模式(时钟分频因子=1)

中央对齐计数模式(时钟分频因子=1,ARR=6)

相关寄存器简介

事件产生寄存器(TIMx_EGR)

状态寄存器(TIMx_SR)

计数器(TIMx_CNT)

预分频器(TIMx_PSC)

自动重装载寄存器(TIMx_ARR)

控制寄存器 1(TIMx_CR1)

用库函数配置带有中断的通用定时器

库函数介绍

定时器参数的选取

设计要求

TIMX程序设计流程

第一步:初始化TIMER3

第二步:初始化定时器3(配置ARR的值与PSC的值)

第三步:初始化NVIC嵌入式中断向量

第四步:配置TIMX的中断

第五步:使能TIMX外设

第六步:编写相应的中断服务函数

代码示例

Main.c

Led.c

Led.h

Timer.h

Timer.c

运行结果


通用定时器中断实验

定时器中断时钟源解析

 

我们这个实验使用来自APB1总线的时钟(AHB时钟频率是72MHz),我们注意到AHB->APB1分频器->APB1中如果APB1预分频器的分频系数是1那么TIMXCLK的时钟频率为“TIMCLK=APB1=AHB”,如果APB1预分频器的分频系数为N(N不为1),那么TIMXCLK的时钟频率为“TIMCLK=2xAPB1=2xAHB/N”。

但是当我们调用ST公司提供的初始化时钟源库函数时,APB1分频器默认的分频系数是2,因此最终TIMXCLK=2xAPB1=2xAHB/2=AHB。

 

TIMXCLK时钟频率=CLK_INT时钟频率,然后通过触发控制器传递给CK_PSC进行第二次分频处理,最终CNT计数器接收到的时钟脉冲为“CLK_INT/(CK_PSC+1)”,(分频系数为何为CK_PSC+1我们稍后再论)

教你如何看逻辑信号图

向上计数模式(时钟分频因子=1)

 

 

首先,CK_INT为TIMER的初始时钟源,CNT_EN代表着TIMER使能,CK_CNT是计数器接受的脉冲频率(在CK_PSC寄存器中进行分频后的),计数寄存器的重装载值为36,“中断事件更新”与“计时器溢出”还有“更新中断标志置1”同步进行,但是我们看到如果我们不手动清除中断标志,那么系统会一直处于执行中断当中无论中断条件是否满足。

中央对齐计数模式(时钟分频因子=1,ARR=6)

 

 

中央对齐计数模式有些不同的是,分别在向上计数溢出与向下计数溢出时均触发中断,而且如果我们不在每次中断后清除中断标志位,那么无论是否满足中断条件中断行为都会一直存在。

相关寄存器简介

事件产生寄存器(TIMx_EGR)

 

更新事件中断是通用定时器执行的普通中断。

状态寄存器(TIMx_SR)

 

计数器(TIMx_CNT)

 

预分频器(TIMx_PSC)

 

自动重装载寄存器(TIMx_ARR)

 

该寄存器用于在计数器的计数值溢出后重新加载计数器的溢出值。

控制寄存器 1(TIMx_CR1)

 

该寄存器用于请求中断和配置计数器模式,一旦中断触发,所有寄存器都会更新。

用库函数配置带有中断的通用定时器

库函数介绍

库函数名称

功能

TIM_TimeBaseInit

用于初始化TIMX定时器

TIM_Cmd

使能或者失能 TIMx 外设

TIM _ITConfig

在配置好NVIC相应参数的情况下,使能或者失能指定的 TIM 中断

TIM_PrescalerConfig

设置 TIMx 预分频

TIM_CounterModeConfig

设置 TIMx 计数器模式

TIM_GetITStatus

检查指定的 TIM 中断发生与否

TIM_ClearITPendingBit

清除 TIMx 的中断待处理位

TIM_GetFlagStatus

检查指定的 TIM 标志位设置与否

TIM_ClearFlag

清除 TIMx 的待处理标志位

TIM_ClearITPendingBit与TIM_ClearFlag函数的区别

TIM_ClearITPendingBit 清除的是一些中断标志位,TIM_ClearFlag清除的是定时器的状态标志,比如定时器捕获状态位定时器触发标志位。

定时器参数的选取

 

通用定时器参数主要有两个PSC预分频系数与ARR计数器重装载值。溢出时间指的是“从0x00到TOP的时间”。

 

设计要求

通过定时器中断配置,每500ms中断一次,然后中断服务函数中控制LED实现LED1状态取反(闪烁)。

TIMX程序设计流程

第一步:初始化TIMER3

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // 使能APB1的外设时钟  

第二步:初始化定时器3(配置ARR的值与PSC的值)

TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;  
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;  
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Down;  
TIM_TimeBaseInitStructure.TIM_Period = 0x1C20;  // 5000
TIM_TimeBaseInitStructure.TIM_Prescaler = 0x1388;  // 7200
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); // 初始化TIMER3  

第三步:初始化NVIC嵌入式中断向量

NVIC_InitTypeDef NVIC_InitStructure;  
  
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;  
NVIC_Init(&NVIC_InitStructure); // 初始化NVIC嵌入式外部中断  

为什么我们先于NVIC初始化配置NVIC中断向量分组呢?

NVIC中断向量分组是用于约束全体中断向量的,如果我们单独把它放到某一个头文件里,就不能表示这个含义,而且如果我们在不同头文件里声明不同的中断向量分组,那么会乱套的。

第四步:配置TIMX的中断

TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); // 配置TIMER3的具体中断行为  

为什么NVIC的TIM中断通道已经开启,我们还配置TIMX的中断干啥?

我们一开始所使能的是TIMX中断通道,TIMX中断的模式很复杂,有多种中断模式,因此我们先使能TIMX的总中断通道,在进一步配置TIMX中断的属性,例如不同的中断条件:向上计数溢出,向下计数溢出……。

第五步:使能TIMX外设

TIM_Cmd(TIM3, ENABLE); // 使能TIMER3外设  

第六步:编写相应的中断服务函数

void TIM3_IRQHandler() // 应该在相应的中断文件内编写中断服务函数  
{  
    if(TIM_GetITStatus(TIM3, TIM_IT_Update) == SET) // 由于定时器有多个中断模式,因此我们一定要检查相应的中断标志位  
    {  
        LED0 = !LED0;  
    }  
    TIM_ClearITPendingBit(TIM3, TIM_IT_Update); // 由于定时器有多个中断模式,因此我们一定要清除相应的中断标志位   
}  

代码示例

Main.c

#include "led.h"  
#include "timer.h"  
#include "stm32f10x.h"  
#include "delay.h"  
  
int main()  
{  
    delay_init(); // 只有初始化系统systick时钟,我们才能调用delay系列函数  
    LED_InitConfig(); // LED初始化  
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // NVIC中断向量分组应该先于NVIC初始化  
    TIMER_InitConfig(0x1C20, 0x1388); // 初始化TIMER3,其中PR=7200,ARR=5000  
      
    while(1)  
    {  
        LED1 = !LED1;  
        delay_ms(250);  
    }  
}  

Led.c

#include "led.h"  
#include "stm32f10x.h"  
  
void LED_InitConfig()  
{  
    GPIO_InitTypeDef GPIO_InitStructure;  
      
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE); // 使能LED0,LED1的时钟  
      
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;  
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
    GPIO_Init(GPIOB, &GPIO_InitStructure); // 配置LED0的GPIO输出属性  
      
    GPIO_ResetBits(GPIOB, GPIO_Pin_5); // 初始化PB5为低电平  
      
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;  
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
    GPIO_Init(GPIOE, &GPIO_InitStructure); // 配置LED1的GPIO输出属性  
      
    GPIO_ResetBits(GPIOE, GPIO_Pin_5); // 初始化PE5为低电平  
}  

Led.h

#ifndef _LED_H  
#define _LED_H  
  
#include "sys.h"  
  
void LED_InitConfig();  
  
#define LED0 PBout(5)  
#define LED1 PEout(5)  
  
#endif  

Timer.h

#ifndef _TIMER_H  
#define _TIMER_H  
  
#include "sys.h"  
  
void TIMER_InitConfig(u16 ARR, u16 PR);  
  
#endif  

Timer.c

运行结果

 

猜你喜欢

转载自blog.csdn.net/weixin_45590473/article/details/108048325