input: analog_axis: rework deadzone calibration code

Rework the data scaling algorithm for the "deadzone" mode so that the
deadzone is subtracted from the input rather than from the output. This
makes the whole output range usable rather than making the output jump
from the center value to the minimum deadzone range.

This changes the calibration data structure as well so now all values
refer to the input data, which is more coherent.

Signed-off-by: Fabio Baltieri <fabiobaltieri@google.com>
This commit is contained in:
Fabio Baltieri 2024-03-18 01:25:46 +00:00 committed by Anas Nashif
parent 1b2bb0dbf2
commit 55c14e6fa6
6 changed files with 64 additions and 21 deletions

View File

@ -120,6 +120,12 @@ General Purpose I/O (GPIO)
Input
=====
* The ``analog-axis`` deadzone calibration value has been changed to be
relative to the raw ADC values, similarly to min and max. The data structures
and properties have been renamed to reflect that (from ``out-deadzone`` to
``in-deadzone``) and when migrating to the new definition the value should be
scaled accordingly.
Interrupt Controller
====================

View File

@ -101,6 +101,48 @@ int analog_axis_calibration_set(const struct device *dev,
return 0;
}
static int32_t analog_axis_out_deadzone(const struct device *dev,
int channel,
int32_t raw_val)
{
const struct analog_axis_config *cfg = dev->config;
const struct analog_axis_channel_config *axis_cfg = &cfg->channel_cfg[channel];
struct analog_axis_calibration *cal = &cfg->calibration[channel];
int16_t in_range = cal->in_max - cal->in_min;
int16_t out_range = axis_cfg->out_max - axis_cfg->out_min;
int16_t in_mid = DIV_ROUND_CLOSEST(cal->in_min + cal->in_max, 2);
int16_t in_min = cal->in_min;
if (abs(raw_val - in_mid) < cal->in_deadzone) {
return DIV_ROUND_CLOSEST(axis_cfg->out_max + axis_cfg->out_min, 2);
}
in_range -= cal->in_deadzone * 2;
in_min += cal->in_deadzone;
if (raw_val < in_mid) {
raw_val += cal->in_deadzone;
} else {
raw_val -= cal->in_deadzone;
}
return DIV_ROUND_CLOSEST((raw_val - in_min) * out_range, in_range) + axis_cfg->out_min;
}
static int32_t analog_axis_out_linear(const struct device *dev,
int channel,
int32_t raw_val)
{
const struct analog_axis_config *cfg = dev->config;
const struct analog_axis_channel_config *axis_cfg = &cfg->channel_cfg[channel];
struct analog_axis_calibration *cal = &cfg->calibration[channel];
int16_t in_range = cal->in_max - cal->in_min;
int16_t out_range = axis_cfg->out_max - axis_cfg->out_min;
return DIV_ROUND_CLOSEST((raw_val - cal->in_min) * out_range, in_range) + axis_cfg->out_min;
}
static void analog_axis_loop(const struct device *dev)
{
const struct analog_axis_config *cfg = dev->config;
@ -135,8 +177,6 @@ static void analog_axis_loop(const struct device *dev)
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) {
@ -149,17 +189,14 @@ static void analog_axis_loop(const struct device *dev)
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 (cal->in_deadzone > 0) {
out = analog_axis_out_deadzone(dev, i, raw_val);
} else {
out = analog_axis_out_linear(dev, i, raw_val);
}
out = CLAMP(out, axis_cfg->out_min, axis_cfg->out_max);
if (axis_data->last_out != out) {
input_report_abs(dev, axis_cfg->axis, out, true, K_FOREVER);
}
@ -237,7 +274,7 @@ static int analog_axis_init(const struct device *dev)
{ \
.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), \
.in_deadzone = DT_PROP(node_id, in_deadzone), \
}
#define ANALOG_AXIS_INIT(inst) \

View File

@ -27,7 +27,7 @@ static void analog_axis_calibration_log(const struct device *dev)
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);
dev->name, i, cal.in_min, cal.in_max, cal.in_deadzone);
}
}

View File

@ -16,7 +16,7 @@ description: |
poll-period-ms = <15>;
axis-x {
io-channels = <&adc 0>;
out-deadzone = <8>;
in-deadzone = <50>;
in-min = <100>;
in-max = <800>;
zephyr,axis = <INPUT_ABS_X>;
@ -56,13 +56,13 @@ child-binding:
Maximum value to output on input events. Defaults to 255 if
unspecified.
out-deadzone:
in-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.
Deadzone for the input center value. If specified input 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

View File

@ -29,8 +29,8 @@ struct analog_axis_calibration {
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;
/** Input value center deadzone. */
uint16_t in_deadzone;
};
/**

View File

@ -136,7 +136,7 @@
io-channels = <&test_adc 0>;
out-min = <(-127)>;
out-max = <127>;
out-deadzone = <8>;
in-deadzone = <8>;
in-min = <(-100)>;
in-max = <100>;
zephyr,axis = <0>;