ESP32(IDF)EC11旋转编码器使用总结

本文详细讲述了ESP32-S2主控与EC11旋转编码器配合的调试过程,包括遇到的正反转判断问题、硬件连接和工作原理。作者尝试了三种方法解决电平检测延迟问题,最终采用双中断检测法准确判断正反转。适合嵌入式开发者深入理解编码器应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、调试过程中遇到的问题

1.正反转判断方法。
2.旋转一格,输出多个脉冲。

二、硬件

主控:ESP32-S2
在这里插入图片描述
EC11旋转编码器
在这里插入图片描述

三、电路

在这里插入图片描述

接线说明:

1.三脚的一边,中间脚接GND,两侧脚接中断I/O。
2.两脚一边,相当于一个按键,按下后两脚导通,所以一脚上拉接I/O,另一脚接GND,正常I/O为高电平,按下后变成低电平,由此来判断是否按下。

四、工作原理

旋转时,编码器会输出脉冲波形。正反转的波形时序不同,以此来判断正反转。

正转波形

黄色为5脚(DT),蓝色为3脚(CLK)
在这里插入图片描述

反转波形

在这里插入图片描述

五、判断正反转

法一

网上找的方法最简单的是,以DT或者CLK一脚的脉冲为中断信号,发生中断后去读另一脚的电平状态,通过另一脚的电平高低状态判断正反转。我也试了该方法。

我是以蓝色(CLK)作为中断信号,发生上升沿中断后去读黄色波形电平,黄色为高电平,则正转。黄色为低电平,则反转。

代码实现

配置上升沿中断,启动一个任务一直等待检测消息队列中是否有新事件,中断函数中向消息队列添加事件。
#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#define EC11_GPIO_SCL 10
#define EC11_GPIO_DAT 11
static xQueueHandle gpioEventQueue = NULL;

static void IRAM_ATTR gpio_isr_handler(void* arg)
{
    uint32_t gpio_num = (uint32_t) arg;
    xQueueSendFromISR(gpioEventQueue, &gpio_num, NULL);
}

static void gpio_task_example(void* arg)
{
    uint32_t io_num;
    for(;;) {
        if(xQueueReceive(gpioEventQueue, &io_num, portMAX_DELAY)) {
            if(gpio_get_level(EC11_GPIO_DAT))
            {
            	printf("+ turn\n");
			}
			else
				 printf("- turn\n");          
    }
}

void app_main(void)
{
    printf("Hello EC11!\n");
    gpio_config_t gpio_10 = {//IO配置为中断
        .pin_bit_mask = 1ULL << EC11_GPIO_SCL,
        .mode = GPIO_MODE_INPUT,
        .intr_type = GPIO_INTR_POSEDGE,//上升沿
        .pull_up_en = 1,//上拉
    };
    gpio_config(&gpio_10);//配置
    gpioEventQueue = xQueueCreate(10, sizeof(uint32_t));
    xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 1, NULL);    
    gpio_install_isr_service(0);
    gpio_isr_handler_add(EC11_GPIO_SCL, gpio_isr_handler, (void*)EC11_GPIO_SCL);
    while(1)
    {
        vTaskDelay(5000 / portTICK_PERIOD_MS);
    }
}

结果

正反转不能准确判断,个人觉得是因为发生中断后去读另一个io电平不够及时。黄色(DT)脉冲低电平只持续2ms,而中断发生后是向消息队列中写事件,然后再在事件处理任务中检测正反转,这期间的任务切换估计花费的时间较长,导致读取到的电平不准确。(完全个人凭经验猜测,如果不对,请指正)

法二

不用消息队列,在中断函数中直接去读取电平。(貌似是中断函数中不能打印log,才使用消息队列的),因为我没有其他外设来展现正反转,所以必须打印,该方法没有亲自实验,如果有大佬实验了,请告知是否可行。

法三

我使用的另一种方法是,两脚都接中断,检测下降沿,根据两引脚中断的先后顺序,判断正反转。

代码实现

创建一个定时清标志位的任务,既可以复位标志,又可以控制旋转检测的频率(转好几格都按一次处理)。
#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#define EC11_GPIO_SCL 10
#define EC11_GPIO_DAT 11
static u_int8_t count = 0;
static xQueueHandle gpioEventQueue = NULL;

static void time_out_task(void *arg)
{
    vTaskDelay(200 / portTICK_PERIOD_MS);
    count = 0;
    vTaskDelete(NULL);
}

static void IRAM_ATTR gpio_isr_handler(void* arg)
{
    uint32_t gpio_num = (uint32_t) arg;
    xQueueSendFromISR(gpioEventQueue, &gpio_num, NULL);
}

static void gpio_task_example(void* arg)
{
    uint32_t io_num;
    for(;;) {
        if(xQueueReceive(gpioEventQueue, &io_num, portMAX_DELAY)) {
            // printf("GPIO[%d] intr, val: %d\n", io_num, gpio_get_level(io_num));
            if(io_num == EC11_GPIO_SCL)
            {                
                if(count == 1)
                {
                    count = 3;
                    printf("+ turn\n");
                }
                else if(count == 0)
                {
                    count = 2;
                    xTaskCreate(time_out_task, "time_out_task", 2048, NULL, 2, NULL);
                }                                  
            } 
            else if(io_num == EC11_GPIO_DAT)
            {
                if(count == 2)
                {
                    count = 3;
                    printf("- turn\n");
                }
                else if(count==0){
                    count = 1;
                    xTaskCreate(time_out_task, "time_out_task", 2048, NULL, 2, NULL);
                }
            }           
        }
    }
}

void app_main(void)
{
    printf("Hello EC11!\n");
    gpio_config_t gpio_10 = {
        .pin_bit_mask = 1ULL << EC11_GPIO_SCL,
        .mode = GPIO_MODE_INPUT,
        .intr_type = GPIO_INTR_NEGEDGE,
        .pull_up_en = 1,
    };
    gpio_config_t gpio_11 = {
        .pin_bit_mask = 1ULL << EC11_GPIO_DAT,
        .mode = GPIO_MODE_INPUT,
        .intr_type = GPIO_INTR_NEGEDGE,
        .pull_up_en = 1,
    };
    gpio_config(&gpio_10);
    gpio_config(&gpio_11);
    gpioEventQueue = xQueueCreate(10, sizeof(uint32_t));
    xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 1, NULL);    
    gpio_install_isr_service(0);
    gpio_isr_handler_add(EC11_GPIO_SCL, gpio_isr_handler, (void*)EC11_GPIO_SCL);
    gpio_isr_handler_add(EC11_GPIO_DAT, gpio_isr_handler, (void*)EC11_GPIO_DAT);
    while(1)
    {
        vTaskDelay(5000 / portTICK_PERIOD_MS);
    }
}

结果

在这里插入图片描述
判断非常准确,快速旋转时,可能转好几格都只按一次旋转处理了,可以调超时任务的处理周期解决。

结语

如果该文章对你有所帮助,希望能一件三连。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

_Hello_WXY

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值