Skip to content

Instantly share code, notes, and snippets.

@cskilbeck
Last active April 30, 2024 20:58
Show Gist options
  • Save cskilbeck/1284c97ad3051ae012170b781ca5b5e9 to your computer and use it in GitHub Desktop.
Save cskilbeck/1284c97ad3051ae012170b781ca5b5e9 to your computer and use it in GitHub Desktop.
Encoder peripheral for esp-adf
//////////////////////////////////////////////////////////////////////
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "esp_rom_gpio.h"
#include "audio_mem.h"
#include "encoder.h"
//////////////////////////////////////////////////////////////////////
static char const *TAG = "encoder";
//////////////////////////////////////////////////////////////////////
namespace
{
uint8_t const encoder_valid_bits[16] = { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 };
} // namespace
//////////////////////////////////////////////////////////////////////
int IRAM_ATTR encoder_read(esp_encoder_handle_t encoder, int bits)
{
encoder->state <<= 2;
encoder->state |= bits;
encoder->state &= 0xf;
if(encoder_valid_bits[encoder->state] != 0) {
encoder->store = (encoder->store << 4) | encoder->state;
switch((encoder->store)) {
case 0xe8:
return 1;
case 0x2b:
return -1;
}
}
return 0;
}
//////////////////////////////////////////////////////////////////////
esp_err_t encoder_init(encoder_config_t *cfg, esp_encoder_handle_t *handle)
{
ESP_LOGI(TAG, "init");
esp_encoder_handle_t encoder = (esp_encoder_handle_t)audio_calloc(1, sizeof(struct esp_encoder));
encoder->gpio_a = cfg->gpio_a;
encoder->gpio_b = cfg->gpio_b;
gpio_config_t gpiocfg = {
.pin_bit_mask = (1llu << cfg->gpio_a) | (1llu << cfg->gpio_b),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_ANYEDGE,
};
gpio_config(&gpiocfg);
if(cfg->encoder_intr_handler) {
ESP_LOGI(TAG, "Adding GPIO interrupt handlers");
gpio_set_intr_type(cfg->gpio_a, GPIO_INTR_ANYEDGE);
gpio_set_intr_type(cfg->gpio_b, GPIO_INTR_ANYEDGE);
gpio_isr_handler_add(cfg->gpio_a, cfg->encoder_intr_handler, cfg->intr_context);
gpio_isr_handler_add(cfg->gpio_b, cfg->encoder_intr_handler, cfg->intr_context);
gpio_intr_enable(cfg->gpio_a);
gpio_intr_enable(cfg->gpio_b);
}
ESP_LOGI(TAG, "init done");
*handle = encoder;
return ESP_OK;
}
//////////////////////////////////////////////////////////////////////
esp_err_t encoder_destroy(esp_encoder_handle_t encoder)
{
ESP_LOGI(TAG, "destroy");
audio_free(encoder);
return ESP_OK;
}
#pragma once
#include "esp_err.h"
#include "driver/gpio.h"
#if defined(__cplusplus)
extern "C" {
#endif
typedef struct encoder_msg_struct
{
int direction;
} encoder_msg_t;
typedef void (*encoder_isr)(void *);
typedef struct encoder_config
{
gpio_num_t gpio_a;
gpio_num_t gpio_b;
encoder_isr encoder_intr_handler;
void *intr_context;
} encoder_config_t;
struct esp_encoder
{
gpio_num_t gpio_a;
gpio_num_t gpio_b;
uint8_t state;
uint8_t store;
};
typedef struct esp_encoder *esp_encoder_handle_t;
esp_err_t encoder_init(encoder_config_t *cfg, esp_encoder_handle_t *encoder);
esp_err_t encoder_destroy(esp_encoder_handle_t encoder);
int encoder_read(esp_encoder_handle_t encoder, int bits);
#if defined(__cplusplus)
}
#endif
/*
* ESPRESSIF MIT License
*
* Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
*
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
* it is free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include "esp_log.h"
#include "encoder.h"
#include "periph_encoder.h"
#include "audio_mem.h"
static const char *TAG = "PERIPH_ENCODER";
#define VALIDATE_ENCODER(periph, ret) \
if(!(periph && esp_periph_get_id(periph) == PERIPH_ID_ENCODER)) { \
ESP_LOGE(TAG, "Invalid ENCODER periph, at line %d", __LINE__); \
return ret; \
}
typedef struct
{
esp_encoder_handle_t encoder;
gpio_num_t gpio_a;
gpio_num_t gpio_b;
} periph_encoder_t;
static void IRAM_ATTR encoder_intr_handler(void *param)
{
esp_periph_handle_t periph = (esp_periph_handle_t)param;
periph_encoder_t *periph_encoder = esp_periph_get_data(periph);
esp_encoder_handle_t encoder = periph_encoder->encoder;
int a = gpio_get_level(encoder->gpio_a);
int b = gpio_get_level(encoder->gpio_b);
esp_periph_send_cmd_from_isr(periph, 0, (void *)((a << 1) | b), 0);
}
static esp_err_t _encoder_run(esp_periph_handle_t self, audio_event_iface_msg_t *msg)
{
periph_encoder_t *periph_encoder = esp_periph_get_data(self);
int direction = encoder_read(periph_encoder->encoder, (int)msg->data);
if(direction == -1) {
return esp_periph_send_event(self, PERIPH_ENCODER_COUNTER_CLOCKWISE, NULL, 0);
}
if(direction == 1) {
return esp_periph_send_event(self, PERIPH_ENCODER_CLOCKWISE, NULL, 0);
}
return ESP_OK;
}
static esp_err_t _encoder_destroy(esp_periph_handle_t self)
{
ESP_LOGI(TAG, "destroy");
periph_encoder_t *periph_encoder = esp_periph_get_data(self);
encoder_destroy(periph_encoder->encoder);
audio_free(periph_encoder);
return ESP_OK;
}
static esp_err_t _encoder_init(esp_periph_handle_t self)
{
ESP_LOGI(TAG, "init");
VALIDATE_ENCODER(self, ESP_FAIL);
periph_encoder_t *periph_encoder = esp_periph_get_data(self);
encoder_config_t encoder_config = {
.gpio_a = periph_encoder->gpio_a,
.gpio_b = periph_encoder->gpio_b,
.encoder_intr_handler = encoder_intr_handler,
.intr_context = self,
};
return encoder_init(&encoder_config, &periph_encoder->encoder);
}
esp_periph_handle_t periph_encoder_init(periph_encoder_cfg_t *config)
{
ESP_LOGI(TAG, "periph_encoder_init");
esp_periph_handle_t periph = esp_periph_create(PERIPH_ID_ENCODER, "periph_encoder");
AUDIO_MEM_CHECK(TAG, periph, return NULL);
periph_encoder_t *periph_encoder = audio_calloc(1, sizeof(periph_encoder_t));
AUDIO_MEM_CHECK(TAG, periph_encoder, {
audio_free(periph);
return NULL;
});
periph_encoder->gpio_a = config->gpio_num_a;
periph_encoder->gpio_b = config->gpio_num_b;
esp_periph_set_data(periph, periph_encoder);
esp_periph_set_function(periph, _encoder_init, _encoder_run, _encoder_destroy);
return periph;
}
/*
* ESPRESSIF MIT License
*
* Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
*
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
* it is free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#ifndef __PERIPH_ENCODER_H
#define __PERIPH_ENCODER_H
#include "esp_peripherals.h"
#include "driver/gpio.h"
#ifdef __cplusplus
extern "C" {
#endif
// Hmm - how to get the max defined PERIPH_ID ? Just hard code PERIPH_ID_LCD for now...
#define PERIPH_ID_ENCODER (PERIPH_ID_LCD + 1)
/**
* @brief The Encoder peripheral configuration
*/
typedef struct
{
gpio_num_t gpio_num_a;
gpio_num_t gpio_num_b;
} periph_encoder_cfg_t;
/**
* @brief Peripheral encoder event id
*/
typedef enum
{
PERIPH_ENCODER_UNCHANGE = 0, /*!< No event */
PERIPH_ENCODER_CLOCKWISE, /*! rotate cw */
PERIPH_ENCODER_COUNTER_CLOCKWISE, /*! rotate ccw */
} periph_encoder_event_id_t;
/**
* @brief Create the encoder peripheral handle for esp_peripherals.
*
* @note The handle was created by this function automatically destroy when `esp_periph_destroy` is called
*
* @param encoder_cfg The encoder configuration
*
* @return The esp peripheral handle
*/
esp_periph_handle_t periph_encoder_init(periph_encoder_cfg_t *encoder_cfg);
#ifdef __cplusplus
}
#endif
#endif //__PERIPH_ENCODER_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment