ESP32 读取正交编码器

ESP32 IDF库中包含了多种的代码示例,其中的 examples/perihperals/pcnt 文件夹下已经提供了能够读取计数器的demo,本文在此demo的基础上修改并实现了ESP32读取正交编码器的功能。

 

代码如下:

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/timers.h"
#include "driver/periph_ctrl.h"
#include "driver/ledc.h"
#include "driver/gpio.h"
#include "driver/pcnt.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "soc/gpio_sig_map.h"


#define PCNT_UNIT           PCNT_UNIT_0
#define PCNT_H_LIM_VAL      30000
#define PCNT_L_LIM_VAL     -30000
#define PCNT_THRESH1_VAL    5
#define PCNT_THRESH0_VAL   -5

#define PCNT_INPUT_IO_A     4  // Pulse Input GPIO
#define PCNT_INPUT_IO_B     5  // Pulse Input GPIO

#define INVALID_HANDLE      0
#define ENCODER_TAKSK_SIZE  (1024 * 2)

xQueueHandle pcnt0_evt_queue;   // A queue to handle pulse counter events
pcnt_isr_handle_t pcnt_user_isr_handle = NULL; //user's ISR service handle

/* A sample structure to pass events from the PCNT
 * interrupt handler to the main program.
 */
typedef struct {
    int unit;           // the PCNT unit that originated an interrupt
    uint32_t status;    // information on the event type that caused the interrupt
} pcnt_evt_t;

static void IRAM_ATTR pcnt_intr_handler0(void *arg)
{
    uint32_t intr_status = PCNT.int_st.val;
    int i;
    pcnt_evt_t pcnt0_evt;
    portBASE_TYPE HPTaskAwoken = pdFALSE;

    for (i = 0; i < PCNT_UNIT_MAX; i++) {
        if (intr_status & (BIT(i))) {
            pcnt0_evt.unit = i;
            /* Save the PCNT event type that caused an interrupt
               to pass it to the main program */
            pcnt0_evt.status = PCNT.status_unit[i].val;
            PCNT.int_clr.val = BIT(i);
            xQueueSendFromISR(pcnt0_evt_queue, &pcnt0_evt, &HPTaskAwoken);
            if (HPTaskAwoken == pdTRUE) {
                portYIELD_FROM_ISR();
            }
        }
    }
}


static void pcnt_init(void)
{

    /* Prepare configuration for the PCNT unit */
    pcnt_config_t pcnt_config;
    
    // ch0
    pcnt_config.pulse_gpio_num  = PCNT_INPUT_IO_A;
    pcnt_config.ctrl_gpio_num   = PCNT_INPUT_IO_B;
    pcnt_config.channel         = PCNT_CHANNEL_0;
    pcnt_config.pos_mode        = PCNT_COUNT_INC;       // Count up on the positive edge
    pcnt_config.neg_mode        = PCNT_COUNT_DEC;       // Keep the counter value on the negative edge

    pcnt_config.lctrl_mode      = PCNT_MODE_REVERSE;    // Reverse counting direction if low
    pcnt_config.hctrl_mode      = PCNT_MODE_KEEP;       // Keep the primary counter mode if high
    pcnt_config.counter_h_lim   = PCNT_H_LIM_VAL;
    pcnt_config.counter_l_lim   = PCNT_L_LIM_VAL;
    pcnt_config.unit            = PCNT_UNIT;
    pcnt_unit_config(&pcnt_config);
    
    // ch1
    pcnt_config.pulse_gpio_num  = PCNT_INPUT_IO_B;
    pcnt_config.ctrl_gpio_num   = PCNT_INPUT_IO_A;
    pcnt_config.channel         = PCNT_CHANNEL_1;
    pcnt_config.pos_mode        = PCNT_COUNT_DEC;       // Count up on the positive edge
    pcnt_config.neg_mode        = PCNT_COUNT_INC;       // Keep the counter value on the negative edge
    pcnt_unit_config(&pcnt_config);


    /* Configure and enable the input filter */
    pcnt_set_filter_value(PCNT_UNIT, 100);
    pcnt_filter_enable(PCNT_UNIT);

    // /* Set threshold 0 and 1 values and enable events to watch */
    // pcnt_set_event_value(PCNT_UNIT, PCNT_EVT_THRES_1, PCNT_THRESH1_VAL);
    // pcnt_event_enable(PCNT_UNIT, PCNT_EVT_THRES_1);
    // pcnt_set_event_value(PCNT_UNIT, PCNT_EVT_THRES_0, PCNT_THRESH0_VAL);
    // pcnt_event_enable(PCNT_UNIT, PCNT_EVT_THRES_0);
    /* Enable events on zero, maximum and minimum limit values */
    pcnt_event_enable(PCNT_UNIT, PCNT_EVT_ZERO);
    pcnt_event_enable(PCNT_UNIT, PCNT_EVT_H_LIM);
    pcnt_event_enable(PCNT_UNIT, PCNT_EVT_L_LIM);

    /* Initialize PCNT's counter */
    pcnt_counter_pause(PCNT_UNIT);
    pcnt_counter_clear(PCNT_UNIT);

    /* Register ISR handler and enable interrupts for PCNT unit */
    pcnt_isr_register(pcnt_intr_handler0, NULL, 0, &pcnt_user_isr_handle);
    pcnt_intr_enable(PCNT_UNIT);

    /* Everything is set up, now go to counting */
    pcnt_counter_resume(PCNT_UNIT);
}

static void encoder_task(void * arg) {
    pcnt0_evt_queue = xQueueCreate(10, sizeof(pcnt_evt_t));
    pcnt_init();

    int16_t count = 0;
    pcnt_evt_t pcnt0_evt;
    portBASE_TYPE res;
    while (1) {

        res = xQueueReceive(pcnt0_evt_queue, &pcnt0_evt, 1000 / portTICK_PERIOD_MS);
        if (res == pdTRUE) {
            pcnt_get_counter_value(PCNT_UNIT, &count);
            ESP_LOGI(__FUNCTION__, "Event PCNT unit[%d]; cnt: %d", pcnt0_evt.unit, count);
            if (pcnt0_evt.status & PCNT_STATUS_THRES1_M) {
                ESP_LOGI(__FUNCTION__, "THRES1 EVT");
            }
            if (pcnt0_evt.status & PCNT_STATUS_THRES0_M) {
                ESP_LOGI(__FUNCTION__, "THRES0 EVT");
            }
            if (pcnt0_evt.status & PCNT_STATUS_L_LIM_M) {
                ESP_LOGI(__FUNCTION__, "L_LIM EVT");
            }
            if (pcnt0_evt.status & PCNT_STATUS_H_LIM_M) {
                ESP_LOGI(__FUNCTION__, "H_LIM EVT");
            }
            if (pcnt0_evt.status & PCNT_STATUS_ZERO_M) {
                ESP_LOGI(__FUNCTION__, "ZERO EVT");
            }
        }
    }

    if(pcnt_user_isr_handle) {
        //Free the ISR service handle.
        esp_intr_free(pcnt_user_isr_handle);
        pcnt_user_isr_handle = NULL;
    }

    ESP_LOGI(__FUNCTION__, "out");
    vTaskDelete (NULL);
}


void timer_cb( TimerHandle_t xTimer ){
    int16_t count = 0;
    pcnt_get_counter_value(PCNT_UNIT, &count);
    pcnt_counter_clear(PCNT_UNIT);
    if(count){
        ESP_LOGI(__FUNCTION__, "Current count  value :%d", count);
    }
}


void encoder_task_start(void){

    xTaskCreate ( encoder_task, "encoder_task", 4096, NULL, INVALID_HANDLE, NULL );
    TimerHandle_t thandle = xTimerCreate("Timer", 50 / portTICK_PERIOD_MS, pdTRUE, NULL, timer_cb);
    xTimerStart(thandle, 0);
}


根据实际情况, 将以下宏定义替换为自己定义的IO口即可。

#define PCNT_INPUT_IO_A     4  // Pulse Input GPIO
#define PCNT_INPUT_IO_B     5  // Pulse Input GPIO

 

ESP32旋转编码器是一种用于测量旋转角度的传感器。它可以通过读取编码器的脉冲信号来确定旋转的角度。下面是一个使用ESP32读取旋转编码器的示例代码: ```cpp #include <Arduino.h> // 定义编码器引脚 #define ENCODER_A_PIN 2 #define ENCODER_B_PIN 3 // 定义编码器计数器 volatile long encoderCount = 0; // 中断处理函数 void IRAM_ATTR encoderISR() { // 读取编码器引脚状态 int a = digitalRead(ENCODER_A_PIN); int b = digitalRead(ENCODER_B_PIN); // 根据编码器引脚状态更新计数器 if (a == b) { encoderCount++; } else { encoderCount--; } } void setup() { // 初始化编码器引脚 pinMode(ENCODER_A_PIN, INPUT_PULLUP); pinMode(ENCODER_B_PIN, INPUT_PULLUP); // 将中断处理函数与编码器引脚绑定 attachInterrupt(digitalPinToInterrupt(ENCODER_A_PIN), encoderISR, CHANGE); attachInterrupt(digitalPinToInterrupt(ENCODER_B_PIN), encoderISR, CHANGE); // 启用中断 interrupts(); } void loop() { // 读取编码器计数器的值 long count = encoderCount; // 打印旋转的角度 float angle = count / 7.0 / 238.0 * 2.0 * PI; Serial.print("旋转的角度为: "); Serial.println(angle); delay(100); } ``` 这段代码使用了ESP32的中断功能来读取编码器的脉冲信号,并根据脉冲信号的变化来更新计数器。通过计算计数器的值,可以得到旋转的角度。请注意,你需要将编码器的引脚连接到ESP32的GPIO引脚,并根据实际情况修改代码中的引脚定义。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值