STM32 通用定时器

一、概述

        STM32内部集成了多个定时/计数器,根据型号不同,STM32系列芯片最多包含8个定时/计数器。其中,TIM6、TIM7为基本定时器,TIM2~TIM5为通用定时器,TIM1、TIM8为高级控制定时器。

1.定时器的类型

  • 基本定时器
  • 通用定时器
  • 高级控制定时器
  • 窗口看门狗定时器
  • 独立看门狗定时器
  • 系统滴答定时器

2.计数模式

  • 向上计数模式:计数器从0计数到自动加载值(ARR),并产生向上溢出事件。

  • 向下计数模式:计数器从自动加载值(ARR)向下计数到0,并产生向下溢出事件。 

  • 中央对齐模式:计数器从0开始计数到自动加载值-1,产生向上溢出事件,然后向下计数到1,产生向下溢出事件,最后再从0开始重新计数。

3.主要功能 

  • 基本定时功能
  •  输出比较
  • 输入捕获
  • 编码器接口模式
  • 单脉冲模式
  • 死区控制和刹车功能

        注:本文将介绍前四种常见的功能。 

4.通用定时器的结构

        STM32通用定时器主要包括1个外部触发引脚(TIMx_ETR),4个输入/输出通道(TIMx_CH1、 TIMx_CH2、TIMx_CH3和TIMx_CH4),1个内部时钟1个触发控制器1个时钟单元(由预分频器PSC、自动重装载寄存器ARR和计数器CNT组成)。如图所示:

5.时钟源

        定时/计数器时钟可由下列时钟源(如上图所示)提供: 

  • 内部时钟(CK_INT)
  • 外部时钟模式1(TIMx_CH1~4)
  • 外部时钟模式2(TIMx_ETR)
  • 内部触发输入(ITR,使用一个定时器作为另一个定时器的预分频器)

        当时钟源来自内部时,可实现定时功能;当时钟源来自外部时,可实现计数功能。

6.功能寄存器

        略。

二、基本定时功能

        下面介绍TIM定时器最基础的功能:基本定时功能。这种功能常用于周期性事件的触发。使用流程和步骤如下:

  1. 选择时钟源
  2. 配置时基单元
  3. 配置NVIC
  4. 编写中断服务函数
void Timer_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			
    //开启TIM2的时钟
	
	TIM_InternalClockConfig(TIM2);		
    //选择时钟源为内部时钟,若不调用此函数,TIM2默认也为内部时钟
	
	/*配置时基单元*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;			
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		
    //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	
    //计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;				
    //计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;				
    //预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			
    //重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);						
    //清除定时器更新标志位,TIM_TimeBaseInit函数末尾,手动产生了更新事件,
    //若不清除此标志位,则开启中断后,会立刻进入一次中断,
    //如果不介意此问题,则不清除此标志位也可。
																
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);					//开启TIM2的更新中断
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//设置NVIC分组

	/*配置NVIC*/
	NVIC_InitTypeDef NVIC_InitStructure;						
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;				//选择配置NVIC的TIM2线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;	//设置抢占优先级为2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//设置响应优先级为1
	NVIC_Init(&NVIC_InitStructure);	
	
	TIM_Cmd(TIM2, ENABLE);			                            //使能TIM2,运行TIM2
}




void TIM2_IRQHandler(void)                                      //定时器中断函数
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{    
            
        //此处编写要周期实现的功能
		
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}

 三、输出比较功能

         当定时器的计数器值(CNT)与捕获比较寄存器(CCR)的值相等时,产生比较事件,并根据配置对输出管脚进行相应的操作,如翻转或置位。其应用场景如下:

  • PWM(脉宽调制)信号的产生:输出占空比可调的PWM信号,用于电机控制、LED调光等。
  • 定时脉冲:在特定时间产生一个脉冲信号,用于精确事件触发。
  • DAC触发:精确触发模拟信号输出。

        下面介绍产生PWM波的使用流程:

  1.  配置GPIO,用于输出PWM,根据引脚定义表配置
  2. 选择时钟源
  3. 配置时基单元
  4. 配置输出比较模式
void PWM_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	

	/*配置GPIO*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;                 //复用推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);								
	//受外设控制的引脚,均需要配置为复用模式
	

	TIM_InternalClockConfig(TIM2);		
    //选择时钟源为内部时钟,若不调用此函数,TIM默认也为内部时钟
	

	/*配置时基单元*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     
    //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; 
    //计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;                 
    //计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 36 - 1;               
    //预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            
    //重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);            

	
	/*配置输出比较模式*/ 
	TIM_OCInitTypeDef TIM_OCInitStructure;	
	TIM_OCStructInit(&TIM_OCInitStructure);                         
    //结构体初始化,若结构体没有完整赋值,则最好执行此函数,给结构体所有成员都赋一个默认值,避免结构体初值不确定的问题
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;               
    //输出比较模式,选择PWM模式1
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;       
    //输出极性,选择为高,若选择极性为低,则输出高低电平取反
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;   
    //输出使能
	TIM_OCInitStructure.TIM_Pulse = 0;								
    //初始的CCR值,也可以不定为0,直接定为想要的占空比所需的CCR值
	TIM_OC3Init(TIM2, &TIM_OCInitStructure);                        


	TIM_Cmd(TIM2, ENABLE);			
    //使能TIM2,运行TIM2
}


TIM_SetCompare3(TIM2, Compare);		            
//设置CCR的值,设置CCR几的值取决于使用哪个引脚,PA2对应的是CCR3
//该行代码用于改变占空比,一般放在主函数或者中断服务函数

 另外,PWM的三项重要数据的计算方法如下:

  1. 占空比:CCR/(ARR+1)
  2. 分辨率:1/(ARR+1)
  3. 频率:CK_PSC/(PSC+1)/(ARR+1),CK_PSC一般为72MHz

四、输入捕获功能 

        输入捕获模式用于测量外部信号的时间特性,例如周期、频率、脉宽等。它通过将外部输入信号的某个边沿(上升沿或下降沿)捕获并保存计数器的值,从而实现时间测量。 

        下面介绍通过输入捕获功能实现频率测量的步骤:

  1.  配置GPIO,用于接收需要测频率的信号,根据引脚定义表配置
  2. 选择时钟源
  3. 配置时基单元
  4. 配置输入捕获功能
  5. 编写频率计算函数
void IC_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);			//开启TIM3的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	

	/*配置GPIO*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;				    //上拉输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);			
	

	TIM_InternalClockConfig(TIM3);		
    //选择TIM3为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
	/*配置时基单元*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     
    //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; 
    //计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;               
    //计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;               
    //预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            
    //重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);

	
	/*配置输入捕获功能*/
	TIM_ICInitTypeDef TIM_ICInitStructure;
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;				
    //选择配置定时器通道1
	TIM_ICInitStructure.TIM_ICFilter = 0xF;							
    //输入滤波器参数,可以过滤信号抖动
	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;		
    //极性,选择为上升沿触发捕获
	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;			
    //捕获预分频,选择不分频,每次信号都触发捕获
	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;	
    //输入信号交叉,选择直通,不交叉
	TIM_ICInit(TIM3, &TIM_ICInitStructure);	
	

	/*选择触发源及从模式*/
	TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);					
    //触发源选择TI1FP1
	TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);					
    //从模式选择复位,即TI1产生上升沿时,会触发CNT归零
	

	TIM_Cmd(TIM3, ENABLE);			//使能TIM3,运行TIM3
}





uint32_t IC_GetFreq(void)
{
	return 1000000 / (TIM_GetCapture1(TIM3) + 1);		
    //测周法得到频率fx = fc / N,这里不执行+1的操作也可
}

         频率测量方法有两种,一种是适用于测量高频信号的测频法,一种是适用于测量低频信号的测周法。其原理如下图所示:

 五、编码器模式

        编码器接口模式用于解码旋转编码器的信号。它可以直接连接增量型旋转编码器的A相和B相信号,并解码出编码器的旋转方向和位置。 每个高级定时器和通用定时器都拥有1个编码器接口,两个输入引脚借用了输入捕获的通道1和通道2。

        下面介绍使用编码器模式来测量电机转速的步骤,硬件上将带编码器的电机的编码输出连接到STM32的PA6和PA7,具体如下:

  1. 配置GPIO,用于接收正交编码,根据引脚定义表配置
  2. 配置时基单元
  3. 配置输入捕获模式
  4. 配置编码器模式
  5. 配置另一个定时器,编写速度计算函数
void Encoder_Init(void)
{
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);			//开启TIM3的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	

    /*配置GPIO*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;           //浮空输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;          //配置PA6和PA7引脚
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);							
	  
  
    /*配置时基单元*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; 
	TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;               //计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;                //预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);             //使用TIM3
	

    /*配置输入捕获模式*/
	TIM_ICInitTypeDef TIM_ICInitStructure;							
	TIM_ICStructInit(&TIM_ICInitStructure);																																		
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;				
	TIM_ICInitStructure.TIM_ICFilter = 0x10;							
    //输入滤波器参数,可以过滤信号抖动
	TIM_ICInit(TIM3, &TIM_ICInitStructure);	
						
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;				
	TIM_ICInitStructure.TIM_ICFilter = 0x10;							
    //输入滤波器参数,可以过滤信号抖动
	TIM_ICInit(TIM3, &TIM_ICInitStructure);							
	
	TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Rising);
	//配置编码器模式以及两个输入通道是否反相

	TIM_ClearFlag(TIM3, TIM_FLAG_Update);
	
	TIM_SetCounter(TIM3, 0);
	
	TIM_Cmd(TIM3, ENABLE);			                                //使能TIM3
}


//使用二、基本定时功能,在中断服务函数中编写计算速度的代码。
//这里需要另外配置一个定时器,相关代码参考第二点,这里不再赘述。
//先计算电机转一圈,STM32收到的n个编码;这取决于电机本身的参数。
//再每隔T时间输出STM32一共接收到的N个编码;则N/n即这段时间T里电机转过的圈数。
//最后用N/n/T即可求出转速。其中:

    int n=xxx                     //根据电机参数计算
    int N=TIM_GetCounter(TIM3);   //STM32接收到的编码数
    TIM_SetCounter(TIM3, 0);      //拿到T时间内的编码数后,计数清零,重新计数
    float Speed=N/n/T;            //T为定时周期
    
    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值