当前位置:   article > 正文

江协科技STM32——旋转编码器计次(软件消抖)_旋转编码器消抖

旋转编码器消抖

一、旋转编码器介绍

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最小系统板
VCC3.3V
GNDGND
APB0
BPB1
CGND

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);	
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

4.注意事项

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

总结

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

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号