driver: input: added input_tsc_keys
input_tsc_keys to detect key press releases using STM32 TSC Signed-off-by: Arif Balik <arifbalik@outlook.com>
This commit is contained in:
committed by
Benjamin Cabé
parent
9aea8f48aa
commit
1c8f06c7c2
@@ -150,5 +150,13 @@
|
||||
group = <6>;
|
||||
channel-ios = <TSC_IO2>;
|
||||
sampling-io = <TSC_IO1>;
|
||||
ts1 {
|
||||
compatible = "tsc-keys";
|
||||
sampling-interval-ms = <10>;
|
||||
oversampling = <10>;
|
||||
noise-threshold = <50>;
|
||||
sticky-key-timeout-ms = <10000>;
|
||||
zephyr,code = <INPUT_KEY_0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -29,6 +29,7 @@ zephyr_library_sources_ifdef(CONFIG_INPUT_PAW32XX input_paw32xx.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_INPUT_PINNACLE input_pinnacle.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_INPUT_PMW3610 input_pmw3610.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_INPUT_SBUS input_sbus.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_INPUT_STM32_TSC_KEYS input_tsc_keys.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_INPUT_STMPE811 input_stmpe811.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_INPUT_TOUCH input_touch.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_INPUT_XEC_KBD input_xec_kbd.c)
|
||||
|
||||
@@ -34,6 +34,7 @@ source "drivers/input/Kconfig.sbus"
|
||||
source "drivers/input/Kconfig.sdl"
|
||||
source "drivers/input/Kconfig.stmpe811"
|
||||
source "drivers/input/Kconfig.touch"
|
||||
source "drivers/input/Kconfig.tsc_keys"
|
||||
source "drivers/input/Kconfig.xec"
|
||||
source "drivers/input/Kconfig.xpt2046"
|
||||
# zephyr-keep-sorted-stop
|
||||
|
||||
21
drivers/input/Kconfig.tsc_keys
Normal file
21
drivers/input/Kconfig.tsc_keys
Normal file
@@ -0,0 +1,21 @@
|
||||
# Copyright (c) 2024 Arif Balik <arifbalik@outlook.com>
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config INPUT_STM32_TSC_KEYS
|
||||
bool "STM32 TSC touch library"
|
||||
default y
|
||||
depends on DT_HAS_ST_STM32_TSC_ENABLED
|
||||
select RING_BUFFER
|
||||
help
|
||||
Enable support for STM32 TSC touch library.
|
||||
|
||||
config INPUT_STM32_TSC_KEYS_BUFFER_WORD_SIZE
|
||||
int "STM32 TSC touch buffer size in words"
|
||||
default 10
|
||||
depends on INPUT_STM32_TSC_KEYS
|
||||
help
|
||||
Size of the ring buffer for the STM32 TSC touch library. The size of this
|
||||
buffer together with the sampling-interval property in the tsc-keys
|
||||
device tree node determines the time window of interest. A slope value is
|
||||
calculated between the oldest and the newest value in the buffer to
|
||||
generate a press or release input event.
|
||||
446
drivers/input/input_tsc_keys.c
Normal file
446
drivers/input/input_tsc_keys.c
Normal file
@@ -0,0 +1,446 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Arif Balik <arifbalik@outlook.com>
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <soc.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <autoconf.h>
|
||||
#include <zephyr/irq.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/devicetree.h>
|
||||
#include <zephyr/input/input.h>
|
||||
#include <zephyr/drivers/reset.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/sys/ring_buffer.h>
|
||||
#include <zephyr/drivers/pinctrl.h>
|
||||
#include <zephyr/drivers/clock_control/stm32_clock_control.h>
|
||||
|
||||
LOG_MODULE_REGISTER(tsc_keys, CONFIG_INPUT_LOG_LEVEL);
|
||||
|
||||
#define DT_DRV_COMPAT st_stm32_tsc
|
||||
|
||||
/* each group only has 4 configurable I/O */
|
||||
#define GET_GROUP_BITS(val, group) (uint32_t)(((val) & 0x0f) << ((group - 1) * 4))
|
||||
|
||||
struct stm32_tsc_group_config {
|
||||
uint8_t group;
|
||||
uint8_t channel_ios;
|
||||
uint8_t sampling_io;
|
||||
bool use_as_shield;
|
||||
};
|
||||
|
||||
typedef void (*stm32_tsc_group_ready_cb)(uint32_t count_value, void *user_data);
|
||||
|
||||
struct stm32_tsc_group_data {
|
||||
stm32_tsc_group_ready_cb cb;
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
struct stm32_tsc_config {
|
||||
const TSC_TypeDef *tsc;
|
||||
const struct stm32_pclken *pclken;
|
||||
struct reset_dt_spec reset;
|
||||
const struct pinctrl_dev_config *pcfg;
|
||||
const struct stm32_tsc_group_config *group_config;
|
||||
struct stm32_tsc_group_data *group_data;
|
||||
uint8_t group_cnt;
|
||||
|
||||
uint32_t pgpsc;
|
||||
uint8_t ctph;
|
||||
uint8_t ctpl;
|
||||
bool spread_spectrum;
|
||||
uint8_t sscpsc;
|
||||
uint8_t ssd;
|
||||
uint16_t max_count;
|
||||
bool iodef;
|
||||
bool sync_acq;
|
||||
bool sync_pol;
|
||||
void (*irq_func)(void);
|
||||
};
|
||||
|
||||
int stm32_tsc_group_register_callback(const struct device *dev, uint8_t group_idx,
|
||||
stm32_tsc_group_ready_cb cb, void *user_data)
|
||||
{
|
||||
const struct stm32_tsc_config *config = dev->config;
|
||||
|
||||
if (group_idx >= config->group_cnt) {
|
||||
LOG_ERR("%s: group index %d is out of range", dev->name, group_idx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct stm32_tsc_group_data *group_data = &config->group_data[group_idx];
|
||||
|
||||
group_data->cb = cb;
|
||||
group_data->user_data = user_data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void stm32_tsc_start(const struct device *dev)
|
||||
{
|
||||
const struct stm32_tsc_config *config = dev->config;
|
||||
|
||||
/* clear interrupts */
|
||||
sys_set_bits((mem_addr_t)&config->tsc->ICR, TSC_ICR_EOAIC | TSC_ICR_MCEIC);
|
||||
|
||||
/* enable end of acquisition and max count error interrupts */
|
||||
sys_set_bits((mem_addr_t)&config->tsc->IER, TSC_IER_EOAIE | TSC_IER_MCEIE);
|
||||
|
||||
/* TODO: When sync acqusition mode is enabled, both this bit and an external input signal
|
||||
* should be set. When the acqusition stops this bit is cleared, so even if a sync signal is
|
||||
* present, the next acqusition will not start until this bit is set again.
|
||||
*/
|
||||
/* start acquisition */
|
||||
sys_set_bit((mem_addr_t)&config->tsc->CR, TSC_CR_START_Pos);
|
||||
}
|
||||
|
||||
static int get_group_index(const struct device *dev, uint8_t group, uint8_t *group_idx)
|
||||
{
|
||||
const struct stm32_tsc_config *config = dev->config;
|
||||
const struct stm32_tsc_group_config *groups = config->group_config;
|
||||
|
||||
for (int i = 0; i < config->group_cnt; i++) {
|
||||
if (groups[i].group == group) {
|
||||
*group_idx = i;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int stm32_tsc_handle_incoming_data(const struct device *dev)
|
||||
{
|
||||
const struct stm32_tsc_config *config = dev->config;
|
||||
|
||||
if (sys_test_bit((mem_addr_t)&config->tsc->ISR, TSC_ISR_MCEF_Pos)) {
|
||||
/* clear max count error flag */
|
||||
sys_set_bit((mem_addr_t)&config->tsc->ICR, TSC_ICR_MCEIC_Pos);
|
||||
LOG_ERR("%s: max count error", dev->name);
|
||||
LOG_HEXDUMP_DBG(config->tsc, sizeof(TSC_TypeDef), "TSC Registers");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (sys_test_bit((mem_addr_t)&config->tsc->ISR, TSC_ISR_EOAF_Pos)) {
|
||||
/* clear end of acquisition flag */
|
||||
sys_set_bit((mem_addr_t)&config->tsc->ICR, TSC_ICR_EOAIC_Pos);
|
||||
|
||||
/* read values */
|
||||
for (uint8_t i = 0; i < config->group_cnt; i++) {
|
||||
const struct stm32_tsc_group_config *group = &config->group_config[i];
|
||||
uint32_t group_bit = BIT(group->group - 1) << 16;
|
||||
|
||||
if (config->tsc->IOGCSR & group_bit) {
|
||||
uint32_t count_value = sys_read32(
|
||||
(mem_addr_t)&config->tsc->IOGXCR[group->group - 1]);
|
||||
|
||||
uint8_t group_idx = 0;
|
||||
|
||||
int ret = get_group_index(dev, group->group, &group_idx);
|
||||
|
||||
if (ret < 0) {
|
||||
LOG_ERR("%s: group %d not found", dev->name, group->group);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct stm32_tsc_group_data *data = &config->group_data[group_idx];
|
||||
|
||||
if (data->cb) {
|
||||
data->cb(count_value, data->user_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stm32_tsc_isr(const struct device *dev)
|
||||
{
|
||||
const struct stm32_tsc_config *config = dev->config;
|
||||
|
||||
/* disable interrupts */
|
||||
sys_clear_bits((mem_addr_t)&config->tsc->IER, TSC_IER_EOAIE | TSC_IER_MCEIE);
|
||||
|
||||
stm32_tsc_handle_incoming_data(dev);
|
||||
}
|
||||
|
||||
static int stm32_tsc_init(const struct device *dev)
|
||||
{
|
||||
const struct stm32_tsc_config *config = dev->config;
|
||||
const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
|
||||
|
||||
int ret;
|
||||
|
||||
if (!device_is_ready(clk)) {
|
||||
LOG_ERR("%s: clock controller device not ready", dev->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* reset TSC values to default */
|
||||
ret = reset_line_toggle_dt(&config->reset);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to reset %s (%d)", dev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clock_control_on(clk, (clock_control_subsys_t)&config->pclken[0]);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to enable clock for %s (%d)", dev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to configure %s pins (%d)", dev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* set ctph (bits 31:28) and ctpl (bits 27:24) */
|
||||
sys_set_bits((mem_addr_t)&config->tsc->CR, (((config->ctph - 1) << 4) | (config->ctpl - 1))
|
||||
<< TSC_CR_CTPL_Pos);
|
||||
|
||||
/* set spread spectrum deviation (bits 23:17) */
|
||||
sys_set_bits((mem_addr_t)&config->tsc->CR, config->ssd << TSC_CR_SSD_Pos);
|
||||
|
||||
/* set pulse generator prescaler (bits 14:12) */
|
||||
sys_set_bits((mem_addr_t)&config->tsc->CR, config->pgpsc << TSC_CR_PGPSC_Pos);
|
||||
|
||||
/* set max count value (bits 7:5) */
|
||||
sys_set_bits((mem_addr_t)&config->tsc->CR, config->max_count << TSC_CR_MCV_Pos);
|
||||
|
||||
/* set spread spectrum prescaler (bit 15) */
|
||||
if (config->sscpsc == 2) {
|
||||
sys_set_bit((mem_addr_t)&config->tsc->CR, TSC_CR_SSPSC_Pos);
|
||||
}
|
||||
|
||||
/* set sync bit polarity */
|
||||
if (config->sync_pol) {
|
||||
sys_set_bit((mem_addr_t)&config->tsc->CR, TSC_CR_SYNCPOL_Pos);
|
||||
}
|
||||
|
||||
/* set sync acquisition */
|
||||
if (config->sync_acq) {
|
||||
sys_set_bit((mem_addr_t)&config->tsc->CR, TSC_CR_AM_Pos);
|
||||
}
|
||||
|
||||
/* set I/O default mode */
|
||||
if (config->iodef) {
|
||||
sys_set_bit((mem_addr_t)&config->tsc->CR, TSC_CR_IODEF_Pos);
|
||||
}
|
||||
|
||||
/* set spread spectrum */
|
||||
if (config->spread_spectrum) {
|
||||
sys_set_bit((mem_addr_t)&config->tsc->CR, TSC_CR_SSE_Pos);
|
||||
}
|
||||
|
||||
/* group configuration */
|
||||
for (int i = 0; i < config->group_cnt; i++) {
|
||||
const struct stm32_tsc_group_config *group = &config->group_config[i];
|
||||
|
||||
if (group->channel_ios & group->sampling_io) {
|
||||
LOG_ERR("%s: group %d has the same channel and sampling I/O", dev->name,
|
||||
group->group);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* if use_as_shield is true, the channel I/Os are used as shield, and can only have
|
||||
* values 1,2,4,8
|
||||
*/
|
||||
if (group->use_as_shield && group->channel_ios != 1 && group->channel_ios != 2 &&
|
||||
group->channel_ios != 4 && group->channel_ios != 8) {
|
||||
LOG_ERR("%s: group %d is used as shield, but has invalid channel I/Os. "
|
||||
"Can only have one",
|
||||
dev->name, group->group);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* clear schmitt trigger hysteresis for enabled I/Os */
|
||||
sys_clear_bits(
|
||||
(mem_addr_t)&config->tsc->IOHCR,
|
||||
GET_GROUP_BITS(group->channel_ios | group->sampling_io, group->group));
|
||||
|
||||
/* set channel I/Os */
|
||||
sys_set_bits((mem_addr_t)&config->tsc->IOCCR,
|
||||
GET_GROUP_BITS(group->channel_ios, group->group));
|
||||
|
||||
/* set sampling I/O */
|
||||
sys_set_bits((mem_addr_t)&config->tsc->IOSCR,
|
||||
GET_GROUP_BITS(group->sampling_io, group->group));
|
||||
|
||||
/* enable group */
|
||||
if (!group->use_as_shield) {
|
||||
sys_set_bit((mem_addr_t)&config->tsc->IOGCSR, group->group - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* disable interrupts */
|
||||
sys_clear_bits((mem_addr_t)&config->tsc->IER, TSC_IER_EOAIE | TSC_IER_MCEIE);
|
||||
|
||||
/* clear interrupts */
|
||||
sys_set_bits((mem_addr_t)&config->tsc->ICR, TSC_ICR_EOAIC | TSC_ICR_MCEIC);
|
||||
|
||||
/* enable peripheral */
|
||||
sys_set_bit((mem_addr_t)&config->tsc->CR, TSC_CR_TSCE_Pos);
|
||||
|
||||
config->irq_func();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define STM32_TSC_GROUP_DEFINE(node_id) \
|
||||
{ \
|
||||
.group = DT_PROP(node_id, group), \
|
||||
.channel_ios = DT_PROP(node_id, channel_ios), \
|
||||
.sampling_io = DT_PROP(node_id, sampling_io), \
|
||||
.use_as_shield = DT_PROP(node_id, st_use_as_shield), \
|
||||
}
|
||||
|
||||
#define STM32_TSC_INIT(index) \
|
||||
static const struct stm32_pclken pclken_##index[] = STM32_DT_INST_CLOCKS(index); \
|
||||
\
|
||||
PINCTRL_DT_INST_DEFINE(index); \
|
||||
\
|
||||
static void stm32_tsc_irq_init_##index(void) \
|
||||
{ \
|
||||
IRQ_CONNECT(DT_INST_IRQN(index), DT_INST_IRQ(index, priority), stm32_tsc_isr, \
|
||||
DEVICE_DT_INST_GET(index), 0); \
|
||||
irq_enable(DT_INST_IRQN(index)); \
|
||||
}; \
|
||||
\
|
||||
static const struct stm32_tsc_group_config group_config_cfg_##index[] = { \
|
||||
DT_INST_FOREACH_CHILD_STATUS_OKAY_SEP(index, STM32_TSC_GROUP_DEFINE, (,))}; \
|
||||
\
|
||||
static struct stm32_tsc_group_data \
|
||||
group_data_cfg_##index[DT_INST_CHILD_NUM_STATUS_OKAY(index)]; \
|
||||
\
|
||||
static const struct stm32_tsc_config stm32_tsc_cfg_##index = { \
|
||||
.tsc = (TSC_TypeDef *)DT_INST_REG_ADDR(index), \
|
||||
.pclken = pclken_##index, \
|
||||
.reset = RESET_DT_SPEC_INST_GET(index), \
|
||||
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(index), \
|
||||
.group_config = group_config_cfg_##index, \
|
||||
.group_data = group_data_cfg_##index, \
|
||||
.group_cnt = DT_INST_CHILD_NUM_STATUS_OKAY(index), \
|
||||
.pgpsc = LOG2CEIL(DT_INST_PROP(index, st_pulse_generator_prescaler)), \
|
||||
.ctph = DT_INST_PROP(index, st_charge_transfer_pulse_high), \
|
||||
.ctpl = DT_INST_PROP(index, st_charge_transfer_pulse_low), \
|
||||
.spread_spectrum = DT_INST_PROP(index, st_spread_spectrum), \
|
||||
.sscpsc = DT_INST_PROP(index, st_spread_spectrum_prescaler), \
|
||||
.ssd = DT_INST_PROP(index, st_spread_spectrum_deviation), \
|
||||
.max_count = LOG2CEIL(DT_INST_PROP(index, st_max_count_value) + 1) - 8, \
|
||||
.iodef = DT_INST_PROP(index, st_iodef_float), \
|
||||
.sync_acq = DT_INST_PROP(index, st_synced_acquisition), \
|
||||
.sync_pol = DT_INST_PROP(index, st_syncpol_rising), \
|
||||
.irq_func = stm32_tsc_irq_init_##index, \
|
||||
}; \
|
||||
DEVICE_DT_INST_DEFINE(index, stm32_tsc_init, NULL, NULL, &stm32_tsc_cfg_##index, \
|
||||
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, NULL);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(STM32_TSC_INIT)
|
||||
|
||||
struct input_tsc_keys_data {
|
||||
uint32_t buffer[CONFIG_INPUT_STM32_TSC_KEYS_BUFFER_WORD_SIZE];
|
||||
struct ring_buf rb;
|
||||
bool expect_release;
|
||||
struct k_timer sampling_timer;
|
||||
};
|
||||
|
||||
struct input_tsc_keys_config {
|
||||
const struct device *tsc_dev;
|
||||
uint32_t sampling_interval_ms;
|
||||
int32_t noise_threshold;
|
||||
int zephyr_code;
|
||||
uint8_t group;
|
||||
};
|
||||
|
||||
static void input_tsc_sampling_timer_callback(struct k_timer *timer)
|
||||
{
|
||||
const struct device *dev = k_timer_user_data_get(timer);
|
||||
|
||||
stm32_tsc_start(dev);
|
||||
}
|
||||
|
||||
static void input_tsc_callback_handler(uint32_t count_value, void *user_data)
|
||||
{
|
||||
const struct device *dev = (const struct device *)user_data;
|
||||
const struct input_tsc_keys_config *config =
|
||||
(const struct input_tsc_keys_config *)dev->config;
|
||||
struct input_tsc_keys_data *data = (struct input_tsc_keys_data *)dev->data;
|
||||
|
||||
if (ring_buf_item_space_get(&data->rb) == 0) {
|
||||
uint32_t oldest_point;
|
||||
int32_t slope;
|
||||
|
||||
(void)ring_buf_get(&data->rb, (uint8_t *)&oldest_point, sizeof(oldest_point));
|
||||
|
||||
slope = count_value - oldest_point;
|
||||
if (slope < -config->noise_threshold && !data->expect_release) {
|
||||
data->expect_release = true;
|
||||
input_report_key(dev, config->zephyr_code, 1, false, K_NO_WAIT);
|
||||
} else if (slope > config->noise_threshold && data->expect_release) {
|
||||
data->expect_release = false;
|
||||
input_report_key(dev, config->zephyr_code, 0, false, K_NO_WAIT);
|
||||
}
|
||||
}
|
||||
|
||||
(void)ring_buf_put(&data->rb, (uint8_t *)&count_value, sizeof(count_value));
|
||||
}
|
||||
|
||||
static int input_tsc_keys_init(const struct device *dev)
|
||||
{
|
||||
const struct input_tsc_keys_config *config = dev->config;
|
||||
struct input_tsc_keys_data *data = dev->data;
|
||||
|
||||
if (!device_is_ready(config->tsc_dev)) {
|
||||
LOG_ERR("%s: TSC device not ready", config->tsc_dev->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ring_buf_item_init(&data->rb, CONFIG_INPUT_STM32_TSC_KEYS_BUFFER_WORD_SIZE, data->buffer);
|
||||
|
||||
uint8_t group_index = 0;
|
||||
|
||||
int ret = get_group_index(config->tsc_dev, config->group, &group_index);
|
||||
|
||||
if (ret) {
|
||||
LOG_ERR("%s: group %d not found", config->tsc_dev->name, config->group);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = stm32_tsc_group_register_callback(config->tsc_dev, group_index,
|
||||
input_tsc_callback_handler, (void *)dev);
|
||||
|
||||
if (ret) {
|
||||
LOG_ERR("%s: failed to register callback for group %d", config->tsc_dev->name,
|
||||
config->group);
|
||||
return ret;
|
||||
}
|
||||
|
||||
k_timer_init(&data->sampling_timer, input_tsc_sampling_timer_callback, NULL);
|
||||
k_timer_user_data_set(&data->sampling_timer, (void *)config->tsc_dev);
|
||||
k_timer_start(&data->sampling_timer, K_NO_WAIT, K_MSEC(config->sampling_interval_ms));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define TSC_KEYS_INIT(inst) \
|
||||
\
|
||||
static struct input_tsc_keys_data tsc_keys_data_##inst; \
|
||||
\
|
||||
static const struct input_tsc_keys_config tsc_keys_config_##inst = { \
|
||||
.tsc_dev = DEVICE_DT_GET(DT_GPARENT(inst)), \
|
||||
.sampling_interval_ms = DT_PROP(inst, sampling_interval_ms), \
|
||||
.zephyr_code = DT_PROP(inst, zephyr_code), \
|
||||
.noise_threshold = DT_PROP(inst, noise_threshold), \
|
||||
.group = DT_PROP(DT_PARENT(inst), group), \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_DEFINE(inst, input_tsc_keys_init, NULL, &tsc_keys_data_##inst, \
|
||||
&tsc_keys_config_##inst, POST_KERNEL, CONFIG_INPUT_INIT_PRIORITY, NULL);
|
||||
|
||||
DT_FOREACH_STATUS_OKAY(tsc_keys, TSC_KEYS_INIT);
|
||||
71
dts/bindings/input/tsc-keys.yaml
Normal file
71
dts/bindings/input/tsc-keys.yaml
Normal file
@@ -0,0 +1,71 @@
|
||||
# Copyright (c) 2024 Arif Balik <arifbalik@outlook.com>
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: |
|
||||
Input driver for STM32 Tocuh Sensing Controller (TSC).
|
||||
|
||||
This node is a st,stm32-tsc grandchild node and applies filters and
|
||||
calculations to detect an input event on a group which is the child of
|
||||
st,stm32-tsc. For more information see drivers/misc/st,stm32-tsc.yaml
|
||||
|
||||
Example:
|
||||
|
||||
#include <dt-bindings/input/input-event-codes.h>
|
||||
|
||||
&tsc {
|
||||
compatible = "st,stm32-tsc";
|
||||
|
||||
tsc_group6: g6 {
|
||||
...
|
||||
ts1 {
|
||||
compatible = "tsc-keys";
|
||||
sampling-interval-ms = <10>;
|
||||
oversampling = <10>;
|
||||
noise-threshold = <50>;
|
||||
zephyr,code = <INPUT_KEY_0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
compatible: "tsc-keys"
|
||||
|
||||
include:
|
||||
- name: base.yaml
|
||||
property-allowlist:
|
||||
- compatible
|
||||
- zephyr,deferred-init
|
||||
- label
|
||||
- status
|
||||
|
||||
properties:
|
||||
sampling-interval-ms:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
Sampling interval in milliseconds.
|
||||
oversampling:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
Over sampling factor. The driver will take the average of the samples
|
||||
taken in the sampling interval and compare it with the previous sample.
|
||||
Larger values will reduce the noise but will increase the latency. The
|
||||
default value is 10 so the slope will be calculated every
|
||||
10 * sampling-interval-ms milliseconds.
|
||||
noise-threshold:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
This value will be used to reject the noise for both
|
||||
directions of the slope.
|
||||
sticky-key-timeout-ms:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
Time in milliseconds to wait before releasing a key. By default a release
|
||||
event will be generated after 10 seconds of the last press event if
|
||||
the key is still pressed.
|
||||
zephyr,code:
|
||||
type: int
|
||||
required: true
|
||||
description: Key code to emit.
|
||||
Reference in New Issue
Block a user