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

 


版权声明:本文为gongkexiaosheng原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。