Stm32 第三十二讲:定时器中断


时钟选择

计数器时钟可以由以下时钟源提供:
①. 内部时钟(CK_INT),在TIMx_SMCR寄存器中默认就是内部时钟
②. 外部时钟模式1:外部输入脚(TIx)
③. 外部时钟模式2:外部触发输入(ETR)
④. 内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器Timer1来作为另一个定时器Timer2的预分频器。

内部时钟选择

时钟计算方法

定时器中断_时钟计算方法

除非APB1的分频系数是1,否则通用定时器的时钟等于APB1时钟的2倍

默认调用SystemInit函数情况下:
SYSCLK=72M
AHB时钟=72M
APB1时钟=36M
所以APB1的分频系数是2,所以通用定时器时钟CK_INT=2*36M=72M

计数器模式

通用定时器可以向上计数、向下计数、向上向下双向计数模式。

向上计数模式

计数器从0计数到自动加载值(TIMx_ARR),然后重新从0开始计数并且产生一个计数器溢出事件。
当时钟分频=1时(CK_PSC=CK_CNT):

定时器中断_向上计数时序图

向下计数模式

计数器从自动装入的值(TIMx_ARR)开始向下计数到0,然后从自动装入的值重新开始,并产生一个计数器向下溢出事件。
当时钟分频=1时(CK_PSC=CK_CNT):

定时器中断_向下计数时序图

中央对齐模式(向上/向下计数)

计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0重新计数。
当时钟分频=1(CK_PSC=CK_CNT),ARR=6时:

定时器中断_中央计数时序图

定时器中断实验相关寄存器

计数器当前值寄存器 CNT

定时器中断_CNT

预分频寄存器 TIMx_PSC

定时器中断_PSC

自动重装载寄存器 TIMx_ARR

该寄存器在物理上实际对应着2个寄存器,一个是程序员可以直接操作的,另外一个是程序员看不到的,这个看不到的寄存器被称为影子寄存器。事实上真正起作用的是影子寄存器。根据TIMx_CR1寄存器中APRE位的位置:ARPE=1时,预装载寄存器的内容可以随时传送到影子寄存器。此时两者是连通的;而ARPE=0时,在每一次更新事件(UEV)时,才把预装在寄存器的内容传送到影子寄存器。

定时器中断_ARR

控制寄存器1 TIMx_CR1

定时器中断_CR1

DMA中断使能寄存器 TIMx_DIER

定时器中断_DIER

常用库函数

定时器参数初始化

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_TimeBaseInitTypeDef
    TIM_TimeBaseStucture.TIM_Period=4999;
    TIM_TimeBaseStucture.TIM_Prescaler=7199;
    TIM_TimeBaseStucture.TIM_ClockDivision=TIM_CKD_DIV1;
    TIM_TimeBaseStucture.TIM_CounterMode=TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure); 

定时器使能函数

操作控制寄存器1 TIMx_CR1。

void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);

定时器中断使能函数

操作DMA中断使能寄存器 TIMx_DIER。

void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);

状态标志位获取和清除

void TIM_ClearFlag(TIM_TypeDef TIMx, uint16_t TIM_FLAG);
void TIM_ClearITPendingBit(TIM_TypeDef
TIMx, uint16_t TIM_IT);
FlagStatus TIM_GetFlagStatus(TIM_TypeDef TIMx, uint16_t TIM_FLAG);
ITStatus TIM_GetITStatus(TIM_TypeDef
TIMx, uint16_t TIM_IT);

定时器中断实现步骤

①. 使能定时器时钟。

RCC_APB1PeriphClockCmd();

②. 初始化定时器,配置ARR,PSC。

TIM_TimeBaseInit();

③. 开启定时器中断,配置NVIC。

void TIM_ITConfig();
NVIC_Init();

④. 使能定时器。

TIM_Cmd();

⑤. 编写中断服务函数。

TIMx_IRQHandler();

定时器中断实验

led.cled.h直接从跑马灯实验中移植即可。

by库函数

timer.h

    #ifndef __TIMER_H
    #define __TIMER_H
    #include "sys.h"

    void TIM3_Int_Init(u16 arr, u16 psc);

    #endif

timer.c

    #include "timer.h"
    #include "sys.h"
    #include "led.h"

    void TIM3_Int_Init(u16 arr, u16 psc)
    {
        TIM_TimeBaseInitTypeDef TIM_TimeBaseInitSturc;
        NVIC_InitTypeDef NVIC_InitStucture;
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);

        TIM_TimeBaseInitSturc.TIM_Period=arr;//重装载值
        TIM_TimeBaseInitSturc.TIM_Prescaler=psc;//预分频系数
        TIM_TimeBaseInitSturc.TIM_CounterMode=TIM_CounterMode_Up;//向上计数模式
        TIM_TimeBaseInitSturc.TIM_ClockDivision=TIM_CKD_DIV1;//时钟分频因子为1
        TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitSturc);

        TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);//使能TIM3的更新中断

        //中断优先级NVIC设置
        NVIC_InitStucture.NVIC_IRQChannel=TIM3_IRQn;//TIM3中断通道
        NVIC_InitStucture.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级2
        NVIC_InitStucture.NVIC_IRQChannelSubPriority=2;//响应优先级2
        NVIC_InitStucture.NVIC_IRQChannelCmd=ENABLE;//中断使能
        NVIC_Init(&NVIC_InitStucture);

        TIM_Cmd(TIM3,ENABLE);//TIM3定时器初始化
    }

    void TIM3_IRQHandler(void)
    {
        if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET)//通过中断标志位判断是否发生了定时器中断
        {
            LED1=!LED1;
            TIM_ClearITPendingBit(TIM3,TIM_IT_Update);//清除中断标志位
        }
    }

main.c

    #include "stm32f10x.h"
    #include "timer.h"
    #include "sys.h"
    #include "delay.h"
    #include "led.h"


    int main(void)
    {
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//NVIC中断优先级分组
        delay_init();//延时函数初始化
        LED_Init();//LED初始化函数
        TIM3_Int_Init(4999,7199);//定时器500ms溢出时间
        while(1)
        {
            LED0=!LED0;
            delay_ms(200);
        }
    }

by寄存器

timer.h

    #ifndef __TIMER_H
    #define __TIMER_H
    #include "sys.h"

    void TIM3_Int_Init(u16 arr, u16 psc);

    #endif

timer.c

    #include "timer.h"
    #include "led.h"
    #include "sys.h"

    void TIM3_Int_Init(u16 arr, u16 psc)
    {
        RCC->APB1ENR|=1<<1;//TIM3定时器时钟使能
        TIM3->ARR=arr;//TIM3自动重装载值
        TIM3->PSC=psc;//设置预分频值
        TIM3->DIER|=1<<0;//允许TIM3更新中断
        MY_NVIC_Init(2,2,TIM3_IRQn,2);//TIM3 NVIC中断设置
        TIM3->CR1&=0<<4;//TIM3向上计数
        TIM3->CR1|=1<<0;//使能TIM3计数器
    }

    void TIM3_IRQHandler(void)
    {
        if(TIM3->SR&0X0001)//检测是否发生溢出中断
        {
            LED1=!LED1;
        }
        TIM3->SR&=~(1<<0);//清除中断标志位
    }

main.c

    #include "sys.h"    
    #include "delay.h"     
    #include "timer.h"
    #include "led.h"

    int main(void)
    {                 
        Stm32_Clock_Init(9);//系统时钟设置
        delay_init(72);//延时初始化
    LED_Init();//LED初始化函数
        TIM3_Int_Init(4999,7199);//溢出时间500ms
    while(1)
        {
            LED0=!LED0;
            delay_ms(200);
        }     
    } 

文章作者: Mat Jenin
文章链接: http://matjenin.xyz
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Mat Jenin !
  目录