stm32 定时器中断

在上一篇 STM32外部中断的理解 中,我们讲述了stm32的外部中断,它是通过外部的一个中断信号作为中断源,对 CPU 进行申请中断处理的;此篇,我们再讲述下 stm32 的内部定时器中断。

1. 综述

stm32 定时器中断,是通过设置内部的定时器相关寄存器,然后定时器进行自增(自减)到某一个数之后,产生一个中断信号,由 cpu 进行处理。如下图:
file

其中,时基单元部分相关的寄存器就是我们需要设置的。

下图是基本定时器框图:
file

2. 名词解析

CK_PSC: 定时器的预分频器时钟源。这个值为系统时钟频率,在手册中,虽然 TIM2 是属于 APB1 外设,APB1 外设的时钟频率为36MHz,但是我们使用的库中,在 SystemInit 函数中,将所有的定时器都倍频到了72MHz,所以这个值为72MHz。
PSC: 定时器的预分频器寄存器(Prescaler Register)。在 stm32 中,预分频器用于将定时器的时钟源分频,以降低定时器的时钟频率。
CK_CNT: 定时器计数器的时钟源。CK_CNT = CK_PSC / (PSC + 1)
CNT: 定时器的计数器寄存器。用于记录定时器的计数值,根据定时器的时钟源逐渐递增或递减,计数器增加达到最大值时会自动重置为0,并触发相应的中断事件。CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1)

ARR: 自动重装载寄存器(Auto-Reload Register)

3. 源码解析

我们这个实例演示了,通过定时器每隔1秒产生一个中断,并在 OLED 屏幕上进行显示。要达到这个功能,主要的核心是设置好 PSCARR
源码:

#include "Timer.h"

uint16_t num = 0;

void Timer_Init(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    TIM_InternalClockConfig(TIM2);

    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInitStruct.TIM_Period = 10000 - 1;
    TIM_TimeBaseInitStruct.TIM_Prescaler = 7200 - 1;
    TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);

    TIM_ClearFlag(TIM2, TIM_FLAG_Update);

    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
    NVIC_Init(&NVIC_InitStruct);

    TIM_Cmd(TIM2, ENABLE);
}

uint16_t Timer_Get(void)
{
    return num;
}

void TIM2_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) {
        num ++;
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    }
}

3.1 使能 TIM2 外设时钟

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

TIM2 属于 APB1 外设。

3.2 配置时钟源

TIM_InternalClockConfig(TIM2);

TIM2 作为内部时钟源,并且时钟频率是72MHz。若不调用此函数,TIM默认也为内部时钟

3.3 配置时基单元

    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;        // 时钟分频 不分频
    TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;    // 计数器模式 向上计数
    TIM_TimeBaseInitStruct.TIM_Period = 10000 - 1;                  // 计数周期,即 ARR 的值
    TIM_TimeBaseInitStruct.TIM_Prescaler = 7200 - 1;                // 预分频器,即 PSC 的值
    TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;               // 重复计数器,高级定时器才会用到
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);

ARR: 10000 – 1, PSC:7200 – 1, 此时我们可以计算出计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1)

CK_CNT_OV = 72MHz/(7200 – 1 + 1)/(10000 – 1 + 1) = 1 Hz
即:1秒产生1次溢出
预分频器确定好后,我们就可以通过设置 ARR 值,来产生不同时间的定时器。

3.4 开启TIM2的更新中断

TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);  

3.5 配置 NVIC

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
    NVIC_Init(&NVIC_InitStruct);

具体的解释参考:STM32外部中断的理解

3.6 TIM 使能

TIM_Cmd(TIM2, ENABLE);

使能TIM2,定时器开始运行

3.7 中断处理

void TIM2_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) {
        num ++;
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    }
}

至此,基本上整个定时器中断的流程就解析完成了。

发表评论