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

Posted _Hello_WXY

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ESP32(IDF)EC11旋转编码器使用总结相关的知识,希望对你有一定的参考价值。

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

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

结果


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

结语

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

以上是关于ESP32(IDF)EC11旋转编码器使用总结的主要内容,如果未能解决你的问题,请参考以下文章

ESP32蓝牙+EC11旋转编码器实现对电脑音量控制

STM32F103 + 0.96/1.3“ I2C OLED + EC11旋转编码器多级菜单demo

ESP32 ESP-IDF开发环境搭建,Windows下基于ESP-IDF | Cmake | VScode插件的 ESP32 开发环境搭建

ESP32-IDF编译问题

ESP32-IDF 05-3 WIFI-esp32获取网络时间

了解EC11旋转编码器,编写EC11旋转编码器驱动程序。