Pete Johanson
4 years ago
committed by
GitHub
9 changed files with 484 additions and 0 deletions
@ -0,0 +1,21 @@
@@ -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 |
@ -0,0 +1,153 @@
@@ -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) |
@ -0,0 +1,59 @@
@@ -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 |
@ -0,0 +1,176 @@
@@ -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; |
||||
} |
Loading…
Reference in new issue