drivers: input: add an analog-axis driver
Add an input driver to read data from an analog device, such as a thumbstick, connected to an ADC channel, and report it as an input device. Signed-off-by: Fabio Baltieri <fabiobaltieri@google.com>
This commit is contained in:
parent
e5b3231354
commit
bd8cee8683
|
@ -96,3 +96,8 @@ Input Event Definitions
|
|||
***********************
|
||||
|
||||
.. doxygengroup:: input_events
|
||||
|
||||
Analog Axis API Reference
|
||||
*************************
|
||||
|
||||
.. doxygengroup:: input_analog_axis
|
||||
|
|
|
@ -4,6 +4,8 @@ zephyr_library()
|
|||
zephyr_library_property(ALLOW_EMPTY TRUE)
|
||||
|
||||
# zephyr-keep-sorted-start
|
||||
zephyr_library_sources_ifdef(CONFIG_INPUT_ANALOG_AXIS input_analog_axis.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_INPUT_ANALOG_AXIS_SETTINGS input_analog_axis_settings.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_INPUT_CAP1203 input_cap1203.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_INPUT_CST816S input_cst816s.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_INPUT_ESP32_TOUCH_SENSOR input_esp32_touch_sensor.c)
|
||||
|
|
|
@ -6,6 +6,7 @@ if INPUT
|
|||
menu "Input drivers"
|
||||
|
||||
# zephyr-keep-sorted-start
|
||||
source "drivers/input/Kconfig.analog_axis"
|
||||
source "drivers/input/Kconfig.cap1203"
|
||||
source "drivers/input/Kconfig.cst816s"
|
||||
source "drivers/input/Kconfig.esp32"
|
||||
|
|
43
drivers/input/Kconfig.analog_axis
Normal file
43
drivers/input/Kconfig.analog_axis
Normal file
|
@ -0,0 +1,43 @@
|
|||
# Copyright 2023 Google LLC
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config INPUT_ANALOG_AXIS
|
||||
bool "ADC based analog axis input driver"
|
||||
default y
|
||||
depends on DT_HAS_ANALOG_AXIS_ENABLED
|
||||
depends on ADC
|
||||
depends on MULTITHREADING
|
||||
help
|
||||
ADC based analog axis input driver
|
||||
|
||||
if INPUT_ANALOG_AXIS
|
||||
|
||||
config INPUT_ANALOG_AXIS_THREAD_STACK_SIZE
|
||||
int "Stack size for the analog axis thread"
|
||||
default 762
|
||||
help
|
||||
Size of the stack used for the analog axis thread.
|
||||
|
||||
config INPUT_ANALOG_AXIS_THREAD_PRIORITY
|
||||
int "Priority for the analog axis thread"
|
||||
default 0
|
||||
help
|
||||
Priority level of the analog axis thread.
|
||||
|
||||
config INPUT_ANALOG_AXIS_SETTINGS
|
||||
bool "Analog axis settings support"
|
||||
default y
|
||||
depends on SETTINGS
|
||||
help
|
||||
Settings support for the analog axis driver, exposes a
|
||||
analog_axis_calibration_save() function to save the calibration into
|
||||
settings and load them automatically on startup.
|
||||
|
||||
config INPUT_ANALOG_AXIS_SETTINGS_MAX_AXES
|
||||
int "Maximum number of axes supported in the settings."
|
||||
default 8
|
||||
help
|
||||
Maximum number of axes that can have calibration value saved in
|
||||
settings.
|
||||
|
||||
endif
|
271
drivers/input/input_analog_axis.c
Normal file
271
drivers/input/input_analog_axis.c
Normal file
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
* Copyright 2023 Google LLC
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT analog_axis
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/adc.h>
|
||||
#include <zephyr/input/input.h>
|
||||
#include <zephyr/input/input_analog_axis.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
|
||||
LOG_MODULE_REGISTER(analog_axis, CONFIG_INPUT_LOG_LEVEL);
|
||||
|
||||
struct analog_axis_channel_config {
|
||||
struct adc_dt_spec adc;
|
||||
int16_t out_min;
|
||||
int16_t out_max;
|
||||
uint16_t axis;
|
||||
bool invert;
|
||||
};
|
||||
|
||||
struct analog_axis_channel_data {
|
||||
int last_out;
|
||||
};
|
||||
|
||||
struct analog_axis_config {
|
||||
uint32_t poll_period_ms;
|
||||
const struct analog_axis_channel_config *channel_cfg;
|
||||
struct analog_axis_channel_data *channel_data;
|
||||
struct analog_axis_calibration *calibration;
|
||||
const uint8_t num_channels;
|
||||
};
|
||||
|
||||
struct analog_axis_data {
|
||||
struct k_mutex cal_lock;
|
||||
analog_axis_raw_data_t raw_data_cb;
|
||||
struct k_timer timer;
|
||||
struct k_thread thread;
|
||||
|
||||
K_KERNEL_STACK_MEMBER(thread_stack,
|
||||
CONFIG_INPUT_ANALOG_AXIS_THREAD_STACK_SIZE);
|
||||
};
|
||||
|
||||
int analog_axis_num_axes(const struct device *dev)
|
||||
{
|
||||
const struct analog_axis_config *cfg = dev->config;
|
||||
|
||||
return cfg->num_channels;
|
||||
}
|
||||
|
||||
int analog_axis_calibration_get(const struct device *dev,
|
||||
int channel,
|
||||
struct analog_axis_calibration *out_cal)
|
||||
{
|
||||
const struct analog_axis_config *cfg = dev->config;
|
||||
struct analog_axis_data *data = dev->data;
|
||||
struct analog_axis_calibration *cal = &cfg->calibration[channel];
|
||||
|
||||
if (channel >= cfg->num_channels) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
k_mutex_lock(&data->cal_lock, K_FOREVER);
|
||||
memcpy(out_cal, cal, sizeof(struct analog_axis_calibration));
|
||||
k_mutex_unlock(&data->cal_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void analog_axis_set_raw_data_cb(const struct device *dev, analog_axis_raw_data_t cb)
|
||||
{
|
||||
struct analog_axis_data *data = dev->data;
|
||||
|
||||
k_mutex_lock(&data->cal_lock, K_FOREVER);
|
||||
data->raw_data_cb = cb;
|
||||
k_mutex_unlock(&data->cal_lock);
|
||||
}
|
||||
|
||||
int analog_axis_calibration_set(const struct device *dev,
|
||||
int channel,
|
||||
struct analog_axis_calibration *new_cal)
|
||||
{
|
||||
const struct analog_axis_config *cfg = dev->config;
|
||||
struct analog_axis_data *data = dev->data;
|
||||
struct analog_axis_calibration *cal = &cfg->calibration[channel];
|
||||
|
||||
if (channel >= cfg->num_channels) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
k_mutex_lock(&data->cal_lock, K_FOREVER);
|
||||
memcpy(cal, new_cal, sizeof(struct analog_axis_calibration));
|
||||
k_mutex_unlock(&data->cal_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void analog_axis_loop(const struct device *dev)
|
||||
{
|
||||
const struct analog_axis_config *cfg = dev->config;
|
||||
struct analog_axis_data *data = dev->data;
|
||||
int16_t bufs[cfg->num_channels];
|
||||
int32_t out;
|
||||
struct adc_sequence sequence = {
|
||||
.buffer = bufs,
|
||||
.buffer_size = sizeof(bufs),
|
||||
};
|
||||
const struct analog_axis_channel_config *axis_cfg_0 = &cfg->channel_cfg[0];
|
||||
int err;
|
||||
int i;
|
||||
|
||||
adc_sequence_init_dt(&axis_cfg_0->adc, &sequence);
|
||||
|
||||
for (i = 0; i < cfg->num_channels; i++) {
|
||||
const struct analog_axis_channel_config *axis_cfg = &cfg->channel_cfg[i];
|
||||
|
||||
sequence.channels |= BIT(axis_cfg->adc.channel_id);
|
||||
}
|
||||
|
||||
err = adc_read(axis_cfg_0->adc.dev, &sequence);
|
||||
if (err < 0) {
|
||||
LOG_ERR("Could not read (%d)", err);
|
||||
return;
|
||||
}
|
||||
|
||||
k_mutex_lock(&data->cal_lock, K_FOREVER);
|
||||
|
||||
for (i = 0; i < cfg->num_channels; i++) {
|
||||
const struct analog_axis_channel_config *axis_cfg = &cfg->channel_cfg[i];
|
||||
struct analog_axis_channel_data *axis_data = &cfg->channel_data[i];
|
||||
struct analog_axis_calibration *cal = &cfg->calibration[i];
|
||||
int16_t in_range = cal->in_max - cal->in_min;
|
||||
int16_t out_range = axis_cfg->out_max - axis_cfg->out_min;
|
||||
int32_t raw_val = bufs[i];
|
||||
|
||||
if (axis_cfg->invert) {
|
||||
raw_val *= -1;
|
||||
}
|
||||
|
||||
if (data->raw_data_cb != NULL) {
|
||||
data->raw_data_cb(dev, i, raw_val);
|
||||
}
|
||||
|
||||
LOG_DBG("%s: ch %d: raw_val: %d", dev->name, i, raw_val);
|
||||
|
||||
out = CLAMP((raw_val - cal->in_min) * out_range / in_range + axis_cfg->out_min,
|
||||
axis_cfg->out_min, axis_cfg->out_max);
|
||||
|
||||
if (cal->out_deadzone > 0) {
|
||||
int16_t center = DIV_ROUND_CLOSEST(
|
||||
axis_cfg->out_max + axis_cfg->out_min, 2);
|
||||
if (abs(out - center) < cal->out_deadzone) {
|
||||
out = center;
|
||||
}
|
||||
}
|
||||
|
||||
if (axis_data->last_out != out) {
|
||||
input_report_abs(dev, axis_cfg->axis, out, true, K_FOREVER);
|
||||
}
|
||||
axis_data->last_out = out;
|
||||
}
|
||||
|
||||
k_mutex_unlock(&data->cal_lock);
|
||||
}
|
||||
|
||||
static void analog_axis_thread(void *arg1, void *arg2, void *arg3)
|
||||
{
|
||||
const struct device *dev = arg1;
|
||||
const struct analog_axis_config *cfg = dev->config;
|
||||
struct analog_axis_data *data = dev->data;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cfg->num_channels; i++) {
|
||||
const struct analog_axis_channel_config *axis_cfg = &cfg->channel_cfg[i];
|
||||
|
||||
if (!adc_is_ready_dt(&axis_cfg->adc)) {
|
||||
LOG_ERR("ADC controller device not ready");
|
||||
return;
|
||||
}
|
||||
|
||||
err = adc_channel_setup_dt(&axis_cfg->adc);
|
||||
if (err < 0) {
|
||||
LOG_ERR("Could not setup channel #%d (%d)", i, err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
k_timer_init(&data->timer, NULL, NULL);
|
||||
k_timer_start(&data->timer,
|
||||
K_MSEC(cfg->poll_period_ms), K_MSEC(cfg->poll_period_ms));
|
||||
|
||||
while (true) {
|
||||
analog_axis_loop(dev);
|
||||
k_timer_status_sync(&data->timer);
|
||||
}
|
||||
}
|
||||
|
||||
static int analog_axis_init(const struct device *dev)
|
||||
{
|
||||
struct analog_axis_data *data = dev->data;
|
||||
k_tid_t tid;
|
||||
|
||||
k_mutex_init(&data->cal_lock);
|
||||
|
||||
tid = k_thread_create(&data->thread, data->thread_stack,
|
||||
K_KERNEL_STACK_SIZEOF(data->thread_stack),
|
||||
analog_axis_thread, (void *)dev, NULL, NULL,
|
||||
CONFIG_INPUT_ANALOG_AXIS_THREAD_PRIORITY,
|
||||
0, K_NO_WAIT);
|
||||
if (!tid) {
|
||||
LOG_ERR("thread creation failed");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
k_thread_name_set(&data->thread, dev->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ANALOG_AXIS_CHANNEL_CFG_DEF(node_id) \
|
||||
{ \
|
||||
.adc = ADC_DT_SPEC_GET(node_id), \
|
||||
.out_min = (int16_t)DT_PROP(node_id, out_min), \
|
||||
.out_max = (int16_t)DT_PROP(node_id, out_max), \
|
||||
.axis = DT_PROP(node_id, zephyr_axis), \
|
||||
.invert = DT_PROP(node_id, invert), \
|
||||
}
|
||||
|
||||
#define ANALOG_AXIS_CHANNEL_CAL_DEF(node_id) \
|
||||
{ \
|
||||
.in_min = (int16_t)DT_PROP(node_id, in_min), \
|
||||
.in_max = (int16_t)DT_PROP(node_id, in_max), \
|
||||
.out_deadzone = DT_PROP(node_id, out_deadzone), \
|
||||
}
|
||||
|
||||
#define ANALOG_AXIS_INIT(inst) \
|
||||
static const struct analog_axis_channel_config analog_axis_channel_cfg_##inst[] = { \
|
||||
DT_INST_FOREACH_CHILD_STATUS_OKAY_SEP(inst, ANALOG_AXIS_CHANNEL_CFG_DEF, (,)) \
|
||||
}; \
|
||||
\
|
||||
static struct analog_axis_channel_data \
|
||||
analog_axis_channel_data_##inst[ARRAY_SIZE(analog_axis_channel_cfg_##inst)]; \
|
||||
\
|
||||
static struct analog_axis_calibration \
|
||||
analog_axis_calibration##inst[ARRAY_SIZE(analog_axis_channel_cfg_##inst)] = { \
|
||||
DT_INST_FOREACH_CHILD_STATUS_OKAY_SEP( \
|
||||
inst, ANALOG_AXIS_CHANNEL_CAL_DEF, (,)) \
|
||||
}; \
|
||||
\
|
||||
static const struct analog_axis_config analog_axis_cfg_##inst = { \
|
||||
.poll_period_ms = DT_INST_PROP(inst, poll_period_ms), \
|
||||
.channel_cfg = analog_axis_channel_cfg_##inst, \
|
||||
.channel_data = analog_axis_channel_data_##inst, \
|
||||
.calibration = analog_axis_calibration##inst, \
|
||||
.num_channels = ARRAY_SIZE(analog_axis_channel_cfg_##inst), \
|
||||
}; \
|
||||
\
|
||||
static struct analog_axis_data analog_axis_data_##inst; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(inst, analog_axis_init, NULL, \
|
||||
&analog_axis_data_##inst, &analog_axis_cfg_##inst, \
|
||||
POST_KERNEL, CONFIG_INPUT_INIT_PRIORITY, NULL);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(ANALOG_AXIS_INIT)
|
111
drivers/input/input_analog_axis_settings.c
Normal file
111
drivers/input/input_analog_axis_settings.c
Normal file
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Copyright 2023 Google LLC
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/input/input_analog_axis.h>
|
||||
#include <zephyr/input/input_analog_axis_settings.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/settings/settings.h>
|
||||
#include <zephyr/sys/printk.h>
|
||||
|
||||
LOG_MODULE_REGISTER(analog_axis_settings, CONFIG_INPUT_LOG_LEVEL);
|
||||
|
||||
#define ANALOG_AXIS_SETTINGS_PATH_MAX 32
|
||||
|
||||
#define MAX_AXES CONFIG_INPUT_ANALOG_AXIS_SETTINGS_MAX_AXES
|
||||
|
||||
static void analog_axis_calibration_log(const struct device *dev)
|
||||
{
|
||||
struct analog_axis_calibration cal;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < analog_axis_num_axes(dev); i++) {
|
||||
analog_axis_calibration_get(dev, i, &cal);
|
||||
|
||||
LOG_INF("%s: ch: %d min: %d max: %d deadzone: %d",
|
||||
dev->name, i, cal.in_min, cal.in_max, cal.out_deadzone);
|
||||
}
|
||||
}
|
||||
|
||||
static int analog_axis_calibration_load(const char *key, size_t len_rd,
|
||||
settings_read_cb read_cb, void *cb_arg)
|
||||
{
|
||||
const struct device *dev;
|
||||
struct analog_axis_calibration cal[MAX_AXES];
|
||||
int axes;
|
||||
char dev_name[ANALOG_AXIS_SETTINGS_PATH_MAX];
|
||||
const char *next;
|
||||
int nlen;
|
||||
ssize_t len;
|
||||
|
||||
nlen = settings_name_next(key, &next);
|
||||
if (nlen + 1 > sizeof(dev_name)) {
|
||||
LOG_ERR("Setting name too long: %d", nlen);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(dev_name, key, nlen);
|
||||
dev_name[nlen] = '\0';
|
||||
|
||||
dev = device_get_binding(dev_name);
|
||||
if (dev == NULL) {
|
||||
LOG_ERR("Cannot restore: device %s not available", dev_name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
len = read_cb(cb_arg, cal, sizeof(cal));
|
||||
if (len < 0) {
|
||||
LOG_ERR("Data restore error: %d", len);
|
||||
}
|
||||
|
||||
axes = analog_axis_num_axes(dev);
|
||||
if (len != sizeof(struct analog_axis_calibration) * axes) {
|
||||
LOG_ERR("Invalid settings data length: %d, expected %d",
|
||||
len, sizeof(struct analog_axis_calibration) * axes);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
for (int i = 0; i < axes; i++) {
|
||||
analog_axis_calibration_set(dev, i, &cal[i]);
|
||||
}
|
||||
|
||||
analog_axis_calibration_log(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SETTINGS_STATIC_HANDLER_DEFINE(analog_axis, "aa-cal", NULL,
|
||||
analog_axis_calibration_load, NULL, NULL);
|
||||
|
||||
int analog_axis_calibration_save(const struct device *dev)
|
||||
{
|
||||
struct analog_axis_calibration cal[MAX_AXES];
|
||||
int axes;
|
||||
char path[ANALOG_AXIS_SETTINGS_PATH_MAX];
|
||||
int ret;
|
||||
|
||||
analog_axis_calibration_log(dev);
|
||||
|
||||
ret = snprintk(path, sizeof(path), "aa-cal/%s", dev->name);
|
||||
if (ret < 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
axes = analog_axis_num_axes(dev);
|
||||
for (int i = 0; i < axes; i++) {
|
||||
analog_axis_calibration_get(dev, i, &cal[i]);
|
||||
}
|
||||
|
||||
ret = settings_save_one(path, &cal[0],
|
||||
sizeof(struct analog_axis_calibration) * axes);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Settings save errord: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
90
dts/bindings/input/analog-axis.yaml
Normal file
90
dts/bindings/input/analog-axis.yaml
Normal file
|
@ -0,0 +1,90 @@
|
|||
# Copyright 2023 Google LLC
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: |
|
||||
ADC based analog axis input device
|
||||
|
||||
Implement an input device generating absolute axis events by periodically
|
||||
reading from some ADC channels.
|
||||
|
||||
Example configuration:
|
||||
|
||||
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
||||
|
||||
analog_axis {
|
||||
compatible = "analog-axis";
|
||||
poll-period-ms = <15>;
|
||||
axis-x {
|
||||
io-channels = <&adc 0>;
|
||||
out-deadzone = <8>;
|
||||
in-min = <100>;
|
||||
in-max = <800>;
|
||||
zephyr,axis = <INPUT_ABS_X>;
|
||||
};
|
||||
};
|
||||
|
||||
compatible: "analog-axis"
|
||||
|
||||
include: base.yaml
|
||||
|
||||
properties:
|
||||
poll-period-ms:
|
||||
type: int
|
||||
default: 15
|
||||
description: |
|
||||
How often to get new ADC samples for the various configured axes in
|
||||
milliseconds. Defaults to 15ms if unspecified.
|
||||
|
||||
child-binding:
|
||||
properties:
|
||||
io-channels:
|
||||
type: phandle-array
|
||||
required: true
|
||||
description: |
|
||||
ADC IO channel to use.
|
||||
|
||||
out-min:
|
||||
type: int
|
||||
default: 0
|
||||
description: |
|
||||
Minimum value to output on input events. Defaults to 0 if unspecified.
|
||||
|
||||
out-max:
|
||||
type: int
|
||||
default: 255
|
||||
description: |
|
||||
Maximum value to output on input events. Defaults to 255 if
|
||||
unspecified.
|
||||
|
||||
out-deadzone:
|
||||
type: int
|
||||
default: 0
|
||||
description: |
|
||||
Deadzone for the output center value. If specified output values
|
||||
between the center of the range plus or minus this value will be
|
||||
reported as center. Defaults to 0, no deadzone.
|
||||
|
||||
in-min:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
Input value that corresponds to the minimum output value.
|
||||
|
||||
in-max:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
Input value that corresponds to the maximum output value.
|
||||
|
||||
zephyr,axis:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
The input code for the axis to report for the device, typically any of
|
||||
INPUT_ABS_*.
|
||||
|
||||
invert:
|
||||
type: boolean
|
||||
description: |
|
||||
If set, invert the raw ADC value before processing it. Useful for
|
||||
differential channels.
|
97
include/zephyr/input/input_analog_axis.h
Normal file
97
include/zephyr/input/input_analog_axis.h
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright 2023 Google LLC
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_INPUT_ANALOG_AXIS_H_
|
||||
#define ZEPHYR_INCLUDE_INPUT_ANALOG_AXIS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <zephyr/device.h>
|
||||
|
||||
/**
|
||||
* @brief Analog axis API
|
||||
* @defgroup input_analog_axis Analog axis API
|
||||
* @ingroup io_interfaces
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Analog axis calibration data structure.
|
||||
*
|
||||
* Holds the calibration data for a single analog axis. Initial values are set
|
||||
* from the devicetree and can be changed by the applicatoin in runtime using
|
||||
* @ref analog_axis_calibration_set and @ref analog_axis_calibration_get.
|
||||
*/
|
||||
struct analog_axis_calibration {
|
||||
/** Input value that corresponds to the minimum output value. */
|
||||
int16_t in_min;
|
||||
/** Input value that corresponds to the maximum output value. */
|
||||
int16_t in_max;
|
||||
/** Output value deadzone relative to the output range. */
|
||||
uint16_t out_deadzone;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Analog axis raw data callback.
|
||||
*
|
||||
* @param dev Analog axis device.
|
||||
* @param channel Channel number.
|
||||
* @param raw_val Raw value for the channel.
|
||||
*/
|
||||
typedef void (*analog_axis_raw_data_t)(const struct device *dev,
|
||||
int channel, int16_t raw_val);
|
||||
|
||||
/**
|
||||
* @brief Set a raw data callback.
|
||||
*
|
||||
* Set a callback to receive raw data for the specified analog axis device.
|
||||
* This is meant to be use in the application to acquire the data to use for
|
||||
* calibration. Set cb to NULL to disable the callback.
|
||||
*
|
||||
* @param dev Analog axis device.
|
||||
* @param cb An analog_axis_raw_data_t callback to use, NULL disable.
|
||||
*/
|
||||
void analog_axis_set_raw_data_cb(const struct device *dev, analog_axis_raw_data_t cb);
|
||||
|
||||
/**
|
||||
* @brief Get the number of defined axes.
|
||||
*
|
||||
* @retval n The number of defined axes for dev.
|
||||
*/
|
||||
int analog_axis_num_axes(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief Get the axis calibration data.
|
||||
*
|
||||
* @param dev Analog axis device.
|
||||
* @param channel Channel number.
|
||||
* @param cal Pointer to an analog_axis_calibration structure that is going to
|
||||
* get set with the current calibration data.
|
||||
*
|
||||
* @retval 0 If successful.
|
||||
* @retval -EINVAL If the specified channel is not valid.
|
||||
*/
|
||||
int analog_axis_calibration_get(const struct device *dev,
|
||||
int channel,
|
||||
struct analog_axis_calibration *cal);
|
||||
|
||||
/**
|
||||
* @brief Set the axis calibration data.
|
||||
*
|
||||
* @param dev Analog axis device.
|
||||
* @param channel Channel number.
|
||||
* @param cal Pointer to an analog_axis_calibration structure with the new
|
||||
* calibration data
|
||||
*
|
||||
* @retval 0 If successful.
|
||||
* @retval -EINVAL If the specified channel is not valid.
|
||||
*/
|
||||
int analog_axis_calibration_set(const struct device *dev,
|
||||
int channel,
|
||||
struct analog_axis_calibration *cal);
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_INPUT_ANALOG_AXIS_H_ */
|
33
include/zephyr/input/input_analog_axis_settings.h
Normal file
33
include/zephyr/input/input_analog_axis_settings.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright 2023 Google LLC
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_INPUT_ANALOG_AXIS_SETTINGS_H_
|
||||
#define ZEPHYR_INCLUDE_INPUT_ANALOG_AXIS_SETTINGS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <zephyr/device.h>
|
||||
|
||||
/**
|
||||
* @addtogroup input_analog_axis
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Save the calibration data.
|
||||
*
|
||||
* Save the calibration data permanently on the specifided device, requires the
|
||||
* the @ref settings subsystem to be configured and initialized.
|
||||
*
|
||||
* @param dev Analog axis device.
|
||||
*
|
||||
* @retval 0 If successful.
|
||||
* @retval -errno In case of any other error.
|
||||
*/
|
||||
int analog_axis_calibration_save(const struct device *dev);
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_INPUT_ANALOG_AXIS_SETTINGS_H_ */
|
|
@ -9,6 +9,22 @@
|
|||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
test_adc: adc@adc0adc0 {
|
||||
compatible = "vnd,adc";
|
||||
reg = <0xadc0adc0 0x1000>;
|
||||
#io-channel-cells = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
status = "okay";
|
||||
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
zephyr,gain = "ADC_GAIN_1";
|
||||
zephyr,reference = "ADC_REF_VDD_1";
|
||||
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
||||
};
|
||||
};
|
||||
|
||||
test_gpio: gpio@0 {
|
||||
compatible = "vnd,gpio";
|
||||
gpio-controller;
|
||||
|
@ -71,6 +87,20 @@
|
|||
idle-timeout-ms = <200>;
|
||||
};
|
||||
|
||||
analog_axis {
|
||||
compatible = "analog-axis";
|
||||
axis-x {
|
||||
io-channels = <&test_adc 0>;
|
||||
out-min = <(-127)>;
|
||||
out-max = <127>;
|
||||
out-deadzone = <8>;
|
||||
in-min = <(-100)>;
|
||||
in-max = <100>;
|
||||
zephyr,axis = <0>;
|
||||
invert;
|
||||
};
|
||||
};
|
||||
|
||||
longpress: longpress {
|
||||
input = <&longpress>;
|
||||
compatible = "zephyr,input-longpress";
|
||||
|
|
|
@ -19,3 +19,8 @@ tests:
|
|||
drivers.input.kbd_16_bit:
|
||||
extra_configs:
|
||||
- CONFIG_INPUT_KBD_MATRIX_16_BIT_ROW=y
|
||||
|
||||
drivers.input.analog_axis:
|
||||
extra_configs:
|
||||
- CONFIG_ADC=y
|
||||
- CONFIG_SETTINGS=y
|
||||
|
|
Loading…
Reference in a new issue