[单片机]旋转编码器

实物图

 

引脚功能

时序图

 

防抖动

因为是两个金属片来回摩擦触碰, 产生的高低电平信号, 会产生抖动, 如果是使用外部中断触发或者计数器中断触发, 将会面临很大的问题, 就是转动一次将会产生很多的抖动信号. 解决方法有硬件防抖, 还有软件防抖.

硬件防抖动

在clk和dt两根引脚分别串联两个小电容, 实现滤波的功能.

软件防抖

因为购买的模块一般都是没有电容的, 所以需要我们在程序中实现防抖动, 有以下方法

1. 中断中加延时函数: 最简单粗暴的方法, 但是不可以的. 首先在中断里面加延时函数是会有风险的, 随着代码体量增大, 用到的传感器数量增多, 程序容易卡死在中断函数里面. 另一方面, 实际操作下来发现效果并不好, 因为具有了延时, 快速旋转将会体验非常卡顿而且还会出现大量的错误.

2.  双中断: 因为两个引脚分别产生波形, 所以在两根引脚上分别让他产生中断, 当两个中断均触发时, 判断为旋转了一次. 这个思想非常好, 避免了在中断中写延时函数, 而且效果提升了很多, 但是快速旋转, 偶尔也会出现问题. 这时候就有了第三个想法.

3. 双中断+记录3次中断为旋转一次: 简单的纠错程序, 因为连续三次判断错误的概率是比较低的, 基本不会出现问题.

参考代码(基于stm32f407)

//初始化旋转编码器
void encoder_init(void)
{
    //配置PA4外部中断,PA3为外部中断
    //使能时钟
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_EXTIT, ENABLE);

    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStruct.GPIO_Speed = GPIO_High_Speed;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; 
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource4);
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource3);
    
    //配置外部中断
    EXTI_InitTypeDef EXTI_InitStruct;
    EXTI_InitStruct.EXTI_Line = EXTI_Line4 | EXTI_Line3;
    EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿有效
    EXTI_InitStruct.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStruct);

    //配置NVIC, 抢占优先级和响应优先级最高.
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置优先级分组
    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = EXTI4_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
    
    NVIC_InitStruct.NVIC_IRQChannel = EXTI3_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
}

enum Direction
{
    JUMP,
    RIGHT,
    LIFT,
};

static uint8_t flag = JUMP;

void EXTI3_IRQHandler(void)
{
    if(EXTI_GetFlagStatus(EXTI_Line3) == SET)
    {
        if(PAin(4) == 0 && flag == RIGHT) //右转
        {
            num++;
            flag = JUMP;
        }
        else if(PAin(4) == 1 && flag == LIFT) //左转
        {
            num--;
            flag = JUMP;
        }
        
        EXTI_ClearITPendingBit(EXTI_Line3);//清空中断标志位
    }
}

void EXTI4_IRQHandler(void)
{    
    if(EXTI_GetFlagStatus(EXTI_Line4) == SET)
    {
        if(PAin(3) == 1) //右转
        {
            flag = RIGHT;
        }
        else if(PAin(3) == 0) //左转
        {
            flag = LIFT;
        }
        
        EXTI_ClearITPendingBit(EXTI_Line4);//清空中断标志位
    }
}

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是51单片机旋转编码器驱动程序的示例代码: ```c #include <reg52.h> sbit pinA = P1^0; // 定义旋转编码器A相引脚 sbit pinB = P1^1; // 定义旋转编码器B相引脚 unsigned char code table[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f}; // 数码管显示表 unsigned char count = 0; // 计数器 void delay(unsigned int xms) // 延时函数 { unsigned int i, j; for(i = xms; i > 0; i--) for(j = 112; j > 0; j--); } void display(unsigned char num) // 数码管显示函数 { P0 = table[num]; } void main() { pinA = 1; pinB = 1; while(1) { if(!pinA) // 如果A相为低电平 { delay(5); // 延时去抖动 if(!pinA) // 确认A相为低电平 { if(!pinB) // 如果B相也为低电平,逆时针旋转 { count--; if(count == 0xff) // 如果计数器减到0,重新从9开始 count = 9; display(count); // 数码管显示计数器的值 } else // 如果B相为高电平,顺时针旋转 { count++; if(count == 10) // 如果计数器加到10,重新从0开始 count = 0; display(count); // 数码管显示计数器的值 } while(!pinA); // 等待A相恢复高电平 } } } } ``` 这段代码实现了一个旋转编码器的计数功能,当旋转编码器顺时针旋转时,计数器加一并显示在数码管上;当旋转编码器逆时针旋转时,计数器减一并显示在数码管上。需要注意的是,编码器的A相和B相需要通过外部电路连接到51单片机的两个引脚上,这里假设A相连接到P1.0引脚,B相连接到P1.1引脚。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值