当前位置: 首页 > backend >正文

SOC-ESP32S3部分:12-2、编码器驱动

飞书文档https://x509p6c8to.feishu.cn/wiki/XtAhwrOuMigRxgkgSHZc8pd2nAd

编码器的驱动比较简单,我们在基础课程已经有学习过编码器的原理,我们这里就不重复讲解原理了,大家可以跳转32部分课程了解下原理,这里主要给大家演示下ESP32S3结合IO中断、消息队列实现的编码器方向和按下事件。

本节课,我们使用拨轮滚轮旋转编码器EC11E153440D,带大家了解下编码器如何驱动。

编码器有四根线,A\B是编码输出,S是按钮状态,C是公共端。

那么我们该怎样去使用这个编码器呢,从给出的数据手册上面我们看到一个波形,我们就是通过这个波形去判断编码器是否转动以及编码器转动的方向?

可以从图中可以看出当从CW方向转动的时候,A的波形上升沿比B波形的上升沿快,具体快多少,这里数据手册给出24±3°,当从CCW方向转动的这个时候恰好相反,B的相位上升沿快于A的上升沿。这样可以通过捕获上升沿的顺序来判断编码器的方向。

首先我们查看原理图,了解编码器的接线如下

编码器有三个引脚,A、B、SW,分别连接到GPIO38、GPIO39、GPIO40中

代码实现如下:

#include <stdio.h>
#include <unistd.h>
#include <sys/lock.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_timer.h"static const char *TAG = "example";#define GPIO_EN_A_IO 38
#define GPIO_EN_B_IO 39
#define GPIO_EN_SW_IO 40
#define GPIO_EN_A_PIN_SEL (1ULL << GPIO_EN_A_IO)
#define GPIO_EN_B_PIN_SEL (1ULL << GPIO_EN_B_IO)
#define GPIO_EN_SW_PIN_SEL (1ULL << GPIO_EN_SW_IO)static bool is_ccw_start = false;
static bool is_cw_start = false;#define DEBOUNCE_TIME_MS 100  // 去抖动时间(毫秒)
static uint64_t last_sw_interrupt_time = 0; //记录上次中断时间,用于去抖动typedef enum {ENCODER_EVENT_NONE = 0,ENCODER_EVENT_CW,ENCODER_EVENT_CCW,ENCODER_EVENT_CLICK,
} encoder_event_t;static QueueHandle_t queue = NULL;
static QueueHandle_t sw_queue = NULL;//设置ccw方向标志位,起始或结束标志
void set_ccw_status(){encoder_event_t event = ENCODER_EVENT_CCW;if(is_ccw_start == true){xQueueSendFromISR(queue, &event, NULL);}elseis_ccw_start = true; //首次触发为开始is_cw_start = false; //如果是CCW方向的触发,可以把CW方向的标志清空
}//设置cw方向标志位,起始或结束标志
void set_cw_status(){encoder_event_t event = ENCODER_EVENT_CW;if(is_cw_start == true){xQueueSendFromISR(queue, &event, NULL);}elseis_cw_start = true;is_ccw_start = false; //如果是CW方向的触发,可以把CCW方向的标志清空
}static void IRAM_ATTR gpio_isr_handler(void* arg)
{uint32_t gpio_num = (uint32_t) arg;if(gpio_num == GPIO_EN_A_IO)  //判断中断来自哪一条中断线{//进入中断之后,是低电平,那就是下降沿if(gpio_get_level(GPIO_EN_A_IO) == 0){if(gpio_get_level(GPIO_EN_B_IO) == 0){//CCW方向set_ccw_status();}else{//CW方向set_cw_status();}}//进入中断之后,是高电平,那就是上升沿else{if(gpio_get_level(GPIO_EN_B_IO) == 0){//CW方向set_cw_status();}else{//CCW方向set_ccw_status();}}}
}/* 外部中断服务函数定义 */
static void IRAM_ATTR sw_gpio_isr_handler(void* arg)
{uint64_t now = esp_timer_get_time() / 1000; // 获取当前时间(毫秒)// 硬件去抖: 检查是否在去抖时间窗口内if ((now - last_sw_interrupt_time) < DEBOUNCE_TIME_MS) {return;}last_sw_interrupt_time = now;encoder_event_t event = ENCODER_EVENT_CLICK;xQueueSendFromISR(sw_queue, &event, NULL);
}static void gpio_task_example(void* arg){encoder_event_t event;while (1){if(xQueueReceive(sw_queue, &event, 0)){ESP_LOGI(TAG, "按钮按下");}if(xQueueReceive(queue, &event, 0)){if(event == ENCODER_EVENT_CW) {ESP_LOGI(TAG, "顺时针旋转");} else if(event == ENCODER_EVENT_CCW) {ESP_LOGI(TAG, "逆时针旋转");}}vTaskDelay(10 / portTICK_PERIOD_MS);}
}void app_main(void)
{// 初始化IO SW为中断输入gpio_config_t io_conf = {};io_conf.intr_type = GPIO_INTR_NEGEDGE;io_conf.pin_bit_mask = GPIO_EN_SW_PIN_SEL;io_conf.mode = GPIO_MODE_INPUT;io_conf.pull_up_en = GPIO_PULLUP_ENABLE;gpio_config(&io_conf);// 初始化IO A为中断输入io_conf.intr_type = GPIO_INTR_NEGEDGE;io_conf.pin_bit_mask = GPIO_EN_A_PIN_SEL;io_conf.mode = GPIO_MODE_INPUT;io_conf.pull_up_en = GPIO_PULLUP_DISABLE;gpio_config(&io_conf);// 初始化IO B为普通输入io_conf.intr_type = GPIO_INTR_DISABLE;io_conf.pin_bit_mask = GPIO_EN_B_PIN_SEL;io_conf.mode = GPIO_MODE_INPUT;io_conf.pull_up_en = GPIO_PULLUP_DISABLE;gpio_config(&io_conf);gpio_install_isr_service(0);//注册IO SW和 IO A的中断回调函数gpio_isr_handler_add(GPIO_EN_SW_IO, sw_gpio_isr_handler, (void*)GPIO_EN_SW_IO);gpio_isr_handler_add(GPIO_EN_A_IO, gpio_isr_handler, (void*)GPIO_EN_A_IO);queue = xQueueCreate(10, sizeof(uint32_t));sw_queue = xQueueCreate(10, sizeof(uint32_t));xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 1, NULL);while(1){vTaskDelay(10 / portTICK_PERIOD_MS);}
}
http://www.xdnf.cn/news/8863.html

相关文章:

  • 使用JSP踩过的坑
  • 《算法笔记》12.2小节——字符串专题->KMP算法 问题 C: 剪花布条
  • 事务操作语句
  • ModbusRTU转profibusDP网关与电动机保护器通讯案例
  • 【操作系统】-4.3.1文件的层次结构
  • Linux驱动学习笔记(九)
  • Vue 3 (2) 模块化开发入门教程(ESM方式)
  • 32-低功耗与钩子函数
  • 人工智能数学基础实验(四):最大似然估计的-AI 模型训练与参数优化
  • 电路图识图基础知识-回路编号及代号(四)
  • 微信小程序常用方法
  • QListWidgetItem的函数介绍
  • Leetcode刷题 | Day67_图论12_Floyd算法 / A*算法
  • Kafka Consumer工作流程
  • 大数据治理:大数据环境下协同办公系统的数据串联与深度分析
  • python打卡训练营打卡记录day36
  • Windows系统部署Redis
  • Spring Cloud项目登录认证从JWT切换到Redis + UUID Token方案
  • 阿里千问系列:Qwen3技术报告解读(上)
  • 力扣第451场周赛
  • Linux基本指令篇 —— cd指令
  • 练习实践--deepseek的使用环境搭建回顾--火山方舟
  • (每日一道算法题)实现 pow(x, n) 的快速幂解法
  • 本地处理 + GPU 加速 模糊视频秒变 4K/8K 修复视频老旧素材
  • 前端协同文档实现思路
  • LLaMA-Factory 微调模型与训练数据量对应关系
  • 【每日一题 | 2025年5.19 ~ 5.25】动态规划相关题
  • 篇章一 数据结构——前置知识(一)
  • Java 类加载机制详解
  • 【SCL编程案例】1-16整数的随机排列