stm32定时器笔记

  参考资料: STM32-定时器详解

        STM32F103C8T6的学习(5)——定时器

        TM32定时器之预分频器详解

        stm32之TIM-基本定时器应用实例(详细)

        STM32 TIM 定时时间的计算

        《STM32单片机应用基础与项目实践》

 

  单片机具有一些外设,即外围功能模块。这些外设可以通过IO,SPI,I2C等总线控制。

  常见的外设:基本IO,定时器TIM,RTC闹钟,ADC模数等等……

定时器概括

  定时器是stm32中的一个常见外设,可以对输入的时钟进行计数,计数达到设定值触发中断。

  stm32有11个定时器,根据复杂度和应用场景分为:

    2个高级定时器(TIM1,TIM8),能产生3对PWM互补输出,常用于三相电机的驱动,时钟由APB2的输出产生;

    4个普通定时器(TIM2~TIM5),时钟由APB1输出产生。

    2个基本定时器(TIM6,TIM7),时钟由APB1输出产生。

    2个看门狗定时器和一个系统嘀嗒定时器(SysTick)。

  其中基本定时器只有大容量的STM32F103RC才有。而STM32F103C8T6只有TIM1,TIM2,TIM3,TIM4(一个高级定时器,三个普通定时器)。

  比较常用的是TIM2~TIM5普通定时器的定时功能。

时钟来源

  计时器时钟可以由以下时钟源提供:

    (1) 内部时钟(CK_INT)。使用这个需要预分频器,见下。

    (2) 外部时钟模式1:外部输入脚(Tix)

    (3) 外部时钟模式2:外部触发输入(ETR)

    (4) 内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器。

  使用内部时钟时,TIM2~TIM5的时钟不是直接来自于APB1,而是来自于输出为APB1的一个倍频器。通过倍频器可以保证在其他外设使用较低时钟频率时,TIM2~TIM5仍然可以得到较高的时钟频率。

预分频器

  stm32中,定时器的时钟源为频率极高的内部时钟时。不分频极其容易溢出。例如$72MHz$的时钟,$72MHz=72*10^6Hz$,一秒计数$7.2*10^7$次。16位最高到$65535$,溢出只需要$\frac{65535}{7.2*10^7}=0.0009s$!因此要对时钟进行分频输出。除此之外,预分频的好处还有:可以得到更准确的时间。

 

定时器初始化

  预分频系数psc:对系统时钟进行分频。

  重装载值arr:计数达到这个值后重新装载。

  stm32的所有外设初始化都是通过库中的结构体和初始化函数,定时器TIM也是这样。这个结构体类型是 TIM_TimeBaseInitTypeDef ,在库中的定义如下:

 

/* TIME 这个结构体可以用于所有的TIMx,除了TIM6和TIM7 */
typedef struct
{
  uint16_t TIM_Prescaler;             // 预先分频系数psc,可以是0~65535间的数 
  uint16_t TIM_CounterMode;           //   (计数器模式,基本定时器只能向上计数,不用设置) 
  uint16_t TIM_Period;                // 重装载值arr,可以理解为周期 
  uint16_t TIM_ClockDivision;         //   (时钟分频,不用设置) 
  uint8_t TIM_RepetitionCounter;      //   (重复计数器,不用设置,只对TIM1和TIM8两个高级定时器起作用的参数) 
} TIM_TimeBaseInitTypeDef;   

 

定时器间隔:

  在系统时钟是$72MHz$时,进入中断程序的时间间隔是$((1+psc)/72M)*(1+arr)$,在arr为$10000-1$,psc为$7200-1$时,中断时间为$1s$。

 

定时器初始化代码:

    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;    // TIM结构体声明 
    NVIC_InitTypeDef NVIC_InitStructure;            // 嵌套向量中断结构体声明 
    
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);     //打开TIM6的时钟 
    
    /* 配置时基 */
    TIM_TimeBaseStructure.TIM_Period = arr;                     // 设置arr 
    TIM_TimeBaseStructure.TIM_Prescaler =psc;                     // 设置psc 
    TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;         // 不分频 
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数 
    TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);             // TIM6初始化 
     
    NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;              // TIM6中断 
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             // 打开IRQ通道 
    NVIC_Init(&NVIC_InitStructure);                              // 初始化NVIC 
    
    TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE);    // 开启定时器 
    TIM_Cmd(TIM6, ENABLE);                      // 打开TIM6外设 

   其中,TIM6定时器的中断程序是库中的 TIM6_IRQHandler ,初始时是空函数,使用时需要直接在其上修改,或者将其注释后再重新写。

 

频闪灯代码

 

#include "stm32f10x.h"

void LED_init(){    // PA0
        GPIO_InitTypeDef LED_InitStructure;
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
        LED_InitStructure.GPIO_Pin=GPIO_Pin_0;
        LED_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
        LED_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
        GPIO_Init(GPIOA,&LED_InitStructure);
}

void TIM_init(int interval_ms){    // TIM3
        u16 arr=10*interval_ms-1;
        u16 psc=7200-1;
    
        TIM_TimeBaseInitTypeDef TIM_InitStructure;
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
        TIM_InitStructure.TIM_Period=arr;
        TIM_InitStructure.TIM_Prescaler=psc;
        TIM_InitStructure.TIM_ClockDivision=0;
        TIM_InitStructure.TIM_CounterMode=TIM_CounterMode_Up;
        TIM_TimeBaseInit(TIM3,&TIM_InitStructure);
    
        NVIC_InitTypeDef NVIC_InitStructure;
        NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;
        NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
        NVIC_Init(&NVIC_InitStructure);
        
        TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
        TIM_Cmd(TIM3,ENABLE);
        
}

void TIM3_IRQHandler(){
        if (TIM_GetITStatus(TIM3,TIM_IT_Update)!=RESET){
                TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
                GPIOA->ODR^=GPIO_Pin_0;
        }
}

int main(void)
{
    LED_init();
        TIM_init(1000);
        while (1);
}

 

 

 

  

  

 

posted @ 2023-04-02 21:54  wegret  阅读(457)  评论(0)    收藏  举报