关于STM32 EC11旋转编码器计数软件消除抖动的办法
关于STM32 EC11旋转编码器计数软件消除抖动的办法
开发板:STM32F103C8T6最小系统板
开发语言:C语言
前言:本人属于前端工程师自学STM32,因为有点空闲时间,就想着去学习一下其他方面的知识,而且本人对物联网比较感兴趣,所以就想着去学会使用STM32,然后再去DIY一些智能家居方面的东西,今天刚好学习到STM32旋转编码器计数,分享一下自己的学习心得给各位,感兴趣的可以看一下。
这个是我在网上买的EC11编码器模块,一共5个引脚,分别是S1,S2,KEY,VCC和GND。
当旋转编码器旋转时,S1、S2会产生相应的高低电平变化, KEY就是普通按键。
EC11旋转编码器介绍以及应用场景:
EC11旋转编码器也叫开关编码器、旋转编码器。其广泛用于车载DVD、车载导航、汽车影音上,也经常用于家用小家电产品的旋转开关上。EC11编码器在各类仪器设备中用于频率、音量、速度、温度、电压、菜单选择、光线强弱等参数的控制。EC11旋转编码器的调节轴有金属柄和塑胶柄,轴长区间9-30mm,调节轴有全柄、半柄、螺纹柄等。EC11系列编码器主要用于汽车电子、多媒体音响、仪器仪表、家用电器、智能家居、计算机周边、医疗器械等领域。主要用于频率调节、亮度调节、温度调节、音量调节的参数控制等。
工作原理: 旋转轴带动两组同轴栅格转盘,栅格转盘间存在一定的角度偏差,旋转的栅格会对发光二极管发出的光信号进行间断遮挡,从而使光敏二极管产生高低电平变化。因为两组栅格转盘存在一定的角度偏差,所以会产生两组相位90°偏差的A/B相信号。
输出信号图:
原来在B站看的教程的代码:
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);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_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);
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStructure);
}
void EXTI0_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line0) == SET)
{
/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
Encoder_Count --;
}
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
void EXTI1_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line1) == SET)
{
/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
{
Encoder_Count ++;
}
}
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
这个办法就是对AB相都进行下降沿中断处理,实际效果很不理想,得到的计数结果不对,计数会乱跳。
然后我在知乎看到了一篇不错的文章,文章中的办法不错,然后试了一下。
解决办法:A脚设置为上升下降沿均会进中断,下降上升一个变换周期,判断这个周期的A脚,B脚的始末状态,来判断正反转一次。
优点:只需要一个管脚的外部触发,另一脚单纯获取电平状态即可。
中断函数代码如下:
static uint8_t EC11_Flag = 0;//标志位
static uint8_t CW_1 = 0;
static uint8_t CW_2 = 0;
void EXTI0_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_Line0) == SET) {
if (EC11_Flag == 0 && GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) {
//下降沿
CW_1 = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1);
EC11_Flag = 1;
}
if (EC11_Flag == 1 && GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 1) {
//上升沿
CW_2 = !GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1);
//取反是因为 a相,b相必然异步,一高一低。
if (CW_1 == 1 && CW_2 == 1) {
Encoder_Count++;
}
if (CW_1 == 0 && CW_2 == 0) {
Encoder_Count--;
}
EC11_Flag = 0;
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
验证结果,不错,很稳:
本人是前端工程师,自学STM32,所以对单片机这一块的知识并不是很了解,有错误的地方,望各位不吝赐教,谢谢。