Merge pull request #59 from petejohanson/sensors/en11-encoder-sensor

Initial low level EC11 sensor driver
This commit is contained in:
Pete Johanson 2020-07-21 14:38:32 -04:00 committed by GitHub
commit 54f1ddc1f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 484 additions and 0 deletions

View File

@ -58,6 +58,24 @@ RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,6) RC(2,7) RC(2,8) RC(2,9)
};
left_encoder: encoder_left {
compatible = "alps,ec11";
label = "LEFT_ENCODER";
a-gpios = <&pro_micro_d 9 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>;
b-gpios = <&pro_micro_d 5 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>;
resolution = <4>;
status = "disabled";
};
right_encoder: encoder_right {
compatible = "alps,ec11";
label = "RIGHT_ENCODER";
a-gpios = <&pro_micro_d 5 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>;
b-gpios = <&pro_micro_d 9 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>;
resolution = <4>;
status = "disabled";
};
// TODO: Encoder node(s)
// TODO: OLED node
// TODO: RGB node(s)

View File

@ -19,3 +19,6 @@
;
};
&left_encoder {
status = "okay";
};

View File

@ -23,3 +23,7 @@
;
};
&right_encoder {
status = "okay";
};

View File

@ -6,4 +6,7 @@ if(CONFIG_ZMK_KSCAN_GPIO_DRIVER)
kscan_gpio_matrix.c
kscan_gpio_direct.c
)
zephyr_library_sources_ifdef(CONFIG_EC11 ec11.c)
zephyr_library_sources_ifdef(CONFIG_EC11_TRIGGER ec11_trigger.c)
endif()

View File

@ -17,3 +17,50 @@ config ZMK_KSCAN_INIT_PRIORITY
help
Keyboard scan device driver initialization priority.
menuconfig EC11
bool "EC11 Incremental Encoder Sensor"
depends on GPIO
help
Enable driver for EC11 incremental encoder sensors.
if EC11
choice
prompt "Trigger mode"
default EC11_TRIGGER_NONE
help
Specify the type of triggering to be used by the driver.
config EC11_TRIGGER_NONE
bool "No trigger"
config EC11_TRIGGER_GLOBAL_THREAD
bool "Use global thread"
depends on GPIO
select EC11_TRIGGER
config EC11_TRIGGER_OWN_THREAD
bool "Use own thread"
depends on GPIO
select EC11_TRIGGER
endchoice
config EC11_TRIGGER
bool
config EC11_THREAD_PRIORITY
int "Thread priority"
depends on EC11_TRIGGER_OWN_THREAD
default 10
help
Priority of thread used by the driver to handle interrupts.
config EC11_THREAD_STACK_SIZE
int "Thread stack size"
depends on EC11_TRIGGER_OWN_THREAD
default 1024
help
Stack size of thread used by the driver to handle interrupts.
endif # EC11

View File

@ -0,0 +1,21 @@
description: |
Sensor driver for the Alps EC11 rotary encoder
compatible: "alps,ec11"
properties:
label:
type: string
required: true
a-gpios:
type: phandle-array
required: true
description: A pin for the encoder
b-gpios:
type: phandle-array
required: true
description: A pin for the encoder
resolution:
type: int
description: Number of pulses per tick
required: false

153
app/drivers/zephyr/ec11.c Normal file
View File

@ -0,0 +1,153 @@
/*
* Copyright (c) 2020 Peter Johanson
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT alps_ec11
#include <device.h>
#include <drivers/gpio.h>
#include <sys/util.h>
#include <kernel.h>
#include <drivers/sensor.h>
#include <sys/__assert.h>
#include <logging/log.h>
#include "ec11.h"
LOG_MODULE_REGISTER(EC11, CONFIG_SENSOR_LOG_LEVEL);
static int ec11_get_ab_state(struct device *dev)
{
struct ec11_data *drv_data = dev->driver_data;
const struct ec11_config *drv_cfg = dev->config_info;
return (gpio_pin_get(drv_data->a, drv_cfg->a_pin) << 1) | gpio_pin_get(drv_data->b, drv_cfg->b_pin);
}
static int ec11_sample_fetch(struct device *dev, enum sensor_channel chan)
{
struct ec11_data *drv_data = dev->driver_data;
const struct ec11_config *drv_cfg = dev->config_info;
u8_t val;
s8_t delta;
__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_ROTATION);
val = ec11_get_ab_state(dev);
LOG_DBG("prev: %d, new: %d", drv_data->ab_state, val);
switch(val | (drv_data->ab_state << 2)) {
case 0b0010: case 0b0100: case 0b1101: case 0b1011:
delta = -1;
break;
case 0b0001: case 0b0111: case 0b1110: case 0b1000:
delta = 1;
break;
default:
delta = 0;
break;
}
LOG_DBG("Delta: %d", delta);
drv_data->pulses += delta;
drv_data->ab_state = val;
drv_data->ticks = drv_data->pulses / drv_cfg->resolution;
drv_data->delta = delta;
drv_data->pulses %= drv_cfg->resolution;
return 0;
}
static int ec11_channel_get(struct device *dev,
enum sensor_channel chan,
struct sensor_value *val)
{
struct ec11_data *drv_data = dev->driver_data;
if (chan != SENSOR_CHAN_ROTATION) {
return -ENOTSUP;
}
val->val1 = drv_data->ticks;
val->val2 = drv_data->delta;
return 0;
}
static const struct sensor_driver_api ec11_driver_api = {
#ifdef CONFIG_EC11_TRIGGER
.trigger_set = ec11_trigger_set,
#endif
.sample_fetch = ec11_sample_fetch,
.channel_get = ec11_channel_get,
};
int ec11_init(struct device *dev)
{
struct ec11_data *drv_data = dev->driver_data;
const struct ec11_config *drv_cfg = dev->config_info;
LOG_DBG("A: %s %d B: %s %d resolution %d", drv_cfg->a_label, drv_cfg->a_pin, drv_cfg->b_label, drv_cfg->b_pin, drv_cfg->resolution);
drv_data->a = device_get_binding(drv_cfg->a_label);
if (drv_data->a == NULL) {
LOG_ERR("Failed to get pointer to A GPIO device");
return -EINVAL;
}
drv_data->b = device_get_binding(drv_cfg->b_label);
if (drv_data->b == NULL) {
LOG_ERR("Failed to get pointer to B GPIO device");
return -EINVAL;
}
if (gpio_pin_configure(drv_data->a, drv_cfg->a_pin,
drv_cfg->a_flags
| GPIO_INPUT)) {
LOG_DBG("Failed to configure A pin");
return -EIO;
}
if (gpio_pin_configure(drv_data->b, drv_cfg->b_pin,
drv_cfg->b_flags
| GPIO_INPUT)) {
LOG_DBG("Failed to configure B pin");
return -EIO;
}
#ifdef CONFIG_EC11_TRIGGER
if (ec11_init_interrupt(dev) < 0) {
LOG_DBG("Failed to initialize interrupt!");
return -EIO;
}
#endif
drv_data->ab_state = ec11_get_ab_state(dev);
return 0;
}
#define EC11_INST(n) \
struct ec11_data ec11_data_##n; \
const struct ec11_config ec11_cfg_##n = { \
.a_label = DT_INST_GPIO_LABEL(n, a_gpios), \
.a_pin = DT_INST_GPIO_PIN(n, a_gpios), \
.a_flags = DT_INST_GPIO_FLAGS(n, a_gpios), \
.b_label = DT_INST_GPIO_LABEL(n, b_gpios), \
.b_pin = DT_INST_GPIO_PIN(n, b_gpios), \
.b_flags = DT_INST_GPIO_FLAGS(n, b_gpios), \
COND_CODE_0(DT_INST_NODE_HAS_PROP(n, resolution), (1), (DT_INST_PROP(n, resolution))), \
}; \
DEVICE_AND_API_INIT(ec11, DT_INST_LABEL(n), ec11_init, \
&ec11_data_##n, \
&ec11_cfg_##n, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \
&ec11_driver_api);
DT_INST_FOREACH_STATUS_OKAY(EC11_INST)

59
app/drivers/zephyr/ec11.h Normal file
View File

@ -0,0 +1,59 @@
/*
* Copyright (c) 2020 Peter Johanson
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <device.h>
#include <drivers/gpio.h>
#include <sys/util.h>
struct ec11_config {
const char *a_label;
const u8_t a_pin;
const u8_t a_flags;
const char *b_label;
const u8_t b_pin;
const u8_t b_flags;
const u8_t resolution;
};
struct ec11_data {
struct device *a;
struct device *b;
u8_t ab_state;
s8_t pulses;
s8_t ticks;
s8_t delta;
#ifdef CONFIG_EC11_TRIGGER
struct gpio_callback a_gpio_cb;
struct gpio_callback b_gpio_cb;
struct device *dev;
sensor_trigger_handler_t handler;
struct sensor_trigger trigger;
#if defined(CONFIG_EC11_TRIGGER_OWN_THREAD)
K_THREAD_STACK_MEMBER(thread_stack, CONFIG_EC11_THREAD_STACK_SIZE);
struct k_sem gpio_sem;
struct k_thread thread;
#elif defined(CONFIG_EC11_TRIGGER_GLOBAL_THREAD)
struct k_work work;
#endif
#endif /* CONFIG_EC11_TRIGGER */
};
#ifdef CONFIG_EC11_TRIGGER
int ec11_trigger_set(struct device *dev,
const struct sensor_trigger *trig,
sensor_trigger_handler_t handler);
int ec11_init_interrupt(struct device *dev);
#endif

View File

@ -0,0 +1,176 @@
/*
* Copyright (c) 2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT alps_ec11
#include <device.h>
#include <drivers/gpio.h>
#include <sys/util.h>
#include <kernel.h>
#include <drivers/sensor.h>
#include "ec11.h"
extern struct ec11_data ec11_driver;
#include <logging/log.h>
LOG_MODULE_DECLARE(EC11, CONFIG_SENSOR_LOG_LEVEL);
static inline void setup_int(struct device *dev,
bool enable)
{
struct ec11_data *data = dev->driver_data;
const struct ec11_config *cfg = dev->config_info;
LOG_DBG("enabled %s", (enable ? "true" : "false"));
if (gpio_pin_interrupt_configure(data->a,
cfg->a_pin,
enable
? GPIO_INT_EDGE_BOTH
: GPIO_INT_DISABLE)) {
LOG_WRN("Unable to set A pin GPIO interrupt");
}
if (gpio_pin_interrupt_configure(data->b,
cfg->b_pin,
enable
? GPIO_INT_EDGE_BOTH
: GPIO_INT_DISABLE)) {
LOG_WRN("Unable to set A pin GPIO interrupt");
}
}
static void ec11_a_gpio_callback(struct device *dev,
struct gpio_callback *cb, u32_t pins)
{
struct ec11_data *drv_data =
CONTAINER_OF(cb, struct ec11_data, a_gpio_cb);
LOG_DBG("");
setup_int(drv_data->dev, false);
#if defined(CONFIG_EC11_TRIGGER_OWN_THREAD)
k_sem_give(&drv_data->gpio_sem);
#elif defined(CONFIG_EC11_TRIGGER_GLOBAL_THREAD)
k_work_submit(&drv_data->work);
#endif
}
static void ec11_b_gpio_callback(struct device *dev,
struct gpio_callback *cb, u32_t pins)
{
struct ec11_data *drv_data =
CONTAINER_OF(cb, struct ec11_data, b_gpio_cb);
LOG_DBG("");
setup_int(drv_data->dev, false);
#if defined(CONFIG_EC11_TRIGGER_OWN_THREAD)
k_sem_give(&drv_data->gpio_sem);
#elif defined(CONFIG_EC11_TRIGGER_GLOBAL_THREAD)
k_work_submit(&drv_data->work);
#endif
}
static void ec11_thread_cb(void *arg)
{
struct device *dev = arg;
struct ec11_data *drv_data = dev->driver_data;
drv_data->handler(dev, &drv_data->trigger);
setup_int(dev, true);
}
#ifdef CONFIG_EC11_TRIGGER_OWN_THREAD
static void ec11_thread(int dev_ptr, int unused)
{
struct device *dev = INT_TO_POINTER(dev_ptr);
struct ec11_data *drv_data = dev->driver_data;
ARG_UNUSED(unused);
while (1) {
k_sem_take(&drv_data->gpio_sem, K_FOREVER);
ec11_thread_cb(dev);
}
}
#endif
#ifdef CONFIG_EC11_TRIGGER_GLOBAL_THREAD
static void ec11_work_cb(struct k_work *work)
{
struct ec11_data *drv_data =
CONTAINER_OF(work, struct ec11_data, work);
LOG_DBG("");
ec11_thread_cb(drv_data->dev);
}
#endif
int ec11_trigger_set(struct device *dev,
const struct sensor_trigger *trig,
sensor_trigger_handler_t handler)
{
struct ec11_data *drv_data = dev->driver_data;
setup_int(dev, false);
k_msleep(5);
drv_data->trigger = *trig;
drv_data->handler = handler;
setup_int(dev, true);
return 0;
}
int ec11_init_interrupt(struct device *dev)
{
struct ec11_data *drv_data = dev->driver_data;
const struct ec11_config *drv_cfg = dev->config_info;
drv_data->dev = dev;
/* setup gpio interrupt */
gpio_init_callback(&drv_data->a_gpio_cb,
ec11_a_gpio_callback,
BIT(drv_cfg->a_pin));
if (gpio_add_callback(drv_data->a, &drv_data->a_gpio_cb) < 0) {
LOG_DBG("Failed to set A callback!");
return -EIO;
}
gpio_init_callback(&drv_data->b_gpio_cb,
ec11_b_gpio_callback,
BIT(drv_cfg->b_pin));
if (gpio_add_callback(drv_data->b, &drv_data->b_gpio_cb) < 0) {
LOG_DBG("Failed to set B callback!");
return -EIO;
}
#if defined(CONFIG_EC11_TRIGGER_OWN_THREAD)
k_sem_init(&drv_data->gpio_sem, 0, UINT_MAX);
k_thread_create(&drv_data->thread, drv_data->thread_stack,
CONFIG_EC11_THREAD_STACK_SIZE,
(k_thread_entry_t)ec11_thread, dev,
0, NULL, K_PRIO_COOP(CONFIG_EC11_THREAD_PRIORITY),
0, K_NO_WAIT);
#elif defined(CONFIG_EC11_TRIGGER_GLOBAL_THREAD)
k_work_init(&drv_data->work, ec11_work_cb);
#endif
return 0;
}