ESP32——PCNT 驱动EC11

ESP32与EC11旋转编码器的奇妙配对:转啊转,亮起来! 详细介绍了EC11旋转编码器的工作原理及接口特点解析了ESP32如何处理编码器的旋转和按键信号展示了使用EC11控制LED亮度及开关的完整实现提供了抗抖动和中断处理的优化方案与代码解析。 阅读详情
AI摘要

文章介绍了在ESP32S3芯片上使用PCNT模块与EC11编码器进行交互,以解析旋转脉冲信号。原示例中,每个完整旋转会使计数器增加/减少4,但作者修改了代码,使得每旋转一格计数器加1。文章还包含了库的使用、滤波配置以及中断服务的安装和管理。

芯片:esp32s3

开发环境:espidfv4.4

一、官网源码

1、readme:ec11旋转脉冲样式 GPIO14 gpio15

```

A +-----+ +-----+ +-----+

| | | |

| | | |

+-----+ +-----+

B +-----+ +-----+ +-----+

| | | |

| | | |

+-----+ +-----+

 

+--------------------------------------->

CW direction

```

嘛意思:一会来看

该示例使4X模式能够解析旋转信号,这意味着,根据旋转方向,每个完整的旋转步骤将导致PCNT计数器增加/减少4。

2)这是有编码器的库,先编译看看结果,代码倒是非常少;乐鑫放在component里

头文件:#include "rotary_encoder.h"

3)修改了下,改为旋转一格加1,原本的为旋转一个变化4,代码放下面,加注释了,还是很容易看懂的,配合ec11的时序图更容易理解哦。.h没改。还是有很多不妥的地方,容易误判,后面再改

ESP32S3学习——pcnt_rotarty_encoder

芯片:esp32s3

开发环境:espidfv4.4

一、官网源码

1、readme:ec11旋转脉冲样式 GPIO14 gpio15

```

A +-----+ +-----+ +-----+

| | | |

| | | |

+-----+ +-----+

B +-----+ +-----+ +-----+

| | | |

| | | |

+-----+ +-----+

 

+--------------------------------------->

CW direction

```

嘛意思:一会来看

该示例使4X模式能够解析旋转信号,这意味着,根据旋转方向,每个完整的旋转步骤将导致PCNT计数器增加/减少4。

2)这是有编码器的库,先编译看看结果,代码倒是非常少;乐鑫放在component里

头文件:#include "rotary_encoder.h"

3)修改了下,改为旋转一格加1,原本的为旋转一个变化4,代码放下面,加注释了,还是很容易看懂的,配合ec11的时序图更容易理解哦。.h没改。还是有很多不妥的地方,容易误判,后面再改

 

 

1eccb48367d94a8bba51fbafc8bfae32.png

 

 

 

// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include <string.h>
#include <sys/cdefs.h>
#include "esp_compiler.h"
#include "esp_log.h"
#include "driver/pcnt.h"
#include "sys/lock.h"
#include "hal/pcnt_hal.h"
#include "rotary_encoder.h"

static const char *TAG = "rotary_encoder";

#define ROTARY_CHECK(a, msg, tag, ret, ...)                                       \
    do {                                                                          \
        if (unlikely(!(a))) {                                                     \
            ESP_LOGE(TAG, "%s(%d): " msg, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
            ret_code = ret;                                                       \
            goto tag;                                                             \
        }                                                                         \
    } while (0)

#define EC11_PCNT_DEFAULT_HIGH_LIMIT (100)
#define EC11_PCNT_DEFAULT_LOW_LIMIT  (-100)

// A flag to identify if pcnt isr service has been installed.
//中断服务是否安装标志位
static bool is_pcnt_isr_service_installed = false;
// A lock to avoid pcnt isr service being installed twice in multiple threads.
// 防止中断服务被多次安装
static _lock_t isr_service_install_lock;
#define LOCK_ACQUIRE() _lock_acquire(&isr_service_install_lock)
#define LOCK_RELEASE() _lock_release(&isr_service_install_lock)
// ec11结构体,计数数目,父对象,pcnt单元
typedef struct {
    int accumu_count;
    rotary_encoder_t parent;
    pcnt_unit_t pcnt_unit;
} ec11_t;
// 滤波
static esp_err_t ec11_set_glitch_filter(rotary_encoder_t *encoder, uint32_t max_glitch_us)
{
    esp_err_t ret_code = ESP_OK;
    ec11_t *ec11 = __containerof(encoder, ec11_t, parent);

    /* Configure and enable the input filter */
    // 配置使能pcnt滤波
    ROTARY_CHECK(pcnt_set_filter_value(ec11->pcnt_unit, max_glitch_us * 80) == ESP_OK, "set glitch filter failed", err, ESP_FAIL);
    // 滤波时间有值,使能滤波
    if (max_glitch_us) {
        pcnt_filter_enable(ec11->pcnt_unit);
    } else {
        pcnt_filter_disable(ec11->pcnt_unit);
    }

    return ESP_OK;
err:
    return ret_code;
}
// 开启计数
static esp_err_t ec11_start(rotary_encoder_t *encoder)
{
    // 这个嘛意思,不造
    ec11_t *ec11 = __containerof(encoder, ec11_t, parent);
    // pcnt api
    pcnt_counter_resume(ec11->pcnt_unit);
    return ESP_OK;
}
// 停止计数
static esp_err_t ec11_stop(rotary_encoder_t *encoder)
{
    ec11_t *ec11 = __containerof(encoder, ec11_t, parent);
    pcnt_counter_pause(ec11->pcnt_unit);
    return ESP_OK;
}
// 获取计数值
static int ec11_get_counter_value(rotary_encoder_t *encoder)
{
    ec11_t *ec11 = __containerof(encoder, ec11_t, parent);
    int16_t val = 0;
    pcnt_get_counter_value(ec11->pcnt_unit, &val);
    return val + ec11->accumu_count;
}
// 删除ec11
static esp_err_t ec11_del(rotary_encoder_t *encoder)
{
    ec11_t *ec11 = __containerof(encoder, ec11_t, parent);
    free(ec11);
    return ESP_OK;
}
//pcnt移除处理函数
static void ec11_pcnt_overflow_handler(void *arg)
{
    ec11_t *ec11 = (ec11_t *)arg;
    uint32_t status = 0;
    //获取pcnt状态
    pcnt_get_event_status(ec11->pcnt_unit, &status);
//max:计数值累加
    if (status & PCNT_EVT_H_LIM) {
        ec11->accumu_count += EC11_PCNT_DEFAULT_HIGH_LIMIT;
    } else if (status & PCNT_EVT_L_LIM) {
        ec11->accumu_count += EC11_PCNT_DEFAULT_LOW_LIMIT;
    }
}
//创建ec11
esp_err_t rotary_encoder_new_ec11(const rotary_encoder_config_t *config, rotary_encoder_t **ret_encoder)
{
    esp_err_t ret_code = ESP_OK;
    ec11_t *ec11 = NULL;

    ROTARY_CHECK(config, "configuration can't be null", err, ESP_ERR_INVALID_ARG);
    ROTARY_CHECK(ret_encoder, "can't assign context to null", err, ESP_ERR_INVALID_ARG);
    //申请空间
    ec11 = calloc(1, sizeof(ec11_t));
    ROTARY_CHECK(ec11, "allocate context memory failed", err, ESP_ERR_NO_MEM);
    //pcnt单元
    ec11->pcnt_unit = (pcnt_unit_t)(config->dev);
    
    // Configure channel 0
    // 通道0配置,输入14,控制15
    pcnt_config_t dev_config = {
        .pulse_gpio_num = config->phase_a_gpio_num,
        .ctrl_gpio_num = config->phase_b_gpio_num,
        .channel = PCNT_CHANNEL_0,
        .unit = ec11->pcnt_unit,
        .pos_mode = PCNT_COUNT_DIS,//上升沿减少
        .neg_mode = PCNT_COUNT_INC,//下降沿增加
        .lctrl_mode = PCNT_MODE_REVERSE,//控制低电平反转
        .hctrl_mode = PCNT_MODE_KEEP,//高电平保持
        .counter_h_lim = EC11_PCNT_DEFAULT_HIGH_LIMIT,
        .counter_l_lim = EC11_PCNT_DEFAULT_LOW_LIMIT,
    };
    ROTARY_CHECK(pcnt_unit_config(&dev_config) == ESP_OK, "config pcnt channel 0 failed", err, ESP_FAIL);

    // Configure channel 1
    // dev_config.pulse_gpio_num = config->phase_b_gpio_num;
    // dev_config.ctrl_gpio_num = config->phase_a_gpio_num;
    // dev_config.channel = PCNT_CHANNEL_1;
    // dev_config.pos_mode = PCNT_COUNT_INC;
    // dev_config.neg_mode = PCNT_COUNT_DEC;
    // ROTARY_CHECK(pcnt_unit_config(&dev_config) == ESP_OK, "config pcnt channel 1 failed", err, ESP_FAIL);

    // PCNT pause and reset value
    // 清零计数值
    pcnt_counter_pause(ec11->pcnt_unit);
    pcnt_counter_clear(ec11->pcnt_unit);


    // register interrupt handler in a thread-safe way
    // 安装,添加中断回调
    LOCK_ACQUIRE();
    if (!is_pcnt_isr_service_installed) {
        ROTARY_CHECK(pcnt_isr_service_install(0) == ESP_OK, "install isr service failed", err, ESP_FAIL);
        // make sure pcnt isr service won't be installed more than one time
        is_pcnt_isr_service_installed = true;
    }
    LOCK_RELEASE();

    pcnt_isr_handler_add(ec11->pcnt_unit, ec11_pcnt_overflow_handler, ec11);
// 使能maxmin
    pcnt_event_enable(ec11->pcnt_unit, PCNT_EVT_H_LIM);
    pcnt_event_enable(ec11->pcnt_unit, PCNT_EVT_L_LIM);

    ec11->parent.del = ec11_del;
    ec11->parent.start = ec11_start;
    ec11->parent.stop = ec11_stop;
    ec11->parent.set_glitch_filter = ec11_set_glitch_filter;
    ec11->parent.get_counter_value = ec11_get_counter_value;

    *ret_encoder = &(ec11->parent);
    return ESP_OK;
err:
    if (ec11) {
        free(ec11);
    }
    return ret_code;
}

 

 

Esp32使用pcnt模块与电磁编码器实例 ESP32 PCNT (Pulse Counter) 是 ESP32 芯片中的一个硬件模块,用于计数外部信号的脉冲数量。它可以用于测量外部设备发送的脉冲信号,如旋转编码器、计数器、计时器等。ESP32 PCNT 模块拥有 8 个可编程的计数器通道,并且每个通道都可以独立地配置和管理。该模块具有硬件滤波器,能够过滤掉低于一定时间阈值的噪声和杂波。此外,ESP32 PCNT 还具有可编程阈值比较器,可以在计数器达到预设阈值时发出中断信号,从而方便地实现计数器中断处理。 阅读详情

相关推荐

ESP32-S2 PWM输入捕获

ESP32-S2 PWM输入捕获

李法师的博客 5579

ESP32 PULSE CNT脉冲计数器使用(简单DEMO)

ESP32 PULSE CNT脉冲计数器使用,他的功能很简单就是实现脉冲个数的计数。

避坑指南:ESP32-S3的PCNT模块配置EC11编码器时这3个参数千万别设错

本文深入解析ESP32-S3的PCNT模块配置EC11编码器时的三大核心参数,包括边沿检测模式、控制模式及极限值与滤波参数的设置。通过实战案例和示波器波形分析,揭示常见配置错误及解决方案,帮助开发者避免计数误差,提升编码器应用的稳定性和精度。

weixin_28693683的博客 359

ESP32与EC11旋转编码器控制LED[项目代码]

本文详细介绍了EC11旋转编码器的工作原理及其与ESP32的配对使用。EC11作为一种数字旋转编码器,能够将旋转运动转化为数字信号,并通过A、B引脚输出相位差为90°的方波信号。文章提供了ESP32与EC11的接线方案,包括引脚连接和上拉电阻的使用。此外,还展示了完整的Arduino代码,实现了通过EC11旋转控制LED亮度和按键控制LED开关的功能。代码解析部分详细说明了中断处理、防抖动技术以及PWM控制LED亮度的实现方法。最后,文章还提供了进阶优化技巧,如加速度感应、非线性亮度变化和长按功能,以及常见问题的解决方案。

ESP32-S3(ESP-IDF)实现EC11编码器双通道脉冲计数:从硬件配置到中断优化

本文详细介绍了如何在ESP32-S3平台上,利用ESP-IDF的PCNT硬件模块实现EC11旋转编码器的双通道脉冲计数。通过深入解析PCNT模块的硬件结构与配置逻辑,提供了从硬件接线、双通道初始化、数字滤波到中断优化的完整实战指南。重点阐述了如何通过配置两个通道实现4倍频计数以提升精度,并分享了中断服务程序优化、单/双边沿模式切换等进阶技巧,帮助开发者高效、稳定地将编码器应用于智能家居、电机控制等项目。

gan8painter的博客 393

ESP32软件解码旋转编码器库:无PCNT芯片的四象限信号处理方案

旋转编码器是嵌入式人机交互中常用的位置/方向传感元件,其核心输出为A/B两路正交(Quadrature)方波信号。软件解码通过GPIO中断与有限状态机实现边沿检测与方向判别,规避了硬件脉冲计数器(PCNT)外设依赖,在ESP32-C2、ESP32-C3等无PCNT的低成本SoC上尤为关键。该方法兼顾低资源占用与工程鲁棒性,适用于EC11等机械式编码器的低速场景(≤15 RPM),广泛用于UI导航、参数调节、菜单切换等IoT终端交互。ESP32_Knob正是面向此类需求设计的轻量级Arduino库,提供事件驱

weixin_42601608的博客 597

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

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

perseverance51博客 5114

ESP32驱动旋转编码器:从原理到物联网应用实战

旋转编码器作为一种经典的人机交互输入设备,其核心原理基于A、B两相信号的相位差判断旋转方向,通过光电或机械开关产生脉冲序列。在嵌入式系统中,可靠的编码器解码涉及消抖处理、中断响应和计数逻辑等关键技术,直接影响交互的精确性和实时性。在物联网和智能硬件领域,这种旋钮式输入为音量调节、菜单导航和参数设置提供了直观的物理反馈,极大地提升了用户体验。本文聚焦于ESP32平台,深入探讨如何利用其GPIO中断和硬件PCNT外设,高效稳定地读取旋转编码器信号,并结合Wi-Fi/蓝牙功能,将其应用于智能家居控制面板等实际场景

weixin_30363509的博客 365

ESP32S3学习——Pulse Counter (PCNT)脉冲计数

ESP32S3学习——Pulse Counter (PCNT)脉冲计数

HandBAI的博客 6415

EC11旋转编码器从原理到实战:硬件设计、软件驱动与调试经验

旋转编码器是嵌入式系统中常用的人机交互元件,通过正交信号实现方向判断与位置计数。其核心原理基于两路相位相差90度的方波信号,经过状态机或中断处理后可实现倍频计数。在实际工程中,机械触点带来的抖动是导致计数错误的主要因素,通常采用RC滤波与软件状态机结合的方式解决。该技术广泛应用于音量调节、菜单导航、3D打印机对刀等场景。本文以EC11为例,详细讲解其内部结构、上拉电阻与去抖电容的选型方法,并对比定时扫描、中断驱动及ESP32 PCNT外设等多种软件实现方案,同时总结数字漂移、按键误触等常见问题的排查经验,帮

weixin_32239523的博客 123

ESP32-S3(ESP-IDF)实现EC11编码器双通道脉冲计数:从硬件配置到中断优化

本文详细介绍了如何在ESP32-S3平台上,利用ESP-IDF的PCNT硬件模块实现EC11旋转编码器的双通道脉冲计数。通过对比GPIO中断方案的不足,阐述了PCNT模块在稳定性、准确性和CPU资源节省方面的核心优势。文章从硬件接线、PCNT工作原理讲起,逐步深入至双通道配置、中断优化及硬件滤波等实战技巧,提供了完整的代码示例与调试方法,帮助开发者高效、可靠地处理编码器信号。

qsc90123456的博客 443

ESP32与EC11的智能互动:从旋转编码到LED控制的魔法之旅

本文详细介绍了如何利用ESP32微控制器驱动EC11旋转编码器实现精准的LED亮度控制。通过解析EC11的相位差信号原理,结合ESP32的GPIO中断与PWM功能,提供了从硬件接线、核心代码到进阶优化的完整方案,包括防抖处理、速度感应调节及伽马校正,旨在打造流畅的智能硬件交互体验。

rgv2345678的博客 189

EC11旋转编码器与ESP32的PWM控制实战:从旋钮到数字交互

旋转编码器作为一种增量式位置传感器,通过正交脉冲信号精确感知旋转方向与步数,相比传统电位器具有无限旋转、无磨损、支持按键确认等优势。其输出信号经消抖与方向判定后,可映射为PWM占空比,从而实现对LED亮度、电机转速、舵机角度等执行器的精细调节。以EC11编码器与ESP32主控为例,介绍硬件接线、上拉电阻原理、中断读取方式以及LEDC外设配置方法,并给出完整Arduino代码示例。结合加速度响应、屏幕显示、联网控制等扩展思路,帮助读者构建从机械旋钮到数字控制的完整链路。该方案适用于音量调节、调光灯、电机调速及

weixin_29196315的博客 270

【ESP32学习-4】外设

ESP32 驱动的逻辑分层 以及各种外设学习中的疑问 如PCNT等

csdndulala的博客 1818

LVGL实战:用ESP32的电容触摸屏和旋转编码器做一个智能家居控制面板(附完整代码)

本文详细介绍了如何利用ESP32开发板、电容触摸屏和旋转编码器构建一个智能家居控制面板,并集成LVGL图形库实现双输入交互。通过优化触摸屏驱动和编码器配置,结合LVGL的输入设备API,实现流畅的用户界面控制和低功耗模式,附完整代码可直接用于实际项目开发。

weixin_28689507的博客 193

ESP32编码器读数总跳变?手把手教你用PCNT模块实现稳定脉冲计数(附完整代码)

本文深入解析ESP32编码器读数跳变问题,提供从硬件到固件的完整解决方案。通过优化PCNT模块配置、信号调理电路和中断处理策略,实现稳定脉冲计数,适用于电机控制和位置反馈系统。附完整代码实现,帮助开发者快速解决ESP32编码器读数不稳定问题。

weixin_42713608的博客 355

用ESP32和EC11旋钮做个无线音量旋钮吧(附Arduino代码和BleKeyboard库避坑指南)

本文详细介绍了如何利用ESP32和EC11旋转编码器制作无线音量旋钮,提供Arduino代码和BleKeyboard库的避坑指南。通过蓝牙连接,实现精准音量控制,兼容多种操作系统,无需额外驱动。项目硬件成本低,适合DIY爱好者提升桌面体验。

weixin_30140399的博客 375

ESP32 PCNT模块与电磁编码器的高精度位置测量实践

本文详细介绍了如何利用ESP32内置的PCNT硬件模块与电磁编码器实现高精度位置测量。通过硬件连接指南、核心代码逐行解析及配置逻辑,阐述了PCNT模块如何通过正交解码实现低CPU占用的脉冲计数。文章还涵盖了抗干扰滤波、4倍频提高分辨率、中断处理以及将脉冲数转换为角度与转速的实用方法,并提供了电源去耦、GPIO配置等实战避坑指南,是ESP32运动控制应用的完整实践方案。

weixin_29168153的博客 201
上一篇: ESP32S3学习——Pulse Counter (PCNT)脉冲计数
下一篇: ESP32S3学习——i2c,点亮0.96寸oled
HandBAI
博客等级 码龄5年 27粉丝 23原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值