STM32(五):TIMER定时器 (标准库函数)

前言

上一篇文章已经介绍了如何用STM32单片机中的Systick嘀嗒定时器来控制LED灯的交替闪烁,实现了点灯的第四种方式。这篇文章我们来介绍一下如何用STM32单片机中的TIMER定时器来控制LED灯的交替闪烁。

一、实验原理

1.定时器的功能

STM32的定时器是一种强大的外设,具有以下几种主要功能:

  • 定时功能:可以设置定时时间,用于在特定时刻执行任务。
  • 计数功能:对输入的时钟脉冲进行计数。
  • PWM信号产生:用于控制电机速度、灯光亮度等。
  • 输入捕获:捕获并记录外部信号的时间参数。
  • 定时器中断:在计数值达到设定值时触发中断,进行相应处理。
  • 2.定时器的类型

    STM32中的定时器根据功能和复杂度分为三种类型:

  • 高级定时器:TIM1和TIM8,支持更复杂的控制需求和高精度的操作。
  • 通用定时器:TIM2、TIM3、TIM4、TIM5,适用于常规的定时和计数任务。
  • 基本定时器:TIM6和TIM7,主要用于简单的定时任务,没有高级控制功能。
  • 3.定时器工作原理

    以下是定时器的工作原理图,主要分为5个部分,分别是时钟产生、定时器、输入、输出和寄存器部分。

    时钟产生来源:
    1. 内部时钟     PB1(CLK_INT) (*)
    2. TIMx_ETR       外部引脚输入
    3. ITRx            内部触发输入,定时器集联
    4. TL1FP1/2        来自于定时器外部通道
    主要是内部时钟,最后输出 [ CK_PSC ]

    输出比较:
    比如说现在定时器是从 0-100 向上计数
    如果把 [ 捕获比较寄存器 ] 的值设置为 v = 50;
    如果 当前值 < v 则输出高电平, 当前值 > v 则输出低电平
    那么效果就是 0-50 输出高电平, 50-100 输出低电平, 实现通道输出
    通过控制 V 的值 就能改变占空比,  实现PWM。

    定时器主要分为3种计数模式,向上计数、向下计数以及中央对齐模式。

    1.向上计数模式

    2.向下计数模式

    3.中央对齐模式

     3.定时器系统框图

    以下是定时器的系统框图:

    主要有4块组成部分:

    1.振荡器

  • HSI RC:内部高速时钟,8MHz。

  • HSE Osc:外部高速时钟,频率依赖于外部晶振。

  • LSI RC:内部低速时钟。

  • LSE Osc:外部低速时钟,通常是32.768kHz,用于实时时钟(RTC)。

  • 2.时钟源选择和配置

  • PLL(相位锁环):可以将HSI或HSE的频率乘以一个系数(如图中所示,最高可达72MHz)。

  • SYSCLK(系统时钟):可以选择HSI、HSE或PLLCLK作为源。

  • MCO(微控制器时钟输出):可以输出不同源的时钟,如HSI、HSE或PLLCLK的一部分。

  • 3.总线和接口时钟

  • AHB Prescaler:高性能总线预分频器,可对HCLK进行分频。

  • APB1 Prescaler和APB2 Prescaler:两个外设总线预分频器,分别对PCLK1和PCLK2进行分频。

  • ADC Prescaler:模数转换器预分频器,提供给ADC的时钟。

  • 4.定时器

  • TIM1、TIM8(高级定时器):接收PCLK2时钟,可以选择直接时钟或通过倍增器增倍(x1或x2)。

  • TIM2、TIM3、TIM4(通用定时器):接收PCLK1时钟,同样有倍增器选择。

  • 系统时钟与这些定时器的关系,决定了定时器的计数速度和精度。

  • 二、实验步骤

    1.时钟初始化

    首先是对时钟的初始化,其中我们要知道以下计算的式子

    定时器频率
    =((1+TIM_Prescaler )/72M)*(1+TIM_Period )


    例:如果想要设置定时器频率为
    1秒,可以设置

    TIM_Prescaler=35999,TIM_Period=1999 //2Khz的频率,计数到

    2000



    TIM_Prescaler=7199,TIM_Period=9999 //10KHz的频率,计数到

    10000

    参数如下所示:

  • TIM_Prescaler  用来设置分频系数
  • TIM_CounterMode  用来设置计数方式
  • TIM_Period 设置自动重载计数周期值
  • TIM_ClockDivision 用来设置时钟分频因子
  • void GENERAL_TIMx_Configuration(void)
    {
      TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    
      /* 开启TIMx_CLK,x[2,3,4,5],即内部时钟CK_INT=72M */
      GENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK, ENABLE);
      
      /* 通用定时器 TIMx,x[2,3,4,5]中断优先级配置 */
      GENERAL_TIMx_NVIC_Configuration();
      
      /* 自动重装载寄存器周的值(计数值) */
      TIM_TimeBaseStructure.TIM_Period=1000;
      /* 累计 TIM_Period个频率后产生一个更新或者中断
         时钟预分频数为71,则驱动计数器的时钟CK_CNT = CK_INT / (71+1)=1M */
      TIM_TimeBaseStructure.TIM_Prescaler= 71;
      /* 时钟分频因子 ,没有用到,不用管 */
      //TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
      /* 计数器计数模式,基本定时器TIM6和TIM7只能向上计数,没有计数模式的设置,不用管 */
      TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; 
      /* 重复计数器的值,通用定时器没有,不用管 */
      //TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
      /* 初始化定时器TIMx, x[2,3,4,5] */
      TIM_TimeBaseInit(GENERAL_TIMx, &TIM_TimeBaseStructure);
    
      /* 清除计数器中断标志位 */
      TIM_ClearITPendingBit(GENERAL_TIMx, TIM_IT_Update);
      /* 开启计数器中断 */
      TIM_ITConfig(GENERAL_TIMx,TIM_IT_Update,ENABLE);
    
      /* 使能计数器: */
      TIM_Cmd(GENERAL_TIMx, ENABLE); 
    }

    2.时钟中断

    时钟中断函数GENERAL_TIMx_NVIC_Configuration(void),这边是对中断来源以及优先级的配置,前面在Systick中有所介绍,可以看一下之前的连接:STM32(四):Systick (标准库函数)-CSDN博客

    void GENERAL_TIMx_NVIC_Configuration(void)
    {
      NVIC_InitTypeDef NVIC_InitStructure; 
      /* 设置中断组为0 */
      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);		
      /* 设置中断来源 */
      NVIC_InitStructure.NVIC_IRQChannel = GENERAL_TIM_IRQ;	
      /* 设置主优先级为 0 */
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;	 
      /* 设置抢占优先级为3 */
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
    	 /*定时器使能 */
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
      NVIC_Init(&NVIC_InitStructure);
    }

    但到这里我们的中断配置还没结束!!重点!!易踩坑!!

    我们还需要在stm32f10x_it.c的文件里面加上新的中断服务函数 GENERAL_TIM_INT_FUN (),记得加上头文件,例如:

    #include "bsp/GeneralTIM/bsp_GeneralTIM.h"
    extern __IO uint16_t timer_count;
    
    void  GENERAL_TIM_INT_FUN (void)
    {
    	if ( TIM_GetITStatus(GENERAL_TIMx,TIM_IT_Update) != RESET ) 
    	{	
    		timer_count++;
    		TIM_ClearITPendingBit(GENERAL_TIMx , TIM_IT_Update);  		 
    	}		 	
    }

    三、实操代码

    程序分为3个文件:bsp_GeneralTIM.c、bsp_GeneralTIM.h、main.c

    1.bsp_GeneralTIM.c

    
    /* 包含头文件 ----------------------------------------------------------------*/
    #include "bsp/GeneralTIM/bsp_GeneralTIM.h" 
    /**
      * 函数功能: 通用定时器 TIMx,x[2,3,4,5]中断优先级配置
      * 输入参数: 无
      * 返 回 值: 无
      * 说    明:无
      */
    void GENERAL_TIMx_NVIC_Configuration(void)
    {
      NVIC_InitTypeDef NVIC_InitStructure; 
      /* 设置中断组为0 */
      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);		
      /* 设置中断来源 */
      NVIC_InitStructure.NVIC_IRQChannel = GENERAL_TIM_IRQ;	
      /* 设置主优先级为 0 */
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;	 
      /* 设置抢占优先级为3 */
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
    	 /*定时器使能 */
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
      NVIC_Init(&NVIC_InitStructure);
    }
    
    
    void GENERAL_TIMx_Configuration(void)
    {
      TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    
      /* 开启TIMx_CLK,x[2,3,4,5],即内部时钟CK_INT=72M */
      GENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK, ENABLE);
      
      /* 通用定时器 TIMx,x[2,3,4,5]中断优先级配置 */
      GENERAL_TIMx_NVIC_Configuration();
      
      /* 自动重装载寄存器周的值(计数值) */
      TIM_TimeBaseStructure.TIM_Period=1000;
      /* 累计 TIM_Period个频率后产生一个更新或者中断
         时钟预分频数为71,则驱动计数器的时钟CK_CNT = CK_INT / (71+1)=1M */
      TIM_TimeBaseStructure.TIM_Prescaler= 71;
      /* 时钟分频因子 ,没有用到,不用管 */
      //TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
      /* 计数器计数模式,基本定时器TIM6和TIM7只能向上计数,没有计数模式的设置,不用管 */
      TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; 
      /* 重复计数器的值,通用定时器没有,不用管 */
      //TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
      /* 初始化定时器TIMx, x[2,3,4,5] */
      TIM_TimeBaseInit(GENERAL_TIMx, &TIM_TimeBaseStructure);
    
      /* 清除计数器中断标志位 */
      TIM_ClearITPendingBit(GENERAL_TIMx, TIM_IT_Update);
      /* 开启计数器中断 */
      TIM_ITConfig(GENERAL_TIMx,TIM_IT_Update,ENABLE);
    
      /* 使能计数器: */
      TIM_Cmd(GENERAL_TIMx, ENABLE); 
    }
    
    

    2.bsp_GeneralTIM.h

    #ifndef __GENERAL_TIM_H__
    #define __GENERAL_TIM_H__
    
    /* 包含头文件 ----------------------------------------------------------------*/
    #include <stm32f10x.h>
    
    /* 类型定义 ------------------------------------------------------------------*/
    /* 宏定义 --------------------------------------------------------------------*/
    /********************通用定时器TIM参数定义,只限TIM2 & TIM3 & TIM4 & TIM5************/
    #define GENERAL_TIMx                 TIM2
    #define GENERAL_TIM_APBxClock_FUN    RCC_APB1PeriphClockCmd
    #define GENERAL_TIM_CLK              RCC_APB1Periph_TIM2
    #define GENERAL_TIM_IRQ              TIM2_IRQn
    #define GENERAL_TIM_INT_FUN          TIM2_IRQHandler
    
    //#define GENERAL_TIMx                 TIM3
    //#define GENERAL_TIM_APBxClock_FUN    RCC_APB1PeriphClockCmd
    //#define GENERAL_TIM_CLK              RCC_APB1Periph_TIM3
    //#define GENERAL_TIM_IRQ              TIM3_IRQn
    //#define GENERAL_TIM_INT_FUN          TIM3_IRQHandler
    
    //#define GENERAL_TIMx                 TIM4
    //#define GENERAL_TIM_APBxClock_FUN    RCC_APB1PeriphClockCmd
    //#define GENERAL_TIM_CLK              RCC_APB1Periph_TIM4
    //#define GENERAL_TIM_IRQ              TIM4_IRQn
    //#define GENERAL_TIM_INT_FUN          TIM4_IRQHandler
    
    //#define GENERAL_TIMx                 TIM5
    //#define GENERAL_TIM_APBxClock_FUN    RCC_APB1PeriphClockCmd
    //#define GENERAL_TIM_CLK              RCC_APB1Periph_TIM5
    //#define GENERAL_TIM_IRQ              TIM5_IRQn
    //#define GENERAL_TIM_INT_FUN          TIM5_IRQHandler
    
    /* 扩展变量 ------------------------------------------------------------------*/
    /* 函数声明 ------------------------------------------------------------------*/
    void GENERAL_TIMx_Configuration(void);
    
    #endif	/* __GENERAL_TIM_H__ */
    
    

    3.main.c

    
    /* 包含头文件 ----------------------------------------------------------------*/
    #include "stm32f10x.h"
    #include "bsp/led/bsp_led.h"
    #include "bsp/key/bsp_key.h"
    #include "bsp/delay/delay.h"
    #include "bsp/systick/bsp_SysTick.h"
    #include "bsp/GeneralTIM/bsp_GeneralTIM.h" 
    /* 函数体 --------------------------------------------------------------------*/
    
    __IO uint16_t timer_count=0;
    /**
      * 函数功能: 主函数.
      * 输入参数: 无
      * 返 回 值: 无
      * 说    明: 无
      */
    int main(void)
    {
    	LED_GPIO_Init();
     //KEY_GPIO_Init();
    //	KEY1_EXIT_Config();
    //	KEY2_EXIT_Config();
    //	KEY3_EXIT_Config();
    	/* 初始化系统滴答定时器 */  
    //  SysTick_Init();
    	
    	
      /* 通用定时器初始化:1ms中断一次  */
      GENERAL_TIMx_Configuration();  
      while (1)
      {
        if(timer_count==500)
        {
          LED1_ON;
    	  LED2_OFF;
    	  LED3_OFF;
        }
    	 if(timer_count==1000)
        {
          LED1_OFF;
    	  LED2_ON;
          LED3_OFF;
        }
       if(timer_count==1500)
        {
          timer_count=0;
          LED1_OFF;
    	  LED2_OFF;
    	  LED3_ON;
        }
        
      }
    
    }
    
    

    四、实验效果

    Timer定时器点灯

    结束语

    本文以STM32VET6为例讲解了用TIMER定时器控制LED灯的交替闪烁的实现方法,并指出其中的易坑点。希望对大家有所帮助!如果还有什么问题,欢迎评论区留言,谢谢!

    作者:是覆盖对于变化

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32(五):TIMER定时器 (标准库函数)

    发表回复