当前位置:网站首页>学习记录:TIM—基本定时器

学习记录:TIM—基本定时器

2022-07-06 09:25:00 Bitter tea seeds

目录

前言

        定时器分类

一、STM32 基本定时器

        1.1、基本定时器功能框图讲解

         1.2、时钟源

        1.3、计数器时钟

        1.4、计数器

        1.5、自动重装载寄存器

        1.6、定时时间的计算

二、STM32 通用定时器

        2.1、通用定时器简介

        2.2、TIMx主要功能 

         2.3、STM32 的通用寄存器

 三、实现程序的步骤 

        3.1、基本定时器实现程序步骤

        3.2、基本定时器实验现象

        3.3、通用定时器实现程序步骤 

         3.4、通用定时器实验现象


前言

        定时器分类

        STM32F1系列中,除了互联型的产品,共有 8 个定时器,分为基本定时器,通用定时器和高级定时器。 

        基本定时器TIM6 和 TIM7 是一个 16 位的只能向上计数的定时器,只能定时,没有外部 IO。

通用定时器TIM2、3、4、5 是一个 16 位的可以向上/下计数的定时器,可以定时,可以输出比较,可以输入捕捉,每个定时器有四个外部 IO。 

高级定时器TIM1、8 是一个 16 位的可以向上/下计数的定时器,可以定时,可以输出比较,可以输入捕捉,还可以有三相电机互补输出信号,每个定时器有 8 个外部 IO。 

一、STM32 基本定时器

        1.1、基本定时器功能框图讲解

        基本定时器的核心是时基,不仅基本定时器有,通用定时器和高级定时器也有。

        可编程通用定时器的主要部分是一个16位计数器和与其相关的自动装载寄存器。这个计数器可以向上计数、向下计数或者向上向下双向计数。此计数器时钟由预分频器分频得到。 计数器、自动装载寄存器和预分频器寄存器可以由软件读写,在计数器运行时仍可以读写。

        时基单元包含:

● 计数器寄存器(TIMx_CNT)

● 预分频器寄存器 (TIMx_PSC)

● 自动装载寄存器 (TIMx_ARR) 

         1.2、时钟源

        定时器时钟 TIMxCLK即内部时钟 CK_INT经 APB1 预分频器后分频提供,如果 APB1 预分频 系数等于 1,则频率不变,否则频率乘以 2,库函数中 APB1 预分频的系数是 2,即 PCLK1=36M, 所以定时器时钟 TIMxCLK=36*2=72M。

        1.3、计数器时钟

        定时器时钟经过 PSC 预分频器之后,即 CK_CNT,用来驱动计数器计数。PSC 是一个 16 位的预分频器,可以对定时器时钟 TIMxCLK 进行 1~65536 之间的任何一个数进行分频。具体计算方式 为:CK_CNT=TIMxCLK/(PSC+1)

        1.4、计数器

        计数器 CNT 是一个 16 位的计数器,只能往上计数,最大计数值为 65535。当计数达到自动重装载寄存器的时候产生更新事件,并清零从头开始计数。

        1.5、自动重装载寄存器

        自动重装载寄存器 ARR 是一个 16 位的寄存器,这里面装着计数器能计数的最大数值。当计数到这个值的时候,如果使能了中断的话,定时器就产生溢出中断。

        1.6、定时时间的计算

        定时器的定时时间等于计数器的中断周期乘以中断的次数。计数器在 CK_CNT 的驱动下,计一 个数的时间则是 CK_CLK 的倒数,等于:1/(TIMxCLK/(PSC+1)),产生一次中断的时间则等于: 1/(CK_CLK * ARR)。如果在中断服务程序里面设置一个变量 time,用来记录中断的次数,那么就可以计算出我们需要的定时时间等于:1/CK_CLK* (ARR+1)*time

二、STM32 通用定时器

        2.1、通用定时器简介

         STM32F1的通用定时器是一个通过可编程预分频器(PSC)驱动的 16 位自动装载计数器 (CNT)构成。

        STM32 的通用定时器可以被用于:测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和 PWM)等。 使用定时器预分频器和 RCC 时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。 

        STM32 的每个通用定时器都是完全独立的, 没有互相共享的任何资源。 

        2.2、TIMx主要功能 

        通用TIMx (TIM2、TIM3、TIM4和TIM5)定时器功能包括:

        ● 16位向上、向下、向上/向下自动装载计数器

自动重装载寄存器 ARR 是一个 16 位的寄存器,这里面装着计数器能计数的最大数值。当计数到 这个值的时候,如果使能了中断的话,定时器就产生溢出中断。

        ● 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65536之间的任意 数值

        ● 4个独立通道

 ─ 输入捕获

 ─ 输出比较

 ─ PWM生成(边缘或中间对齐模式)

 ─ 单脉冲模式输出

        ● 使用外部信号控制定时器和定时器互连的同步电路

        ● 如下事件发生时产生中断/DMA

─ 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发) 

─ 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)

─ 输入捕获

─ 输出比较

        ● 支持针对定位的增量(正交)编码器和霍尔传感器电路

        ● 触发输入作为外部时钟或者按周期的电流管理 

         2.3、STM32 的通用寄存器

        控制寄存器 1(TIMx_CR1)

        TIMx_CR1 的最低位,也就是计数器使能位,该位必须置 1,才能让定时器开始计数。

        从第 4 位 DIR 可以看出默认的计数方式是向上计数,同时也可以向下计数。

        第 5,6 位是设置计数对齐方式的。

        第 8 和第 9 位可以看出,可以设置定时器的时钟分频因子为 1,2,4。

        DMA/中断使能寄存器 (TIMx_DIER),该寄存器是一个 16 位的寄存器。

         它的第 0 位,该位是更新中断允许位,用到的是定时器的更新中断,所以该位要设置为 1,来允许由于更新事件所产生的中断。

        预分频寄存器(TIMx_PSC),该寄存器用设置对时钟进行分频,然后提供给计数器,作为计数器的时钟。

         通过 TIMx_SMCR 寄存器的相关位来设置时钟:1.内部时钟(CK_INT)2.外部时钟模式 1:外部输入脚(TIx)3.外部时钟模式 2:外部触发输入(ETR)4.内部触发输入(ITRx):使用 A 定时器作为 B 定时器的预分频器(A 为 B 提供时钟)。

         这里的 CK_INT 时钟是从 APB1 倍频的来的,除非 APB1 的时钟分频数设置为 1,否则通用定时器 TIMx 的时钟是 APB1 时钟的 2 倍,当 APB1 的时钟不分频的时候,通用定时器 TIMx 的时钟就等于 APB1 的时钟。这里还要注意的就是高级定时器的时钟不是来自 APB1,而是来自 APB2 的。

        TIMx_CNT 寄存器,该寄存器是定时器的计数器,该寄存器存储了当前定时器的计数值。 

        自动重装载寄存器(TIMx_ARR)在物理上实际对应着 2 个寄存器。 一个是程序员可以直接操作的,另外一个是影子寄存器。

        真正起作用的是影子寄存器。根据 TIMx_CR1 寄存器中 APRE 位的设置:APRE=0 时,预装载寄存器的内容可以随时传送到影子寄存器,此时 2 者是连通的;而 APRE=1 时,在每一次更新事件(UEV)时,才把预装在寄存器的内容传送到影子寄存器。

        状态寄存器(TIMx_SR)用来标记当前与定时器相关的各种事件/中断是否发生。 

 三、实现程序的步骤 

        使用定时器产生中断,然后在中断服务函数里面翻转 DS1 上的电平,来指示定时器中断的产生。

        3.1、基本定时器实现程序步骤

        (1) TIM_Prescaler:定时器预分频器设置,时钟源经该预分频器才是定时器时钟,它设定 TIMx_PSC 寄存器的值。可设置范围为 0 至 65535,实现 1 至 65536 分频。

        (2) TIM_CounterMode:定时器计数方式,可是在为向上计数、向下计数以及三种中心对齐模式。 基本定时器只能是向上计数,即 TIMx_CNT 只能从 0 开始递增,并且无需初始化。

        (3) TIM_Period:定时器周期,实际就是设定自动重载寄存器的值,在事件生成时更新到影子寄 存器。可设置范围为 0 至 65535。

        (4) TIM_ClockDivision:时钟分频,设置定时器时钟 CK_INT 频率与数字滤波器采样时钟频率分 频比,基本定时器没有此功能,不用设置。

        (5) TIM_RepetitionCounter:重复计数器,属于高级控制寄存器专用寄存器位,利用它可以非常容易控制输出 PWM 的个数。这里不用设置。

 LED.h代码如下所示:

#ifndef __LED_H
#define	__LED_H


#include "stm32f10x.h"


/* 定义LED连接的GPIO端口, 用户只需要修改下面的代码即可改变控制的LED引脚 */
// R-红色
#define LED1_GPIO_PORT    	GPIOB			              /* GPIO端口 */
#define LED1_GPIO_CLK 	    RCC_APB2Periph_GPIOB		/* GPIO端口时钟 */
#define LED1_GPIO_PIN		GPIO_Pin_5			        /* 连接到SCL时钟线的GPIO */

#define ON  0
#define OFF 1

/* 使用标准的固件库控制IO*/
#define LED1(a)	if (a)	\
					GPIO_SetBits(LED1_GPIO_PORT,LED1_GPIO_PIN);\
					else		\
					GPIO_ResetBits(LED1_GPIO_PORT,LED1_GPIO_PIN)


/* 直接操作寄存器的方法控制IO */
#define	digitalHi(p,i)		 {p->BSRR=i;}	 //输出为高电平		
#define digitalLo(p,i)		 {p->BRR=i;}	 //输出低电平
#define digitalToggle(p,i) {p->ODR ^=i;} //输出反转状态


/* 定义控制IO的宏 */
#define LED1_TOGGLE		 digitalToggle(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED1_OFF		   digitalHi(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED1_ON			   digitalLo(LED1_GPIO_PORT,LED1_GPIO_PIN)

void LED_GPIO_Config(void);

#endif /* __LED_H */

 LED.c程序如下所示:

#include "bsp_led.h"   


void LED_GPIO_Config(void)
{		
		/*定义一个GPIO_InitTypeDef类型的结构体*/
		GPIO_InitTypeDef GPIO_InitStructure;

		/*开启LED相关的GPIO外设时钟*/
		RCC_APB2PeriphClockCmd( LED1_GPIO_CLK , ENABLE);

		/*选择要控制的GPIO引脚*/
		GPIO_InitStructure.GPIO_Pin = LED1_GPIO_PIN;	

		/*设置引脚模式为通用推挽输出*/
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   

		/*设置引脚速率为50MHz */   
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 

		/*调用库函数,初始化GPIO*/
		GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);	

		/* 关闭所有led灯	*/
		GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
		
}

 TIMEBASE.h程序如下所示:

#ifndef __BSP_TIMEBASE_H
#define __BSP_TIMEBASE_H


#include "stm32f10x.h"

#define BASIC_TIM6 // 如果使用TIM7,注释掉这个宏即可

#ifdef  BASIC_TIM6 // 使用基本定时器TIM6
#define            BASIC_TIM                   TIM6
#define            BASIC_TIM_APBxClock_FUN     RCC_APB1PeriphClockCmd
#define            BASIC_TIM_CLK               RCC_APB1Periph_TIM6
#define            BASIC_TIM_Period            (1000-1)
#define            BASIC_TIM_Prescaler         71
#define            BASIC_TIM_IRQ               TIM6_IRQn
#define            BASIC_TIM_IRQHandler        TIM6_IRQHandler

#else  // 使用基本定时器TIM7
#define            BASIC_TIM                   TIM7
#define            BASIC_TIM_APBxClock_FUN     RCC_APB1PeriphClockCmd
#define            BASIC_TIM_CLK               RCC_APB1Periph_TIM7
#define            BASIC_TIM_Period            1000-1
#define            BASIC_TIM_Prescaler         71
#define            BASIC_TIM_IRQ               TIM7_IRQn
#define            BASIC_TIM_IRQHandler        TIM7_IRQHandler

#endif

void BASIC_TIM_Init(void);


#endif	/* __BSP_TIMEBASE_H */


 TIMEBASE.c程序如下所示:

// 基本定时器TIMx,x[6,7]定时初始化函数
#include "bsp_TiMbase.h" 

// 中断优先级配置
static void BASIC_TIM_NVIC_Config(void)
{
    NVIC_InitTypeDef NVIC_InitStructure; 
    // 设置中断组为0
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);		
		// 设置中断来源
    NVIC_InitStructure.NVIC_IRQChannel = BASIC_TIM_IRQ ;	
		// 设置主优先级为 0
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;	 
	  // 设置抢占优先级为3
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;	
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

/*
 * 注意:TIM_TimeBaseInitTypeDef结构体里面有5个成员,TIM6和TIM7的寄存器里面只有
 * TIM_Prescaler和TIM_Period,所以使用TIM6和TIM7的时候只需初始化这两个成员即可,
 * 另外三个成员是通用定时器和高级定时器才有.
 *-----------------------------------------------------------------------------
 *typedef struct
 *{ TIM_Prescaler            都有
 *	TIM_CounterMode			     TIMx,x[6,7]没有,其他都有
 *  TIM_Period               都有
 *  TIM_ClockDivision        TIMx,x[6,7]没有,其他都有
 *  TIM_RepetitionCounter    TIMx,x[1,8,15,16,17]才有
 *}TIM_TimeBaseInitTypeDef; 
 *-----------------------------------------------------------------------------
 */


static void BASIC_TIM_Mode_Config(void)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
		
		// 开启定时器时钟,即内部时钟CK_INT=72M
    BASIC_TIM_APBxClock_FUN(BASIC_TIM_CLK, ENABLE);
	
		// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
    TIM_TimeBaseStructure.TIM_Period = BASIC_TIM_Period;	

	  // 时钟预分频数为
    TIM_TimeBaseStructure.TIM_Prescaler= BASIC_TIM_Prescaler;
	
		// 时钟分频因子 ,基本定时器没有,不用管
    //TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
		
		// 计数器计数模式,基本定时器只能向上计数,没有计数模式的设置
    //TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; 
		
		// 重复计数器的值,基本定时器没有,不用管
		//TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
	
	  // 初始化定时器
    TIM_TimeBaseInit(BASIC_TIM, &TIM_TimeBaseStructure);
		
		// 清除计数器中断标志位
    TIM_ClearFlag(BASIC_TIM, TIM_FLAG_Update);
	  
		// 开启计数器中断
    TIM_ITConfig(BASIC_TIM,TIM_IT_Update,ENABLE);
		
		// 使能计数器
    TIM_Cmd(BASIC_TIM, ENABLE);	
}

void BASIC_TIM_Init(void)
{
	BASIC_TIM_NVIC_Config();
	BASIC_TIM_Mode_Config();
}

 main.c程序如下所示:


// 基本定时器TIMx,x[6,7]定时应用
#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_TiMbase.h"

volatile uint32_t time = 0; // ms 计时变量 

int main(void)
{
	/* led 端口配置 */ 
	LED_GPIO_Config();
	
	BASIC_TIM_Init();
	
  while(1)
  {
    if ( time == 1000 ) /* 1000 * 1 ms = 1s 时间到 */
    {
      time = 0;
			/* LED1 取反 */      
			LED1_TOGGLE; 
    }        
  }
}

        3.2、基本定时器实验现象

        正点原子精英开发板LED0红灯每隔一秒闪烁一次;

        3.3、通用定时器实现程序步骤 

time.h程序如下所示:

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

void TIM3_Int_Init(u16 arr,u16 psc);
 
#endif

time.c程序如下所示:

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

//通用定时器3中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器3!
void TIM3_Int_Init(u16 arr,u16 psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
	
	//定时器TIM3初始化
	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
 
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断

	//中断优先级NVIC设置
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器


	TIM_Cmd(TIM3, ENABLE);  //使能TIMx					 
}
//定时器3中断服务程序
void TIM3_IRQHandler(void)   //TIM3中断
{
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)  //检查TIM3更新中断发生与否
		{
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx更新中断标志 
		LED1=!LED1;
		}
}

 main.c程序如下所示

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

	
 int main(void)
 {		
 
	delay_init();	    	 //延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	 //串口初始化为115200
 	LED_Init();			     //LED端口初始化
	TIM3_Int_Init(4999,7199);//10Khz的计数频率,计数到5000为500ms  
   	while(1)
	{
		LED0=!LED0;
		delay_ms(200);		   
	}	 

 
}	 
 

         3.4、通用定时器实验现象

          正点原子精英开发板LED0红灯闪烁的比LED1绿灯快;

原网站

版权声明
本文为[Bitter tea seeds]所创,转载请带上原文链接,感谢
https://blog.csdn.net/weixin_69250798/article/details/125520578