江协科技STM32旋转编码器计次及软件消抖原理详解

目录

  • 一、旋转编码器介绍
  • 1.原理简介
  • 2.波形图
  • 问题描述
  • 二、解决方案
  • 1.检测思路
  • 2.引脚连接
  • 3.关键代码展示(Encoder.c)
  • 4.注意事项
  • 总结
  • 一、旋转编码器介绍

    1.原理简介

    本实验使用的是EC11旋转编码器,这是一种增量式旋转编码器,拥有A、B、C三个输出通道,其中A、B两相输出正交信号,相位差为90°,C相输出零脉冲信号,用于标识位置。当编码器正转时,A相的输出信号超前B相90°;当编码器反转时,A相滞后B相90°。我们在程序中可以根据A、B两相信号输出的先后顺序,来判断旋转编码器是正转还是反转。

    2.波形图

    (1)正转、反转波形:

    理想的波形输出如上图所示(仅为示意图),A、B两相波形有一个90°的相位差。

    问题描述

    关于检测波形,江协科技有两个版本,第一个版本是课程视频中的方法:同时检测A、B两相的下降沿,若A相下降沿触发中断后,B相为低电平,则为反转;若B相下降沿触发中断后,A相为低电平,则为正转。第二个版本是文件资料中的代码:触发中断后,再次判断引脚电平来避免抖动。本人将两种方法均尝试后,发现转动编码器会经常发生误判,消抖效果不太理想。

    (2)实际波形示意图:

    在查找一些资料后发现旋转编码器确实存在抖动,具体可参考下面贴出的两篇文章。上图为一个简单的波形抖动示意图,如果用前文所述方法进行检测,确实会很容易发生误判。

    参考文章:
    EC11、EC16、ECxx旋转编码器按钮软件滤波程序滤除干扰杂波51单片机C程序
    详解EC11编码器示波器波形图

    二、解决方案

    1.检测思路

    第一种方法:在原代码中断服务函数的转动判断里,加一个很短的延时来实现消抖。但是我们一般不在中断函数里加延时,因为延时会占用CPU,对中断造成影响。这种方法虽然能解决抖动问题,但在编码器转动比较快时,容易漏判,不建议使用该方法。

    第二种方法:A相下降沿触发中断,在A相低电平期间死循环,直到A相恢复高电平后循环结束,然后通过检测B相在这期间产生了上升沿还是下降沿来判断正、反转。判断B相是上升沿还是下降沿的方法为:每次进入中断后立即保存B相的上一个状态(Last_status),同时在A相低电平期间更新B相的当前状态(Current_status),若上一个状态是高电平,而当前状态是低电平,则为下降沿;若上一个状态是低电平,而当前状态是高电平,则为上升沿。

    第三种方法:增加判断正、反转的条件,读取一个周期内的电平变化再进行判断。首先将最小系统板的PB0引脚与A相连接,触发方式选择上升/下降沿触发,用A相的输出信号来触发中断,然后在A相下降沿触发第一次中断后读取B相电平,紧接着A相上升沿触发第二次中断后读取B相电平,结合两次读取到的电平来判断是正转还是反转,这种检测方法和第二种方法的原理相同,即从A相的下降沿触发到上升沿触发期间,若B相电平发生了变化,则判定编码器转动,反之未转动,波形抖动时B相的电平保持不变,能够实现消抖。

    第四种方法:STM32有一个专门的编码器接口,定时器开启编码器模式,调用标准库中的TIM_EncoderInterfaceConfig()编码器函数进行参数的配置,然后通过TIM_GetCounter()函数直接读取计数值即可。编码器模式下计数器的计数方式如下图所示,有三种模式可选择。例如:第一种模式中,当TI1FP1为上升沿(Rising)时,若TI2FP2为高电平,则计数器向下计数,若TI2FP2为低电平,则计数器向上计数;而当TI1FP1为下降沿(Falling)时,若TI2FP2为高电平,则计数器向上计数,若TI2FP2为低电平,则计数器向下计数;TI2FP2的边沿触发不计数。这种计数方式下,毛刺信号的下降沿和上升沿产生的误判之间能够相互抵消。

    经测试,以上四种方法均能解决抖动问题,本文仅列出第三种方法的参考代码。

    2.引脚连接

    旋转编码器 STM32最小系统板
    VCC 3.3V
    GND GND
    A PB0
    B PB1
    C GND

    3.关键代码展示(Encoder.c)

    #include "stm32f10x.h"
    int16_t Encoder_Count, B_level, Cnt;
    
    void Encoder_Init(void)
    {
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);	
    	GPIO_InitTypeDef GPIO_InitStructure;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(GPIOB, &GPIO_InitStructure);
    	
    	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);	
    	EXTI_InitTypeDef EXTI_InitStructure;
    	EXTI_InitStructure.EXTI_Line = EXTI_Line0;
    	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
    	EXTI_Init(&EXTI_InitStructure);
    	
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	
    	NVIC_InitTypeDef NVIC_InitStructure;
    	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
    	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    	NVIC_Init(&NVIC_InitStructure);
    }
    
    int16_t Encoder_Get(void)
    {
    	int16_t Temp;
    	Temp = Encoder_Count;
    	Encoder_Count = 0;
    	return Temp;
    }
    
    void EXTI0_IRQHandler(void)
    {
    	if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0 && Cnt==0)//A相下降沿触发第一次中断
    	{
    		Cnt++;//计数值加一,表示已经触发了第一次中断
    		B_level=0;//读取B相电平,若为高电平则B_level置1,反之保持0
    		if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 1)
    		{
    			B_level=1;
    		}
    	}
    	if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 1 && Cnt==1)//A相上升沿触发第二次中断
    	{
            Cnt=0;//计数清零
    		if(B_level==1 && GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) 
    		{
    			Encoder_Count++;//正转
    		}
    		if(B_level==0 && GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 1)
    		{
    			Encoder_Count--;//反转
    		}	 
    	}	
    	EXTI_ClearITPendingBit(EXTI_Line0);	
    }
    
    

    4.注意事项

    在第三种方法中,中断服务函数中的if语句顺序只能是先检测A相的下降沿,再检测上升沿。如果是先检测A相的上升沿再检测下降沿,则转动编码器时就会出现单片机上电后第一次转动没反应,以及每次反方向旋转的第一次转动没反应的问题。这是因为旋转编码器转动一次,只会产生“一个波形”,而单片机上电后A、B相默认输出高电平,第一次转动后A相先产生下降沿(触发中断后不会进入if语句),再产生上升沿(触发中断后只进入第一个if语句,同时给Cnt和B_level赋值),这样产生的后果是,之后每次转动编码器,都是第一次下降沿触发中断后进入第二个if语句,然后第二次上升沿触发中断后进入第一个if语句,这样虽然也能计次,但程序的逻辑不合理,会出现转动编码器但不计次的问题。

    总结

    以上就是旋转编码器的简单介绍以及消抖方法的全部内容,希望对大家有所帮助!

    物联沃分享整理
    物联沃-IOTWORD物联网 » 江协科技STM32旋转编码器计次及软件消抖原理详解

    发表评论