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

结果

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

结语

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

版权声明:本文为CSDN博主「_Hello_WXY」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_33315912/article/details/121870177

生成海报
点赞 0

_Hello_WXY

我还没有学会写个人说明!

暂无评论

相关推荐

K210应用5-使用中断方式通过UART接收数据

使用中断方式通过UART接收数据 实验目的本节实验目的为实现串口发送和接收。这一节计划采取中断的方式来实现串口接收,K210串口接收到0x00,则熄灭前节提到的红色LED灯,并通过串口打印Red Le

MCU串行通讯和并行通讯的区别以及UART的理解

假如我们需要从一个MCU发送一段数据到另一个MCU,我们可以选择两种通信方式,串行通信或者并行通信。 假如我们要发送的数据是数字198转化为二进制,就是11000110,如果使用串行通信

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

一、调试过程中遇到的问题 1.正反转判断方法。 2.旋转一格,输出多个脉冲。 二、硬件 主控:ESP32-S2 EC11旋转编码器 三、电路 接线说明: 1.三脚的一边,中间