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旋转编码器使用总结的主要内容,如果未能解决你的问题,请参考以下文章
STM32F103 + 0.96/1.3“ I2C OLED + EC11旋转编码器多级菜单demo
ESP32 ESP-IDF开发环境搭建,Windows下基于ESP-IDF | Cmake | VScode插件的 ESP32 开发环境搭建