This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push: new 54b2381c421 drivers/input: Create Single Button Multi Actions 54b2381c421 is described below commit 54b2381c421cee8bb3b853fd1bc5ae1f0a0e862a Author: Alan Carvalho de Assis <acas...@gmail.com> AuthorDate: Fri Jul 11 10:37:07 2025 -0300 drivers/input: Create Single Button Multi Actions This commit creates a sbutton device that uses a single button to create a keyboard driver that returns TAB or ENTER depending how long the user keeps the button pressed. Signed-off-by: Alan C. Assis <acas...@gmail.com> --- .../components/drivers/character/input/index.rst | 1 + .../components/drivers/character/input/sbutton.rst | 41 +++ .../arm/stm32f4/boards/stm32f4discovery/index.rst | 21 ++ boards/arm/stm32/common/include/board_sbutton.h | 83 ++++++ boards/arm/stm32/common/src/CMakeLists.txt | 4 + boards/arm/stm32/common/src/Make.defs | 4 + boards/arm/stm32/common/src/stm32_sbutton.c | 182 ++++++++++++++ .../stm32f4discovery/configs/sbutton/defconfig | 56 +++++ boards/arm/stm32/stm32f4discovery/include/board.h | 3 + .../arm/stm32/stm32f4discovery/src/stm32_bringup.c | 14 ++ drivers/input/CMakeLists.txt | 4 + drivers/input/Kconfig | 41 +++ drivers/input/Make.defs | 4 + drivers/input/sbutton.c | 278 +++++++++++++++++++++ include/nuttx/input/sbutton.h | 113 +++++++++ 15 files changed, 849 insertions(+) diff --git a/Documentation/components/drivers/character/input/index.rst b/Documentation/components/drivers/character/input/index.rst index ce0d896c629..c1317ebd493 100644 --- a/Documentation/components/drivers/character/input/index.rst +++ b/Documentation/components/drivers/character/input/index.rst @@ -6,5 +6,6 @@ Input Devices :caption: Supported Drivers keypad.rst + sbutton.rst See ``include/nuttx/input/*.h`` for registration information. diff --git a/Documentation/components/drivers/character/input/sbutton.rst b/Documentation/components/drivers/character/input/sbutton.rst new file mode 100644 index 00000000000..3281db7fd4a --- /dev/null +++ b/Documentation/components/drivers/character/input/sbutton.rst @@ -0,0 +1,41 @@ +================================== +Single Button Multi Actions Driver +================================== + +**Single Button (aka SButton)** is an kind of keyboard that uses +only a single physical button (Switch) in the board. This kind of +button is used with simple interfaces like those used on 3D Printers +or other devices where all the user needs is to move to the next +option and confirm the selection. + +It could be done detecting if the button was pressed for a short +period of time (i.e. less than 500ms) or long pressed. If it is a +short press the driver will return **TAB** and if it is a long +press the driver will return **ENTER**. Using it is possible to +navigate on those kind of menu. + +**How does it work?**. The driver uses a simple config data (this +config data is equivalent to the platform data on Linux kernel) to +map the pin from MCU will be used to register and detect the +interrupt from this pin physically connected to the button. + +It uses a kind of "polymorphism" in C to allow the driver to get +access to the functions responsible to attach and enable the +interrupt and to get the status of the pin. +See ``include/nuttx/input/sbutton.h`` +and ``boards/arm/stm32/common/src/stm32_sbutton.c`` to understand +better how it works. But basically the board file (config data) +creates a struct when the first field (variable) is the config +struct used the but SButton driver (``drivers/input/sbutton.c``). + +Every time the user presses or releases the key button an interrupt +is generated. The ISR of this interrupt inside sbutton +(``sbutton_interrupt()``) calls a workqueue to process it (because +we cannot spend time inside the ISR processing data, it could +degradate the performance of the RTOS). All that workqueue +(``sbutton_worker()``) needs to do it measure the elapsed time +(ticks) from the moment the key was pressed until the moment it +was released to decide if it is a "KEY_1" (**TAB**) or a "KEY_2" +(**ENTER**). Then is call the ``keyboard_event()`` from the +keyboard upper to send this key stroke to the user application. + diff --git a/Documentation/platforms/arm/stm32f4/boards/stm32f4discovery/index.rst b/Documentation/platforms/arm/stm32f4/boards/stm32f4discovery/index.rst index 5f76b5cac5e..f75ef95816e 100644 --- a/Documentation/platforms/arm/stm32f4/boards/stm32f4discovery/index.rst +++ b/Documentation/platforms/arm/stm32f4/boards/stm32f4discovery/index.rst @@ -1970,6 +1970,27 @@ the second dongle you will connect to UART3 (PB10 and PB11). In the main NSH console (in USART2) type: "pts_test &". It will create a new console in UART3. Just press ENTER and start typing commands on it. +sbutton +------- + +This is a configuration to test the Single Button Dual Action feature. +To test it just compile and flash nuttx.bin in the board. Then run the +``kbd`` command inside ``nsh>`` and short press and long press User +Button (B1) on the board. + +You will see something like this:: + + NuttShell (NSH) NuttX-12.10.0 + nsh> kbd + kbd_main: nsamples: 0 + kbd_main: Opening /dev/kbd0 + Sample : + code : 65 + type : 0 + Sample : + code : 66 + type : 0 + sporadic -------- diff --git a/boards/arm/stm32/common/include/board_sbutton.h b/boards/arm/stm32/common/include/board_sbutton.h new file mode 100644 index 00000000000..0dd67c11398 --- /dev/null +++ b/boards/arm/stm32/common/include/board_sbutton.h @@ -0,0 +1,83 @@ +/**************************************************************************** + * boards/arm/stm32/common/include/board_sbutton.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __BOARDS_ARM_STM32_COMMON_INCLUDE_BOARD_SBUTTON_H +#define __BOARDS_ARM_STM32_COMMON_INCLUDE_BOARD_SBUTTON_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_sbutton_initialize + * + * Description: + * This function is called by application-specific, setup logic to + * configure the Single Button Dual Action. + * + * Input Parameters: + * devno - The device number, used to build the device path as /dev/distN + * + * Returned Value: + * Zero is returned on success. Otherwise, a negated errno value is + * returned to indicate the nature of the failure. + * + ****************************************************************************/ + +int board_sbutton_initialize(int devno); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __BOARDS_ARM_STM32_COMMON_INCLUDE_BOARD_SBUTTON_H */ diff --git a/boards/arm/stm32/common/src/CMakeLists.txt b/boards/arm/stm32/common/src/CMakeLists.txt index 733194f4eb1..d13a75b1a4d 100644 --- a/boards/arm/stm32/common/src/CMakeLists.txt +++ b/boards/arm/stm32/common/src/CMakeLists.txt @@ -154,4 +154,8 @@ if(CONFIG_STEPPER_DRV8825) list(APPEND SRCS stm32_drv8825.c) endif() +if(CONFIG_INPUT_SBUTTON) + list(APPEND SRCS stm32_sbutton.c) +endif() + target_sources(board PRIVATE ${SRCS}) diff --git a/boards/arm/stm32/common/src/Make.defs b/boards/arm/stm32/common/src/Make.defs index e5ea54072a7..1048df7d17a 100644 --- a/boards/arm/stm32/common/src/Make.defs +++ b/boards/arm/stm32/common/src/Make.defs @@ -162,6 +162,10 @@ ifeq ($(CONFIG_STEPPER_DRV8825),y) CSRCS += stm32_drv8825.c endif +ifeq ($(CONFIG_INPUT_SBUTTON),y) + CSRCS += stm32_sbutton.c +endif + DEPPATH += --dep-path src VPATH += :src CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)arch$(DELIM)$(CONFIG_ARCH)$(DELIM)src$(DELIM)board$(DELIM)src diff --git a/boards/arm/stm32/common/src/stm32_sbutton.c b/boards/arm/stm32/common/src/stm32_sbutton.c new file mode 100644 index 00000000000..096a9e9fc5e --- /dev/null +++ b/boards/arm/stm32/common/src/stm32_sbutton.c @@ -0,0 +1,182 @@ +/**************************************************************************** + * boards/arm/stm32/common/src/stm32_sbutton.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <nuttx/arch.h> + +#include <stdio.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/board.h> +#include <arch/board/board.h> +#include <nuttx/input/sbutton.h> + +#include "stm32.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct stm32_sbtnconfig_s +{ + /* Configuration structure as seen by the HC-SR04 driver */ + + struct sbutton_config_s config; + + /* Additional private definitions only known to this driver */ + + void *arg; /* Argument to pass to the interrupt handler */ + xcpt_t isr; /* ISR Handler */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int sbtn_irq_attach(const struct sbutton_config_s *config, + xcpt_t isr, void *arg); +static void sbtn_irq_enable(const struct sbutton_config_s *config, + bool enable); +static void sbtn_irq_clear(const struct sbutton_config_s *config); + +static bool sbtn_pin_status(void); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* A reference to a structure of this type must be passed to the SButton + * driver. This structure provides information about the configuration + * of the SButton and provides some board-specific hooks. + * + * Memory for this structure is provided by the caller. It is not copied + * by the driver and is presumed to persist while the driver is active. + */ + +static struct stm32_sbtnconfig_s g_sbtnconfig = +{ + .config = + { + .attach = sbtn_irq_attach, + .enable = sbtn_irq_enable, + .clear = sbtn_irq_clear, + .status = sbtn_pin_status, + }, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Attach the SBUTTON interrupt handler to the GPIO interrupt */ + +static int sbtn_irq_attach(const struct sbutton_config_s *state, xcpt_t isr, + void *arg) +{ + struct stm32_sbtnconfig_s *priv = (struct stm32_sbtnconfig_s *)state; + irqstate_t flags; + + sinfo("sbtn_irq_attach\n"); + + flags = enter_critical_section(); + + priv->isr = isr; + priv->arg = arg; + + stm32_gpiosetevent(BOARD_SBUTTON_GPIO_INT, true, true, + true, isr, arg); + + leave_critical_section(flags); + + return OK; +} + +/* Enable or disable the GPIO interrupt */ + +static void sbtn_irq_enable(const struct sbutton_config_s *state, + bool enable) +{ + struct stm32_sbtnconfig_s *priv = (struct stm32_sbtnconfig_s *)state; + + iinfo("%d\n", enable); + + stm32_gpiosetevent(BOARD_SBUTTON_GPIO_INT, true, true, + true, enable ? priv->isr : NULL, priv->arg); +} + +/* Acknowledge/clear any pending GPIO interrupt */ + +static void sbtn_irq_clear(const struct sbutton_config_s *state) +{ + /* FIXME: Nothing to do ? */ +} + +/* Read and return the status of button (PRESSED = true; RELEASED = false */ + +static bool sbtn_pin_status(void) +{ + /* STM32F4Discovery button B1 is high level when press + * then just returned the status of the pin directly. + */ + + return stm32_gpioread(BOARD_SBUTTON_GPIO_INT); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: board_sbutton_initialize + * + * Description: + * This function is called by application-specific, setup logic to + * configure the Single Button Dual Action. + * + * Input Parameters: + * devno - The device number, used to build the device path as /dev/kbdN + * + * Returned Value: + * Zero is returned on success. Otherwise, a negated errno value is + * returned to indicate the nature of the failure. + * + ****************************************************************************/ + +int board_sbutton_initialize(int devno) +{ + /* Configure the Single Button Dual Action interrupt pin */ + + stm32_configgpio(BOARD_SBUTTON_GPIO_INT); + + /* Register the Single Button with overlay config pointer */ + + return sbutton_register(&g_sbtnconfig.config, devno); +} diff --git a/boards/arm/stm32/stm32f4discovery/configs/sbutton/defconfig b/boards/arm/stm32/stm32f4discovery/configs/sbutton/defconfig new file mode 100644 index 00000000000..3f7bd12d6f7 --- /dev/null +++ b/boards/arm/stm32/stm32f4discovery/configs/sbutton/defconfig @@ -0,0 +1,56 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_ARCH_FPU is not set +# CONFIG_ASSERTIONS_FILENAME is not set +# CONFIG_NDEBUG is not set +# CONFIG_NSH_ARGCAT is not set +# CONFIG_NSH_CMDOPT_HEXDUMP is not set +CONFIG_ARCH="arm" +CONFIG_ARCH_BOARD="stm32f4discovery" +CONFIG_ARCH_BOARD_COMMON=y +CONFIG_ARCH_BOARD_STM32F4_DISCOVERY=y +CONFIG_ARCH_CHIP="stm32" +CONFIG_ARCH_CHIP_STM32=y +CONFIG_ARCH_CHIP_STM32F407VG=y +CONFIG_ARCH_STACKDUMP=y +CONFIG_BOARD_LATE_INITIALIZE=y +CONFIG_BOARD_LOOPSPERMSEC=16717 +CONFIG_BUILTIN=y +CONFIG_EXAMPLES_HELLO=y +CONFIG_EXAMPLES_KEYBOARD=y +CONFIG_FS_PROCFS=y +CONFIG_HAVE_CXX=y +CONFIG_HAVE_CXXINITIALIZE=y +CONFIG_INIT_ENTRYPOINT="nsh_main" +CONFIG_INPUT=y +CONFIG_INPUT_SBUTTON=y +CONFIG_INTELHEX_BINARY=y +CONFIG_LINE_MAX=64 +CONFIG_MM_REGIONS=2 +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_FILEIOSIZE=512 +CONFIG_NSH_READLINE=y +CONFIG_PREALLOC_TIMERS=4 +CONFIG_RAM_SIZE=114688 +CONFIG_RAM_START=0x20000000 +CONFIG_RAW_BINARY=y +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_HPWORK=y +CONFIG_SCHED_WAITPID=y +CONFIG_START_DAY=6 +CONFIG_START_MONTH=12 +CONFIG_START_YEAR=2011 +CONFIG_STM32_JTAG_SW_ENABLE=y +CONFIG_STM32_PWR=y +CONFIG_STM32_SPI1=y +CONFIG_STM32_USART2=y +CONFIG_SYSTEM_NSH=y +CONFIG_USART2_RXBUFSIZE=128 +CONFIG_USART2_SERIAL_CONSOLE=y +CONFIG_USART2_TXBUFSIZE=128 +CONFIG_WQUEUE_NOTIFIER=y diff --git a/boards/arm/stm32/stm32f4discovery/include/board.h b/boards/arm/stm32/stm32f4discovery/include/board.h index 287d8e63933..4dce12316ab 100644 --- a/boards/arm/stm32/stm32f4discovery/include/board.h +++ b/boards/arm/stm32/stm32f4discovery/include/board.h @@ -465,6 +465,9 @@ #define BOARD_XEN1210_GPIO_INT GPIO_XEN1210_INT +#define BOARD_SBUTTON_GPIO_INT (GPIO_INPUT|GPIO_FLOAT|GPIO_EXTI|\ + GPIO_OPENDRAIN|GPIO_PORTA|GPIO_PIN0) + /* Define what timer to use as XEN1210 CLK (will use channel 1) */ #define BOARD_XEN1210_PWMTIMER 1 diff --git a/boards/arm/stm32/stm32f4discovery/src/stm32_bringup.c b/boards/arm/stm32/stm32f4discovery/src/stm32_bringup.c index ef55e3c4552..d34e6b38281 100644 --- a/boards/arm/stm32/stm32f4discovery/src/stm32_bringup.c +++ b/boards/arm/stm32/stm32f4discovery/src/stm32_bringup.c @@ -98,6 +98,10 @@ #include "stm32_nunchuck.h" #endif +#ifdef CONFIG_INPUT_SBUTTON +#include "board_sbutton.h" +#endif + #ifdef CONFIG_SENSORS_ZEROCROSS #include "stm32_zerocross.h" #endif @@ -452,6 +456,16 @@ int stm32_bringup(void) } #endif +#ifdef CONFIG_INPUT_SBUTTON + /* Register the Single Button Dual Action driver */ + + ret = board_sbutton_initialize(0); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: board_sbtn_initialize() failed: %d\n", ret); + } +#endif + #ifdef CONFIG_SENSORS_APDS9960 /* Register the APDS-9960 gesture sensor */ diff --git a/drivers/input/CMakeLists.txt b/drivers/input/CMakeLists.txt index a1c4eea640e..3b897b1a733 100644 --- a/drivers/input/CMakeLists.txt +++ b/drivers/input/CMakeLists.txt @@ -106,6 +106,10 @@ if(CONFIG_INPUT) list(APPEND SRCS keyboard_upper.c) endif() + if(CONFIG_INPUT_SBUTTON) + list(APPEND SRCS sbutton.c) + endif() + if(CONFIG_INPUT_DJOYSTICK) list(APPEND SRCS djoystick.c) endif() diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 47b861b7398..a5cff99c51c 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -622,6 +622,47 @@ config INPUT_BUTTONS_NPOLLWAITERS endif # INPUT_BUTTONS +config INPUT_SBUTTON + bool "Single Button Multi Actions" + select INPUT_KEYBOARD + default n + ---help--- + Enable the Single Button Multi Actions upper half driver. + This driver allows using a single button to generate two + events (mapped to short press and long press). Using it + is possible navigate a menu interface when using with a + LCD/OLED display. + +if INPUT_SBUTTON + +config INPUT_SBUTTON_KEY1 + int "ASCII code to generate for short press" + default 9 + ---help--- + Which key will be generated when user do a short press. + Default key is TAB (ASCII 9) + +config INPUT_SBUTTON_KEY2 + int "ASCII code to generate for long press" + default 13 + ---help--- + Which key will be generated when user do a short press. + Default key is ENTER (ASCII 13) + +config INPUT_SBUTTON_BUFSIZE + int "Buffer size to store keys" + default 8 + +config INPUT_SBUTTON_KEY_THRESH_MS + int "Threshold in milliseconds for short press" + default 500 + +config INPUT_SBUTTON_KEY_DEBOUNCE_MS + int "Key Debounce value in milliseconds" + default 30 + +endif # INPUT_SBUTTON + config INPUT_DJOYSTICK bool "Discrete Joystick" default n diff --git a/drivers/input/Make.defs b/drivers/input/Make.defs index 7cfc0c28d7c..815a4ce847d 100644 --- a/drivers/input/Make.defs +++ b/drivers/input/Make.defs @@ -118,6 +118,10 @@ ifeq ($(CONFIG_INPUT_NUNCHUCK),y) CSRCS += nunchuck.c endif +ifeq ($(CONFIG_INPUT_SBUTTON),y) + CSRCS += sbutton.c +endif + ifeq ($(CONFIG_INPUT_SPQ10KBD),y) CSRCS += spq10kbd.c endif diff --git a/drivers/input/sbutton.c b/drivers/input/sbutton.c new file mode 100644 index 00000000000..0c2e21e24d1 --- /dev/null +++ b/drivers/input/sbutton.c @@ -0,0 +1,278 @@ +/**************************************************************************** + * drivers/input/sbutton.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdbool.h> +#include <stdio.h> +#include <assert.h> +#include <debug.h> +#include <errno.h> +#include <poll.h> +#include <fcntl.h> + +#include <nuttx/input/sbutton.h> +#include <nuttx/fs/fs.h> +#include <nuttx/clock.h> +#include <nuttx/ascii.h> +#include <nuttx/kmalloc.h> +#include <nuttx/mutex.h> +#include <nuttx/wqueue.h> +#include <nuttx/semaphore.h> +#include <nuttx/input/keyboard.h> +#include <nuttx/input/kbd_codec.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* This format is used to construct the /dev/kbd[n] device driver path. It + * defined here so that it will be used consistently in all places. + */ + +#define DEV_FORMAT "/dev/kbd%d" +#define DEV_NAMELEN 12 + +#define KEY_PRESS true +#define KEY_RELEASE false + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct sbutton_dev_s +{ + FAR const struct sbutton_config_s *config; /* Board configuration data */ + + mutex_t lock; /* Exclusive access to dev */ + clock_t start; /* Clock tick when the key was pressed */ + clock_t end; /* Clock tick when the key was released */ + bool pressed; /* Keep previous status of button */ + struct work_s work; /* Supports the interrupt handling "bottom half" */ + + /* Keyboard lowerhalf of the registered keyboard */ + + struct keyboard_lowerhalf_s lower; +}; + +/**************************************************************************** + * Static Function Prototypes + ****************************************************************************/ + +static int sbutton_interrupt(int irq, FAR void *context, FAR void *arg); +static void sbutton_worker(FAR void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sbutton_worker + ****************************************************************************/ + +static void sbutton_worker(FAR void *arg) +{ + FAR struct sbutton_dev_s *priv = (FAR struct sbutton_dev_s *)arg; + uint8_t state; + int ret; + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return; + } + + /* Read the status of the button */ + + state = priv->config->status(); + + /* If the user just pressed the button, start measuring */ + + if (state == KEY_PRESS && !priv->pressed) + { + iinfo("Button pressed\n"); + + priv->pressed = true; + priv->start = clock_systime_ticks(); + } + else + { + if (state == KEY_RELEASE && priv->pressed) + { + uint32_t elapsed; + + iinfo("Button released\n"); + + priv->pressed = false; + + priv->end = clock_systime_ticks(); + elapsed = priv->end - priv->start; + + /* Debounce to avoid getting wrong press/release event */ + + if (elapsed < MSEC2TICK(CONFIG_INPUT_SBUTTON_KEY_DEBOUNCE_MS)) + { + iwarn("Button event too short, ignoring it\n"); + } + else + { + if (elapsed < MSEC2TICK(CONFIG_INPUT_SBUTTON_KEY_THRESH_MS)) + { + iinfo("KEY_1\n"); + keyboard_event(&priv->lower, CONFIG_INPUT_SBUTTON_KEY1, + KEYBOARD_PRESS); + } + else + { + iinfo("KEY_2\n"); + keyboard_event(&priv->lower, CONFIG_INPUT_SBUTTON_KEY2, + KEYBOARD_PRESS); + } + } + } + } + + /* Unlock and return */ + + nxmutex_unlock(&priv->lock); +} + +/**************************************************************************** + * Name: sbutton_interrupt + ****************************************************************************/ + +static int sbutton_interrupt(int irq, FAR void *context, FAR void *arg) +{ + FAR struct sbutton_dev_s *priv = (FAR struct sbutton_dev_s *)arg; + int ret; + + /* Let the event worker know that it has an interrupt event to handle + * It is possible that we will already have work scheduled from a + * previous interrupt event. That is OK we will service all the events + * in the same work job. + */ + + if (work_available(&priv->work)) + { + ret = work_queue(HPWORK, &priv->work, sbutton_worker, priv, 0); + if (ret != 0) + { + ierr("ERROR: Failed to queue work: %d\n", ret); + } + } + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sbutton_register + * + * Description: + * Configure the Single Button Multi Key Keyboard to use the provided + * instance. This will register the driver as /dev/kbdN where N is the + * minor device number. + * + * Input Parameters: + * config - Persistent board configuration data + * kbdminor - The keyboard input device minor number + * + * Returned Value: + * Zero is returned on success. Otherwise, a negated errno value is + * returned to indicate the nature of the failure. + * + ****************************************************************************/ + +int sbutton_register(FAR const struct sbutton_config_s *config, + char kbdminor) +{ + FAR struct sbutton_dev_s *priv; + char kbddevname[DEV_NAMELEN]; + int ret; + + /* Debug Sanity Checks */ + + DEBUGASSERT(config != NULL); + DEBUGASSERT(config->attach != NULL); + DEBUGASSERT(config->enable != NULL); + DEBUGASSERT(config->clear != NULL); + + priv = kmm_zalloc(sizeof(struct sbutton_dev_s)); + if (!priv) + { + ierr("ERROR: kmm_zalloc(%d) failed\n", sizeof(struct sbutton_dev_s)); + return -ENOMEM; + } + + /* Initialize the device driver instance */ + + priv->config = config; /* Save the board configuration */ + priv->pressed = false; + + nxmutex_init(&priv->lock); /* Initialize device mutex */ + + config->clear(config); + config->enable(config, false); + + /* Attach the interrupt handler */ + + ret = config->attach(config, sbutton_interrupt, priv); + if (ret < 0) + { + ierr("ERROR: Failed to attach interrupt\n"); + goto errout_with_priv; + } + + /* Start servicing events */ + + priv->config->enable(priv->config, true); + + snprintf(kbddevname, sizeof(kbddevname), DEV_FORMAT, kbdminor); + + /* Register the device as a keyboard device */ + + ret = keyboard_register(&priv->lower, kbddevname, + CONFIG_INPUT_SBUTTON_BUFSIZE); + if (ret < 0) + { + ierr("ERROR: keyboard_register() failed: %d\n", ret); + goto errout_with_priv; + } + + return OK; + +errout_with_priv: + nxmutex_destroy(&priv->lock); + kmm_free(priv); + return ret; +} diff --git a/include/nuttx/input/sbutton.h b/include/nuttx/input/sbutton.h new file mode 100644 index 00000000000..9e9cd6b3d4e --- /dev/null +++ b/include/nuttx/input/sbutton.h @@ -0,0 +1,113 @@ +/**************************************************************************** + * include/nuttx/input/sbutton.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* The single button driver exports a standard character driver interface. By + * convention, the driver is exposed as /dev/kbd[n] and works like a common + * keyboard device, but generates only INPUT_TAB and INPUT_ENTER events. + */ + +#ifndef __INCLUDE_NUTTX_INPUT_SBUTTON_H +#define __INCLUDE_NUTTX_INPUT_SBUTTON_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <nuttx/i2c/i2c_master.h> +#include <stdbool.h> +#include <nuttx/irq.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* A reference to a structure of this type must be passed to the + * single button keyboard driver. This structure provides information + * about the configuration and provides some board-specific hooks. + */ + +struct sbutton_config_s +{ + /* IRQ/GPIO access callbacks. These operations all hidden behind + * callbacks to isolate the Q10 Keyboard driver from differences in GPIO + * interrupt handling by varying boards and MCUs. + * + * attach - Attach the Q10 kbd interrupt handler to the GPIO interrupt + * enable - Enable or disable the GPIO interrupt + * clear - Acknowledge/clear any pending GPIO interrupt + */ + + int (*attach)(FAR const struct sbutton_config_s *config, xcpt_t isr, + FAR void *arg); + void (*enable)(FAR const struct sbutton_config_s *config, bool enable); + void (*clear)(FAR const struct sbutton_config_s *config); + bool (*status)(void); +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: spq10kbd_register + * + * Description: + * Configure the single button driver to use the provided instance. + * This will register the driver as /dev/kbdN where N is the + * minor device number. + * + * Input Parameters: + * config - Persistent board configuration data + * kbdminor - The keyboard input device minor number + * + * Returned Value: + * Zero is returned on success. Otherwise, a negated errno value is + * returned to indicate the nature of the failure. + * + ****************************************************************************/ + +int sbutton_register(FAR const struct sbutton_config_s *config, + char kbdminor); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_NUTTX_INPUT_SBUTTON_H */