Skip to content

drivers: input: cap12xx Add properties for sensitivity and guard signal #89503

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 140 additions & 39 deletions drivers/input/input_cap12xx.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,23 @@
#include <zephyr/drivers/gpio.h>
#include <zephyr/input/input.h>
#include <zephyr/logging/log.h>
#include <zephyr/math/ilog2.h>
LOG_MODULE_REGISTER(cap12xx, CONFIG_INPUT_LOG_LEVEL);

#define REG_MAIN_CONTROL 0x00
#define CONTROL_INT 0x01
#define REG_MAIN_CONTROL 0x00
#define MAIN_CONTROL_GAIN_MASK GENMASK(7, 6)
#define MAIN_CONTROL_GAIN_SHIFT 6

#define CONTROL_INT 0x01

#define REG_INPUT_STATUS 0x03

#define REG_SENSITIVITY_CONTROL 0x1F
#define DELTA_SENSE_BITS 3
#define DELTA_SENSE_SHIFT 4
#define DELTA_SENSE_MASK GENMASK(6, 4)
#define DELTA_SENSE_MAX GENMASK(DELTA_SENSE_BITS - 1, 0)

#define REG_INTERRUPT_ENABLE 0x27
#define INTERRUPT_ENABLE 0xFF
#define INTERRUPT_DISABLE 0x00
Expand All @@ -25,13 +35,24 @@ LOG_MODULE_REGISTER(cap12xx, CONFIG_INPUT_LOG_LEVEL);
#define REPEAT_ENABLE 0xFF
#define REPEAT_DISABLE 0x00

#define REG_SIGNAL_GUARD_ENABLE 0x29

#define REG_CALIB_SENSITIVITY_CONFIG1 0x80
#define REG_CALIB_SENSITIVITY_CONFIG2 0x81
#define CALSENS_BITS 2
#define NUM_CALSENS_PER_REG 4

struct cap12xx_config {
struct i2c_dt_spec i2c;
const uint8_t input_channels;
const uint16_t *input_codes;
struct gpio_dt_spec *int_gpio;
bool repeat;
const uint16_t poll_interval_ms;
const uint8_t sensor_gain;
const uint8_t sensitivity_delta_sense;
const uint8_t *signal_guard;
const uint8_t *calib_sensitivity;
};

struct cap12xx_data {
Expand All @@ -45,11 +66,11 @@ struct cap12xx_data {
static int cap12xx_clear_interrupt(const struct i2c_dt_spec *i2c)
{
uint8_t ctrl;
int r;
int ret;

r = i2c_reg_read_byte_dt(i2c, REG_MAIN_CONTROL, &ctrl);
if (r < 0) {
return r;
ret = i2c_reg_read_byte_dt(i2c, REG_MAIN_CONTROL, &ctrl);
if (ret < 0) {
return ret;
}

ctrl = ctrl & ~CONTROL_INT;
Expand All @@ -63,25 +84,65 @@ static int cap12xx_enable_interrupt(const struct i2c_dt_spec *i2c, bool enable)
return i2c_reg_write_byte_dt(i2c, REG_INTERRUPT_ENABLE, intr);
}

static int cap12xx_set_sensor_gain(const struct i2c_dt_spec *i2c, uint8_t gain)
{
uint8_t regval = gain << MAIN_CONTROL_GAIN_SHIFT;

return i2c_reg_update_byte_dt(i2c, REG_MAIN_CONTROL, MAIN_CONTROL_GAIN_MASK, regval);
}

static int cap12xx_set_sensitivity(const struct i2c_dt_spec *i2c, uint8_t sensitivity)
{
uint8_t regval = sensitivity << DELTA_SENSE_SHIFT;

return i2c_reg_update_byte_dt(i2c, REG_SENSITIVITY_CONTROL, DELTA_SENSE_MASK, regval);
}

static int cap12xx_set_calsens(const struct i2c_dt_spec *i2c, const uint8_t *calsens,
uint8_t channels)
{
int ret;
uint8_t regval;

for (uint8_t i = 0; i < channels; i += NUM_CALSENS_PER_REG) {
regval = 0;
for (uint8_t j = 0; j < NUM_CALSENS_PER_REG && i + j < channels; j++) {
/* Convert the enumerated sensitivity to the corresponding register value */
regval |= (ilog2(calsens[i + j]) << (CALSENS_BITS * j));
}
if (i == 0) {
ret = i2c_reg_write_byte_dt(i2c, REG_CALIB_SENSITIVITY_CONFIG1, regval);
} else {
ret = i2c_reg_write_byte_dt(i2c, REG_CALIB_SENSITIVITY_CONFIG2, regval);
}

if (ret) {
return ret;
}
}

return 0;
}

static int cap12xx_process(const struct device *dev)
{
const struct cap12xx_config *config = dev->config;
struct cap12xx_data *data = dev->data;
int r;
int ret;
uint8_t input_state;

/*
* Clear INT bit to clear SENSOR INPUT STATUS bits.
* Note that this is also required in polling mode.
*/
r = cap12xx_clear_interrupt(&config->i2c);
ret = cap12xx_clear_interrupt(&config->i2c);

if (r < 0) {
return r;
if (ret < 0) {
return ret;
}
r = i2c_reg_read_byte_dt(&config->i2c, REG_INPUT_STATUS, &input_state);
if (r < 0) {
return r;
ret = i2c_reg_read_byte_dt(&config->i2c, REG_INPUT_STATUS, &input_state);
if (ret < 0) {
return ret;
}

if (config->int_gpio == NULL) {
Expand Down Expand Up @@ -129,7 +190,8 @@ static int cap12xx_init(const struct device *dev)
{
const struct cap12xx_config *config = dev->config;
struct cap12xx_data *data = dev->data;
int r;
uint8_t guarded_channels = 0;
int ret;

if (!device_is_ready(config->i2c.bus)) {
LOG_ERR("I2C controller device not ready");
Expand All @@ -140,13 +202,43 @@ static int cap12xx_init(const struct device *dev)

k_work_init(&data->work, cap12xx_work_handler);

for (uint8_t i = 0; i < config->input_channels; i++) {
if (config->signal_guard[i]) {
guarded_channels |= BIT(i);
}
}
ret = i2c_reg_write_byte_dt(&config->i2c, REG_SIGNAL_GUARD_ENABLE, guarded_channels);
if (ret < 0) {
LOG_ERR("Could not set guarded channels");
return ret;
}
ret = cap12xx_set_calsens(&config->i2c, config->calib_sensitivity, config->input_channels);
if (ret < 0) {
LOG_ERR("Could not set calibration sensitivities");
return ret;
}
/* Convert the enumerated gain to the corresponding register value */
ret = cap12xx_set_sensor_gain(&config->i2c, ilog2(config->sensor_gain));
if (ret < 0) {
LOG_ERR("Could not set analog gain");
return ret;
}
/* Convert the enumerated sensitivity to the corresponding register value,
* which is in reverse order
*/
ret = cap12xx_set_sensitivity(&config->i2c,
DELTA_SENSE_MAX - ilog2(config->sensitivity_delta_sense));
if (ret < 0) {
LOG_ERR("Could not set sensitivity");
return ret;
}
if (config->int_gpio == NULL) {
LOG_DBG("cap12xx driver in polling mode");
k_timer_init(&data->poll_timer, cap12xx_timer_handler, NULL);
r = cap12xx_enable_interrupt(&config->i2c, true);
if (r < 0) {
ret = cap12xx_enable_interrupt(&config->i2c, true);
if (ret < 0) {
LOG_ERR("Could not configure interrupt");
return r;
return ret;
}
k_timer_start(&data->poll_timer, K_MSEC(config->poll_interval_ms),
K_MSEC(config->poll_interval_ms));
Expand All @@ -158,49 +250,50 @@ static int cap12xx_init(const struct device *dev)
return -ENODEV;
}

r = gpio_pin_configure_dt(config->int_gpio, GPIO_INPUT);
if (r < 0) {
ret = gpio_pin_configure_dt(config->int_gpio, GPIO_INPUT);
if (ret < 0) {
LOG_ERR("Could not configure interrupt GPIO pin");
return r;
return ret;
}

r = gpio_pin_interrupt_configure_dt(config->int_gpio, GPIO_INT_EDGE_TO_ACTIVE);
if (r < 0) {
ret = gpio_pin_interrupt_configure_dt(config->int_gpio, GPIO_INT_EDGE_TO_ACTIVE);
if (ret < 0) {
LOG_ERR("Could not configure interrupt GPIO interrupt");
return r;
return ret;
}

gpio_init_callback(&data->int_gpio_cb, cap12xx_isr_handler,
BIT(config->int_gpio->pin));

r = gpio_add_callback_dt(config->int_gpio, &data->int_gpio_cb);
if (r < 0) {
ret = gpio_add_callback_dt(config->int_gpio, &data->int_gpio_cb);
if (ret < 0) {
LOG_ERR("Could not set gpio callback");
return r;
return ret;
}

r = cap12xx_clear_interrupt(&config->i2c);
if (r < 0) {
ret = cap12xx_clear_interrupt(&config->i2c);
if (ret < 0) {
LOG_ERR("Could not clear interrupt");
return r;
return ret;
}
r = cap12xx_enable_interrupt(&config->i2c, true);
if (r < 0) {
ret = cap12xx_enable_interrupt(&config->i2c, true);
if (ret < 0) {
LOG_ERR("Could not configure interrupt");
return r;
return ret;
}
if (config->repeat) {
r = i2c_reg_write_byte_dt(&config->i2c, REG_REPEAT_ENABLE, REPEAT_ENABLE);
if (r < 0) {
ret = i2c_reg_write_byte_dt(&config->i2c, REG_REPEAT_ENABLE, REPEAT_ENABLE);
if (ret < 0) {
LOG_ERR("Could not disable repeated interrupts");
return r;
return ret;
}
LOG_DBG("cap12xx enabled repeated interrupts");
} else {
r = i2c_reg_write_byte_dt(&config->i2c, REG_REPEAT_ENABLE, REPEAT_DISABLE);
if (r < 0) {
ret = i2c_reg_write_byte_dt(&config->i2c, REG_REPEAT_ENABLE,
REPEAT_DISABLE);
if (ret < 0) {
LOG_ERR("Could not enable repeated interrupts");
return r;
return ret;
}
LOG_DBG("cap12xx disabled repeated interrupts");
}
Expand All @@ -214,14 +307,22 @@ static int cap12xx_init(const struct device *dev)
static struct gpio_dt_spec cap12xx_int_gpio_##index = \
GPIO_DT_SPEC_INST_GET(index, int_gpios);)) \
static const uint16_t cap12xx_input_codes_##index[] = DT_INST_PROP(index, input_codes); \
static const uint8_t cap12xx_signal_guard_##index[] = \
DT_INST_PROP(index, signal_guard); \
static const uint8_t cap12xx_calib_sensitivity_##index[] = \
DT_INST_PROP(index, calib_sensitivity); \
static const struct cap12xx_config cap12xx_config_##index = { \
.i2c = I2C_DT_SPEC_INST_GET(index), \
.input_channels = DT_INST_PROP_LEN(index, input_codes), \
.input_codes = cap12xx_input_codes_##index, \
IF_ENABLED(DT_INST_NODE_HAS_PROP(index, int_gpios), ( \
.int_gpio = &cap12xx_int_gpio_##index,)) \
.repeat = DT_INST_PROP(index, repeat), \
.poll_interval_ms = DT_INST_PROP_OR(index, poll_interval_ms, 10)}; \
.poll_interval_ms = DT_INST_PROP(index, poll_interval_ms), \
.sensor_gain = DT_INST_PROP(index, sensor_gain), \
.sensitivity_delta_sense = DT_INST_PROP(index, sensitivity_delta_sense), \
.signal_guard = cap12xx_signal_guard_##index, \
.calib_sensitivity = cap12xx_calib_sensitivity_##index}; \
static struct cap12xx_data cap12xx_data_##index; \
DEVICE_DT_INST_DEFINE(index, cap12xx_init, NULL, &cap12xx_data_##index, \
&cap12xx_config_##index, POST_KERNEL, CONFIG_INPUT_INIT_PRIORITY, \
Expand Down
50 changes: 50 additions & 0 deletions dts/bindings/input/microchip,cap12xx.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ properties:
poll-interval-ms:
type: int
default: 10
description: |
Polling interval in ms when not using interrupt mode.
Expand All @@ -28,3 +29,52 @@ properties:
required: true
description: |
Array of input event key codes (INPUT_KEY_* or INPUT_BTN_*).
sensor-gain:
type: int
enum:
- 1
- 2
- 4
- 8
default: 1
description: |
Defines the gain of the sensor circuitry. This
effectively controls the sensitivity, as a
smaller delta capacitance is required to
generate the same delta count values.
sensitivity-delta-sense:
type: int
enum: [1, 2, 4, 8, 16, 32, 64, 128]
default: 32
description:
Controls the sensitivity multiplier of a touch detection.
Higher value means more sensitive settings.
At the more sensitive settings, touches are detected for a smaller delta
capacitance corresponding to a "lighter" touch.

signal-guard:
type: array
enum: [0, 1]
default: [0, 0, 0]
description: |
0 - off
1 - on
The signal guard isolates the signal from virtual grounds.
If enabled then the behavior of the channel is changed to signal guard.
The number of entries must correspond to the number of channels.
calib-sensitivity:
type: array
enum: [1, 2, 4]
default: [1, 1, 1]
description: |
Specifies an array of numeric values that controls the gain
used by the calibration routine to enable sensor inputs
to be more sensitive for proximity detection.
Gain is based on touch pad capacitance range
1 - 5-50pF
2 - 0-25pF
4 - 0-12.5pF
The number of entries must correspond to the number of channels.
4 changes: 4 additions & 0 deletions tests/drivers/build_all/input/app.overlay
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,10 @@
reg = <0x3>;
int-gpios = <&test_gpio 0 0>;
input-codes = <0 1 2>;
sensor-gain = <1>;
sensitivity-delta-sense = <32>;
signal-guard = <0 0 0>;
calib-sensitivity = <1 1 1>;
};

stmpe811@4 {
Expand Down