stm32f4编码器模式

因为想申请 CSDN 博客认证需要一定的粉丝量,而我写了五年博客才 700 多粉丝,本文开启关注才可阅读全文,很抱歉影响您的阅读体验

stm32f4编码器模式

花费一下午时间研究编码器的使用,简单总结

  • E6B2-CWZ1X编码器
  • stm32f407 定时器编码器模式

一、编码器简介

1、分类

编码器可按以下方式来分类。

(1)增量型:

每转过单位的角度就发出一个脉冲信号,通常为A相、B相(某些包括Z相)输出。A相、B相为相互延迟1/4周期的脉冲输出(即正交信号),根据延迟关系可以区别正反转,而且通过取A相、B相的上升和下降沿可以进行2或4倍频。Z相为单圈脉冲,即每圈发出一个脉冲,常用于校正累计误差。

(2)绝对值型:

对应一圈,每个基准的角度发出一个唯一与该角度对应二进制的数值,通过外部记圈器件可以进行多个位置的记录和测量。

2、编码器中的线、位、分辨率

请参考:链接

3、编码器原理


前文提到编码器通过发送正交脉冲信号表示角度信息,如图为一个示例。(其中TI1和TI2分别对应编码器输出A、B项)

脉冲信号特性表示信息
两项先后关系旋转方向
脉冲个数转过角度

注意到A、B项信号都可能出现毛刺,需要算法修正毛刺


二、stm32f4编码器模式

1、简介

我们可以利用外部中断分别捕获A、B项边沿,手写逻辑消除毛刺并解析编码器数据,但这是比较复杂的。其实这里的脉冲输入是一种特殊的输入捕获情况,因此stm32专门在定时器中提供了编码器模式,可大大简化解析过程。

(1)、stm32f407中定时器1、2、3、4、5、8提供编码器接口模式
(2)、可以对输入信号TI1,TI2进行滤波处理,数字滤波器由事件器组成,每N个事件才视为一个有效边沿,可以在TIMx_CCMR1、TIMx_CCMR2中的IC1F位域设置
(3)、stm32提供了单项计数(只能测速度)和双项计数模式(可测速度&方向),双项模式可以更好地消除毛刺干扰,一般使用双项模式,具体见下图
这里写图片描述
下图为双项模式下计数效果,可见在A、B中仅一项有毛刺时,计数值加减后保持不变,实现了抖动补偿
这里写图片描述

(4)、 编码器A、B相输入的信号TI1、TI2经滤波和反相后成为TI1FP1 或 TI2FP2 ,定时器的时钟由他们上的每次有效信号转换提供,也就是说最终计数值即反映转过角度。
(5)、 TI1FP1 或 TI2FP2反相可以改变计数方向,如下图:
这里写图片描述
(6)、定时器配置为编码器接口模式时,会提供传感器当前位置的相关信息。使用另一个配置为捕获模式的定时器测量两个编码器事件之间的周期,可获得动态信息(速度、加速度和减速度)。
(7)、计数溢出后,定时器会装载“重装载值”,并清零重新计数,此值可设置为编码器旋转一周的脉冲个数,这样既可利用溢出中断次数判断转了几圈。但若只要求旋转角度,此值可以任意。任意时刻角度为:溢出中断次数*重装载值+当前计数值
(8)、TIMx_CR1寄存器的 DIR 位指示当前旋转方向

2、示例代码

(1)定时器初始化设置

void TIM3_Int_Init() 
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_ICInitTypeDef TIM_ICInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); 

//定时器设置-------------------------------------------------------------	
  TIM_TimeBaseInitStructure.TIM_Period = 3600; 	//重装载值
	TIM_TimeBaseInitStructure.TIM_Prescaler=0x0;  //预分频
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //时钟分割
	
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);//初始化TIM3

//编码器模式设置--------------------------------------------------------------			  		

	TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//计数模式3
	
	TIM_ICStructInit(&TIM_ICInitStructure); 
    TIM_ICInitStructure.TIM_ICFilter = 10;//滤波器值
    TIM_ICInit(TIM3, &TIM_ICInitStructure);
//溢出中断设置--------------------------------------------------------------	
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); //允许TIM3溢出中断

	NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn; 
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x01; 
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x01; 
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init(&NVIC_InitStructure);
  
 //Reset counter-----------------------------------------------
  TIM_SetCounter(TIM3,0); //TIM3->CNT=0
  TIM_Cmd(TIM3, ENABLE); 
}

(2)在中断服务函数中进行圈数计算

int circle_count=0;//全局变量-圈数
void TIM3_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET)
	{		
		if((TIM3->CR1>>4 & 0x01)==0) //DIR==0
			circle_count++;
		else if((TIM3->CR1>>4 & 0x01)==1)//DIR==1
			circle_count--;
	}
	TIM_ClearITPendingBit(TIM3,TIM_IT_Update); 
}

(3)获取当前角度值

int Encoder=0;
extern int circle_count;

Encoder=TIM_GetCounter(TIM3)+3600*circle_count;//当前角度
  • 上述代码在stm32f407平台测试通过
  • 使用另一个配置为捕获模式的定时器测量两个编码器事件之间的周期,可获得动态信息(速度、加速度和减速度)。

三、update 2023/7/7

  • 有网友想要完整代码,我从19年的上古工程中找了找,这是我们当初 robomaster 比赛时摩擦轮测速的代码,时间太久了我也看不太懂,请大家自行参考
    1. 编码器初始化

      //摩擦轮编码器(右)
      void TIM2_Encoder_init(void)
      {
      	GPIO_InitTypeDef         GPIO_InitStructure; 
          TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
          TIM_ICInitTypeDef        TIM_ICInitStructure;
       
          RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
          RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
        
          GPIO_PinAFConfig(GPIOA,GPIO_PinSource0,GPIO_AF_TIM2);
          GPIO_PinAFConfig(GPIOA,GPIO_PinSource1,GPIO_AF_TIM2);
       
          GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1; 
          GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
          GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
          GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
          GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP ;
          GPIO_Init(GPIOA,&GPIO_InitStructure); 
       
          TIM_TimeBaseStructure.TIM_Period = 60000; 
          TIM_TimeBaseStructure.TIM_Prescaler = 0; 
          TIM_TimeBaseStructure.TIM_ClockDivision = 0; 
          TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
          TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); 
       
          TIM_EncoderInterfaceConfig(TIM2, 
      								TIM_EncoderMode_TI1,
      								TIM_ICPolarity_Falling,
      								TIM_ICPolarity_Falling);
          TIM_ICStructInit(&TIM_ICInitStructure);
          TIM_ICInitStructure.TIM_ICFilter = 10; 
          TIM_ICInit(TIM2, &TIM_ICInitStructure);
          TIM_ClearFlag(TIM2, TIM_FLAG_Update); 
          TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); 
          TIM2->CNT = 30000;
          TIM_Cmd(TIM2, ENABLE);
      }
      
      
      //摩擦轮编码器(左)
      void TIM8_Encoder_init(void)
      {
      	GPIO_InitTypeDef         GPIO_InitStructure; 
          TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
          TIM_ICInitTypeDef        TIM_ICInitStructure;
       
          RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8,ENABLE);
          RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOI,ENABLE);
        
          GPIO_PinAFConfig(GPIOI,GPIO_PinSource5,GPIO_AF_TIM8);
          GPIO_PinAFConfig(GPIOI,GPIO_PinSource6,GPIO_AF_TIM8);
       
          GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6; //GPIOB0,GPIOB1
          GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
          GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
          GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
          GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP ;
          GPIO_Init(GPIOI,&GPIO_InitStructure); 
       
          TIM_TimeBaseStructure.TIM_Period = 60000; 
          TIM_TimeBaseStructure.TIM_Prescaler = 0; 
          TIM_TimeBaseStructure.TIM_ClockDivision = 0; 
          TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
          TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure); 
       
          TIM_EncoderInterfaceConfig(TIM8, 
      								TIM_EncoderMode_TI1,
      								TIM_ICPolarity_Falling,
      								TIM_ICPolarity_Falling);
          TIM_ICStructInit(&TIM_ICInitStructure);
          TIM_ICInitStructure.TIM_ICFilter = 10; 
          TIM_ICInit(TIM8, &TIM_ICInitStructure);
          TIM_ClearFlag(TIM8, TIM_FLAG_Update); 
          TIM_ITConfig(TIM8, TIM_IT_Update, ENABLE); 
          TIM8->CNT = 30000;
          TIM_Cmd(TIM8, ENABLE);
      }
      
      //编码器采集定时器
      void TIM6_Init(u16 prd,u16 psc)
      {
      	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
      	NVIC_InitTypeDef NVIC_InitStructure;
      	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6,ENABLE);  
      	
        	TIM_TimeBaseInitStructure.TIM_Period = prd; 		//自动重装载值
      	TIM_TimeBaseInitStructure.TIM_Prescaler=psc;  		//定时器分频
      	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
      	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; 
      	
      	TIM_TimeBaseInit(TIM6,&TIM_TimeBaseInitStructure);
      	
      	NVIC_InitStructure.NVIC_IRQChannel=TIM6_DAC_IRQn; 
      	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x03; //抢占优先级1
      	NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x03; //子优先级3
      	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
      	NVIC_Init(&NVIC_InitStructure);
      	
      	TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE); 
      	TIM_Cmd(TIM6,ENABLE); 
      }
      
    2. 中断服务函数

      //编码器信息
      //编号0对应左边的电机,1对应右边电机
      //逆时针转计数增加
      uint16_t encoder_data[2];
      uint16_t encoder_data_last[2]={30000,30000};
      int16_t fric_spd[2];
      int16_t spd_last[2]={0,0};
      
      //编码器信息采集
      void TIM6_DAC_IRQHandler(void)
      {
      //	static int i=0;
      	if(TIM_GetITStatus(TIM6,TIM_IT_Update)==SET) //溢出中断
      	{
      //		if(i==100)//0.1s
      //			ANO_Send_UserData(RC_CtrlData.mouse.x,0,0,0);
      //		else if(i==200)
      //			ANO_Send_UserData(RC_CtrlData.mouse.x,1,0,0),i=0;
      //		i++;
      		
      		//LED1=!LED1;//DS1翻转
      		encoder_data[0]=TIM8->CNT;
      		encoder_data[1]=TIM2->CNT;
      		
      		for(int i=0;i<2;i++)
      		{
      			fric_spd[i]=encoder_data[i]-encoder_data_last[i];
      		
      			//从0到60000的跳变
      			if(fric_spd[i]>30000)
      			{
      				fric_spd[i]-=60000;
      			}
      			//从60000到0的跳变
      			else if(fric_spd[i]<-30000)
      			{
      				fric_spd[i]+=60000;
      			}
      			if(fric_spd[i]>700||fric_spd[i]<-700)
      				fric_spd[i]=spd_last[i];
      			else
      				spd_last[i]=fric_spd[i];
      			encoder_data_last[i]=encoder_data[i];
      		}
      		
      		ClientFrame.boolSet[0]=fabs(fric_spd[0])>70 ? 1:0;
      		ClientFrame.boolSet[1]=fabs(fric_spd[1])>70 ? 1:0;
      		
      		if(ClientFrame.boolSet[0] && ClientFrame.boolSet[1])
      			shooterEnable=1;
      		else
      			shooterEnable=0;
      	}
      	TIM_ClearITPendingBit(TIM6,TIM_IT_Update);  //清除中断标志位
      }
      
      
  • 94
    点赞
  • 461
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 80
    评论
STM32F407ZET6是STMicroelectronics推出的一款基于ARM Cortex-M4内核的32位微控制器。它具有高性能和丰富的外设资源,广泛应用于各种嵌入式系统中。 TIM4是STM32F407ZET6微控制器上的一个定时器模块,它可以用于测量时间、生成定时脉冲等应用。除了定时功能外,TIM4还集成了编码器模式,可以用于读取和解析编码器信号。 编码器是一种用于测量物理量或位置的装置。它通常由一个固定部分和一个旋转部分组成,旋转部分通过旋转来改变物理量或位置。编码器根据旋转部分的变化生成一个脉冲序列,这个脉冲序列可以用来测量旋转时间、计数旋转角度等信息。 在使用STM32F407ZET6的TIM4编码器模式时,我们可以配置TIM4定时器来读取编码器信号。具体步骤如下: 1. 配置GPIO引脚:编码器通常使用两个引脚,一个用于读取脉冲信号(例如PA0引脚),另一个用于读取方向信号(例如PA1引脚)。需要将这两个引脚配置为输入模式,并使能GPIO时钟。 2. 配置TIM4定时器:配置TIM4定时器为编码器模式,设置计数方向(正向递增或递减)、计数模式(四倍计数或正常计数)等参数。 3. 启动TIM4定时器:启动TIM4定时器开始计数。 4. 读取编码器值:通过读取TIM4的计数寄存器,可以得到编码器的当前值。根据TIM4的编码器模式和计数方向,可以计算出旋转的角度或物理量。 总之,使用STM32F407ZET6的TIM4编码器模式可以很方便地读取和解析编码器信号。这为测量物理量、计数旋转角度等应用提供了便利。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 80
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云端FFF

所有博文免费阅读,求打赏鼓励~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值