2018-10-17 10:39:10 +02:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2018, Nordic Semiconductor ASA
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sensor.h>
|
|
|
|
|
|
|
|
#include <nrfx_qdec.h>
|
|
|
|
#include <hal/nrf_gpio.h>
|
|
|
|
|
2018-10-30 19:23:40 -04:00
|
|
|
#define LOG_LEVEL CONFIG_SENSOR_LOG_LEVEL
|
|
|
|
#include <logging/log.h>
|
2018-11-02 15:17:09 +01:00
|
|
|
LOG_MODULE_REGISTER(qdec_nrfx);
|
2018-10-17 10:39:10 +02:00
|
|
|
|
|
|
|
|
|
|
|
#define FULL_ANGLE 360
|
|
|
|
|
|
|
|
/* limit range to avoid overflow when converting steps to degrees */
|
|
|
|
#define ACC_MAX (INT_MAX / FULL_ANGLE)
|
|
|
|
#define ACC_MIN (INT_MIN / FULL_ANGLE)
|
|
|
|
|
|
|
|
|
|
|
|
struct qdec_nrfx_data {
|
|
|
|
s32_t acc;
|
|
|
|
sensor_trigger_handler_t data_ready_handler;
|
|
|
|
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
|
|
|
u32_t pm_state;
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static struct qdec_nrfx_data qdec_nrfx_data;
|
|
|
|
|
|
|
|
DEVICE_DECLARE(qdec_nrfx);
|
|
|
|
|
|
|
|
|
|
|
|
static void accumulate(struct qdec_nrfx_data *data, int16_t acc)
|
|
|
|
{
|
|
|
|
unsigned int key = irq_lock();
|
|
|
|
|
|
|
|
bool overflow = ((acc > 0) && (ACC_MAX - acc < data->acc)) ||
|
|
|
|
((acc < 0) && (ACC_MIN - acc > data->acc));
|
|
|
|
|
|
|
|
if (!overflow) {
|
|
|
|
data->acc += acc;
|
|
|
|
}
|
|
|
|
|
|
|
|
irq_unlock(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qdec_nrfx_sample_fetch(struct device *dev, enum sensor_channel chan)
|
|
|
|
{
|
|
|
|
struct qdec_nrfx_data *data = &qdec_nrfx_data;
|
|
|
|
|
|
|
|
int16_t acc;
|
|
|
|
int16_t accdbl;
|
|
|
|
|
|
|
|
ARG_UNUSED(dev);
|
|
|
|
|
2018-10-30 19:23:40 -04:00
|
|
|
LOG_DBG("");
|
2018-10-17 10:39:10 +02:00
|
|
|
|
|
|
|
if ((chan != SENSOR_CHAN_ALL) && (chan != SENSOR_CHAN_ROTATION)) {
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
nrfx_qdec_accumulators_read(&acc, &accdbl);
|
|
|
|
|
|
|
|
accumulate(data, acc);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qdec_nrfx_channel_get(struct device *dev,
|
|
|
|
enum sensor_channel chan,
|
|
|
|
struct sensor_value *val)
|
|
|
|
{
|
|
|
|
struct qdec_nrfx_data *data = &qdec_nrfx_data;
|
|
|
|
unsigned int key;
|
|
|
|
s32_t acc;
|
|
|
|
|
|
|
|
ARG_UNUSED(dev);
|
2018-10-30 19:23:40 -04:00
|
|
|
LOG_DBG("");
|
2018-10-17 10:39:10 +02:00
|
|
|
|
|
|
|
if (chan != SENSOR_CHAN_ROTATION) {
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
key = irq_lock();
|
|
|
|
acc = data->acc;
|
|
|
|
data->acc = 0;
|
|
|
|
irq_unlock(key);
|
|
|
|
|
2018-11-29 23:41:08 +01:00
|
|
|
BUILD_ASSERT_MSG(DT_NORDIC_NRF_QDEC_QDEC_0_STEPS > 0,
|
|
|
|
"only positive number valid");
|
|
|
|
BUILD_ASSERT_MSG(DT_NORDIC_NRF_QDEC_QDEC_0_STEPS <= 2148,
|
|
|
|
"overflow possible");
|
2018-11-16 13:46:59 +01:00
|
|
|
|
|
|
|
val->val1 = (acc * FULL_ANGLE) / DT_NORDIC_NRF_QDEC_QDEC_0_STEPS;
|
|
|
|
val->val2 = (acc * FULL_ANGLE)
|
|
|
|
- (val->val1 * DT_NORDIC_NRF_QDEC_QDEC_0_STEPS);
|
2018-10-17 10:39:10 +02:00
|
|
|
if (val->val2 != 0) {
|
|
|
|
val->val2 *= 1000000;
|
2018-11-16 13:46:59 +01:00
|
|
|
val->val2 /= DT_NORDIC_NRF_QDEC_QDEC_0_STEPS;
|
2018-10-17 10:39:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qdec_nrfx_trigger_set(struct device *dev,
|
|
|
|
const struct sensor_trigger *trig,
|
|
|
|
sensor_trigger_handler_t handler)
|
|
|
|
{
|
|
|
|
struct qdec_nrfx_data *data = &qdec_nrfx_data;
|
|
|
|
unsigned int key;
|
|
|
|
|
|
|
|
ARG_UNUSED(dev);
|
2018-10-30 19:23:40 -04:00
|
|
|
LOG_DBG("");
|
2018-10-17 10:39:10 +02:00
|
|
|
|
|
|
|
if (trig->type != SENSOR_TRIG_DATA_READY) {
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((trig->chan != SENSOR_CHAN_ALL) &&
|
|
|
|
(trig->chan != SENSOR_CHAN_ROTATION)) {
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
key = irq_lock();
|
|
|
|
data->data_ready_handler = handler;
|
|
|
|
irq_unlock(key);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qdec_nrfx_event_handler(nrfx_qdec_event_t event)
|
|
|
|
{
|
|
|
|
sensor_trigger_handler_t handler;
|
|
|
|
unsigned int key;
|
|
|
|
|
|
|
|
switch (event.type) {
|
|
|
|
case NRF_QDEC_EVENT_REPORTRDY:
|
|
|
|
accumulate(&qdec_nrfx_data, event.data.report.acc);
|
|
|
|
|
|
|
|
key = irq_lock();
|
|
|
|
handler = qdec_nrfx_data.data_ready_handler;
|
|
|
|
irq_unlock(key);
|
|
|
|
|
|
|
|
if (handler) {
|
|
|
|
struct sensor_trigger trig = {
|
|
|
|
.type = SENSOR_TRIG_DATA_READY,
|
|
|
|
.chan = SENSOR_CHAN_ROTATION,
|
|
|
|
};
|
|
|
|
|
|
|
|
handler(DEVICE_GET(qdec_nrfx), &trig);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2018-10-30 19:23:40 -04:00
|
|
|
LOG_ERR("unhandled event (0x%x)", event.type);
|
2018-10-17 10:39:10 +02:00
|
|
|
break;
|
2018-10-23 11:04:33 +05:30
|
|
|
}
|
2018-10-17 10:39:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void qdec_nrfx_gpio_ctrl(bool enable)
|
|
|
|
{
|
2018-11-16 13:46:59 +01:00
|
|
|
#if defined(DT_NORDIC_NRF_QDEC_QDEC_0_ENABLE_PIN)
|
2018-10-17 10:39:10 +02:00
|
|
|
uint32_t val = (enable)?(0):(1);
|
|
|
|
|
2018-11-16 13:46:59 +01:00
|
|
|
nrf_gpio_pin_write(DT_NORDIC_NRF_QDEC_QDEC_0_ENABLE_PIN, val);
|
|
|
|
nrf_gpio_cfg_output(DT_NORDIC_NRF_QDEC_QDEC_0_ENABLE_PIN);
|
2018-10-17 10:39:10 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qdec_nrfx_init(struct device *dev)
|
|
|
|
{
|
|
|
|
static const nrfx_qdec_config_t config = {
|
|
|
|
.reportper = NRF_QDEC_REPORTPER_40,
|
|
|
|
.sampleper = NRF_QDEC_SAMPLEPER_2048us,
|
2018-11-16 13:46:59 +01:00
|
|
|
.psela = DT_NORDIC_NRF_QDEC_QDEC_0_A_PIN,
|
|
|
|
.pselb = DT_NORDIC_NRF_QDEC_QDEC_0_B_PIN,
|
|
|
|
#if defined(DT_NORDIC_NRF_QDEC_QDEC_0_LED_PIN)
|
|
|
|
.pselled = DT_NORDIC_NRF_QDEC_QDEC_0_LED_PIN,
|
2018-10-17 10:39:10 +02:00
|
|
|
#else
|
|
|
|
.pselled = 0xFFFFFFFF, /* disabled */
|
|
|
|
#endif
|
2018-11-16 13:46:59 +01:00
|
|
|
.ledpre = DT_NORDIC_NRF_QDEC_QDEC_0_LED_PRE,
|
2018-10-17 10:39:10 +02:00
|
|
|
.ledpol = NRF_QDEC_LEPOL_ACTIVE_HIGH,
|
|
|
|
.interrupt_priority = NRFX_QDEC_CONFIG_IRQ_PRIORITY,
|
|
|
|
.dbfen = 0, /* disabled */
|
|
|
|
.sample_inten = 0, /* disabled */
|
|
|
|
};
|
|
|
|
|
|
|
|
nrfx_err_t nerr;
|
|
|
|
|
2018-10-30 19:23:40 -04:00
|
|
|
LOG_DBG("");
|
2018-10-17 10:39:10 +02:00
|
|
|
|
2019-06-25 16:07:41 +02:00
|
|
|
IRQ_CONNECT(DT_NORDIC_NRF_QDEC_QDEC_0_IRQ_0,
|
|
|
|
DT_NORDIC_NRF_QDEC_QDEC_0_IRQ_0_PRIORITY,
|
2018-10-17 10:39:10 +02:00
|
|
|
nrfx_isr, nrfx_qdec_irq_handler, 0);
|
|
|
|
|
|
|
|
nerr = nrfx_qdec_init(&config, qdec_nrfx_event_handler);
|
|
|
|
if (nerr == NRFX_ERROR_INVALID_STATE) {
|
2018-10-30 19:23:40 -04:00
|
|
|
LOG_ERR("qdec already in use");
|
2018-10-17 10:39:10 +02:00
|
|
|
return -EBUSY;
|
|
|
|
} else if (nerr != NRFX_SUCCESS) {
|
2018-10-30 19:23:40 -04:00
|
|
|
LOG_ERR("failed to initialize qdec");
|
2018-10-17 10:39:10 +02:00
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
qdec_nrfx_gpio_ctrl(true);
|
|
|
|
nrfx_qdec_enable();
|
|
|
|
|
|
|
|
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
|
|
|
struct qdec_nrfx_data *data = &qdec_nrfx_data;
|
|
|
|
|
|
|
|
data->pm_state = DEVICE_PM_ACTIVE_STATE;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
|
|
|
|
|
|
|
static int qdec_nrfx_pm_get_state(struct qdec_nrfx_data *data,
|
|
|
|
u32_t *state)
|
|
|
|
{
|
|
|
|
unsigned int key = irq_lock();
|
|
|
|
*state = data->pm_state;
|
|
|
|
irq_unlock(key);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qdec_nrfx_pm_set_state(struct qdec_nrfx_data *data,
|
|
|
|
u32_t new_state)
|
|
|
|
{
|
|
|
|
u32_t old_state;
|
|
|
|
unsigned int key;
|
|
|
|
|
|
|
|
key = irq_lock();
|
|
|
|
old_state = data->pm_state;
|
|
|
|
irq_unlock(key);
|
|
|
|
|
|
|
|
if (old_state == new_state) {
|
|
|
|
/* leave unchanged */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (old_state == DEVICE_PM_ACTIVE_STATE) {
|
|
|
|
/* device must be suspended */
|
|
|
|
nrfx_qdec_disable();
|
|
|
|
qdec_nrfx_gpio_ctrl(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (new_state == DEVICE_PM_OFF_STATE) {
|
|
|
|
/* device must be uninitialized */
|
|
|
|
nrfx_qdec_uninit();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (new_state == DEVICE_PM_ACTIVE_STATE) {
|
|
|
|
qdec_nrfx_gpio_ctrl(true);
|
|
|
|
nrfx_qdec_enable();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* record the new state */
|
|
|
|
key = irq_lock();
|
|
|
|
data->pm_state = new_state;
|
|
|
|
irq_unlock(key);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-02-14 09:35:42 +05:30
|
|
|
static int qdec_nrfx_pm_control(struct device *dev, u32_t ctrl_command,
|
|
|
|
void *context, device_pm_cb cb, void *arg)
|
2018-10-17 10:39:10 +02:00
|
|
|
{
|
|
|
|
struct qdec_nrfx_data *data = &qdec_nrfx_data;
|
|
|
|
int err;
|
|
|
|
|
2018-10-30 19:23:40 -04:00
|
|
|
LOG_DBG("");
|
2018-10-17 10:39:10 +02:00
|
|
|
|
|
|
|
switch (ctrl_command) {
|
|
|
|
case DEVICE_PM_GET_POWER_STATE:
|
|
|
|
err = qdec_nrfx_pm_get_state(data, context);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DEVICE_PM_SET_POWER_STATE:
|
|
|
|
err = qdec_nrfx_pm_set_state(data, *((u32_t *)context));
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
err = -ENOTSUP;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-02-14 09:35:42 +05:30
|
|
|
if (cb) {
|
|
|
|
cb(dev, err, context, arg);
|
|
|
|
}
|
|
|
|
|
2018-10-17 10:39:10 +02:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* CONFIG_DEVICE_POWER_MANAGEMENT */
|
|
|
|
|
|
|
|
|
|
|
|
static const struct sensor_driver_api qdec_nrfx_driver_api = {
|
|
|
|
.sample_fetch = qdec_nrfx_sample_fetch,
|
|
|
|
.channel_get = qdec_nrfx_channel_get,
|
|
|
|
.trigger_set = qdec_nrfx_trigger_set,
|
|
|
|
};
|
|
|
|
|
2018-11-16 13:46:59 +01:00
|
|
|
DEVICE_DEFINE(qdec_nrfx, DT_NORDIC_NRF_QDEC_QDEC_0_LABEL, qdec_nrfx_init,
|
2018-10-17 10:39:10 +02:00
|
|
|
qdec_nrfx_pm_control, NULL, NULL, POST_KERNEL,
|
|
|
|
CONFIG_SENSOR_INIT_PRIORITY, &qdec_nrfx_driver_api);
|