diff --git a/Documentation/components/drivers/character/input/images/mpr121.png b/Documentation/components/drivers/character/input/images/mpr121.png new file mode 100644 index 0000000000000..d23fb213f76c8 Binary files /dev/null and b/Documentation/components/drivers/character/input/images/mpr121.png differ diff --git a/Documentation/components/drivers/character/input/index.rst b/Documentation/components/drivers/character/input/index.rst index 3ef4746c003f8..cc15022a11833 100644 --- a/Documentation/components/drivers/character/input/index.rst +++ b/Documentation/components/drivers/character/input/index.rst @@ -7,6 +7,7 @@ Input Devices keypad-keyboard.rst keypad.rst + mpr121.rst sbutton.rst See ``include/nuttx/input/*.h`` for registration information. diff --git a/Documentation/components/drivers/character/input/mpr121.rst b/Documentation/components/drivers/character/input/mpr121.rst new file mode 100644 index 0000000000000..9a24b3158fbf4 --- /dev/null +++ b/Documentation/components/drivers/character/input/mpr121.rst @@ -0,0 +1,72 @@ +======================== +MPR121 Capacitive Keypad +======================== + +**What is the MPR121 Keypad** +The MPR Capacitive Keypad is a small keyboard with 12 keys, similar +to a telephone keypad, however the keys are numered from 0 to 11. + +This is the picture of the MPR121 Capacitive Keypad: + +.. image:: images/mpr121.png + :alt: MPR121 Capacitive Keypad + :align: center + :width: 200px + +**Purpose**. The MPR121 driver provides a generic keypad +implementation for this capacitive keypad. The MPR121 uses the I2C +bus to setup and read the touched/released key and uses an IRQ pin +(open drain) to indicate when a key is pressed/released or when some +unnexpected issue happens (currently not used). + +The driver also enables debounce, and emits keyboard events through +the common keyboard upper-half. This makes the device available as a +character driver (e.g., ``/dev/keypad0``) using the standard keyboard +interfaces. + +**Driver Overview**. The MPR121 lower-half scans the matrix and calls +``keyboard_event()`` when it detects a press or release. The keyboard +upper-half registers the character device at the requested ``devpath`` +and stores events in a circular buffer. Applications read +``struct keyboard_event_s`` from the device or use the optional +kbd-codec layer. + +**Board Support**. To support the MPR121, a board must provide: + +#. **IRQ Definition** + + - Define the GPIO Input pin with pull-up enabled (since the + IRQ pin is open-drain) to be used to detect the interrupts. + +#. **Registration Hook** + + - Define a ``struct xxx_mpr121config_s`` that will wrap the + ``struct mpr121_config_s config`` as first member and the irq + argument and ISR handler (xcpt_t isr). The ``config`` needs to + have its ``.irq_attach`` and ``keymap`` initialized with the + board IRQ ISR function and the key mapping array. + - Implement ``board_mpr121_initialize(int devno, int busno)`` to + call ``mpr121_register(&config, devpath)``. It needs to initialize + the I2C Master port/channel that will be used to communicate with + the MPR121 and an instance of that ``xxx_mpr121config_s`` structure. + - Invoke the board hook during bring-up (for example, + ``board_mpr121_initialize(0, 1)`` for ``/dev/keymap0`` and ``i2c1``). + +**Reference Implementation (STM32F4Discovery)**. The current reference +is in ``boards/arm/stm32/common/src/stm32_mpr121.c``: + +- Keymap: 4x3 keypad layout +- Registration: ``board_mpr121_initialize()`` calls + ``mpr121_register()`` + +**Data Path Summary**. + +- Board calls ``board_mpr121_initialize(0, 1)`` +- ``mpr121_register()`` initializes and configure the MPR121 chip + and calls ``keyboard_register(&lower, devpath, buflen)`` +- The upper-half registers the device node at ``devpath`` +- Every time an interruption is generated on IRQ pin it will call the + ``mpr121_worker()`` and it is calls ``keyboard_event()`` on + press/release of keys. +- Applications read events from the device node (``/dev/keypad0``). + diff --git a/Documentation/platforms/arm/stm32f4/boards/stm32f4discovery/index.rst b/Documentation/platforms/arm/stm32f4/boards/stm32f4discovery/index.rst index 699b2105c65ec..1c2197aa35dca 100644 --- a/Documentation/platforms/arm/stm32f4/boards/stm32f4discovery/index.rst +++ b/Documentation/platforms/arm/stm32f4/boards/stm32f4discovery/index.rst @@ -1475,6 +1475,54 @@ because code cannot be executed from CCM memory. STATUS: 2018-06-02: Configuration added by Alan Carvalho de Assis. +mpr121_keypad +------------- + +This board config enables the usage of an external MPR121 Capacitive +Keypad connected to STM32F4Discovery board this way: + +================ ============= +STM32F4Discovery MPR121 Keypad +================ ============= +GND GND +3V [1] 3V3 +I2C1 SDA (PB9) SDA +I2C1 SCL (PB6) SCL +PB0 IRQ +================ ============= + +1: You need to remove the diode D3 and short-circuit the PADs in the +board to get 3.3V. Be aware: although my board works fine, it could +damage something that expects 3V in our board (double check). + +After compiling and flashing the firmware in our board, run kbd command. + +.. code:: console + + NuttShell (NSH) NuttX-12.13.0 + nsh> ls /dev + /dev: + console + keypad0 + null + ttyS0 + zero + nsh> kbd + kbd_main: nsamples: 0 + kbd_main: Opening /dev/keypad0 + Sample : + code : 48 + type : 0 + Sample : + code : 48 + type : 1 + Sample : + code : 49 + type : 0 + Sample : + code : 49 + type : 1 + netnsh ------ diff --git a/boards/arm/stm32/common/include/stm32_mpr121.h b/boards/arm/stm32/common/include/stm32_mpr121.h new file mode 100644 index 0000000000000..185f2aac544c2 --- /dev/null +++ b/boards/arm/stm32/common/include/stm32_mpr121.h @@ -0,0 +1,82 @@ +/**************************************************************************** + * boards/arm/stm32/common/include/stm32_mpr121.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_STM32_MPR121_H +#define __BOARDS_ARM_STM32_COMMON_INCLUDE_STM32_MPR121_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * 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: board_mpr121_initialize + * + * Description: + * Initialize and register the MPR121 capacitive keypad. + * + * Input Parameters: + * devno - The device number, used to build the device path as /dev/keypadN + * busno - The I2C bus number + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int board_mpr121_initialize(int devno, int busno); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __BOARDS_ARM_STM32_COMMON_INCLUDE_STM32_MPR121_H */ diff --git a/boards/arm/stm32/common/src/CMakeLists.txt b/boards/arm/stm32/common/src/CMakeLists.txt index acbd4200a7f8f..6883bfaea511a 100644 --- a/boards/arm/stm32/common/src/CMakeLists.txt +++ b/boards/arm/stm32/common/src/CMakeLists.txt @@ -166,4 +166,8 @@ if(CONFIG_INPUT_KMATRIX_I2C) list(APPEND SRCS stm32_kmatrix_i2c.c) endif() +if(CONFIG_INPUT_MPR121_KEYPAD) + list(APPEND SRCS stm32_mpr121.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 a86d230436e0e..bacc31b1dcf33 100644 --- a/boards/arm/stm32/common/src/Make.defs +++ b/boards/arm/stm32/common/src/Make.defs @@ -86,6 +86,10 @@ ifeq ($(CONFIG_SENSORS_APDS9960),y) CSRCS += stm32_apds9960.c endif +ifeq ($(CONFIG_INPUT_MPR121_KEYPAD),y) + CSRCS += stm32_mpr121.c +endif + ifeq ($(CONFIG_SENSORS_ZEROCROSS),y) CSRCS += stm32_zerocross.c endif diff --git a/boards/arm/stm32/common/src/stm32_mpr121.c b/boards/arm/stm32/common/src/stm32_mpr121.c new file mode 100644 index 0000000000000..bd0d0d5ad5e02 --- /dev/null +++ b/boards/arm/stm32/common/src/stm32_mpr121.c @@ -0,0 +1,182 @@ +/**************************************************************************** + * boards/arm/stm32/common/src/stm32_mpr121.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 + +#include +#include +#include + +#include +#include + +#include "stm32.h" +#include "stm32_i2c.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Device I2C Address of MPR121 Capacitive Keypad */ + +#define MPR121_I2C_ADDR 0x5a + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +struct stm32_mpr121config_s +{ + /* Configuration structure as seen by the MPR121 driver */ + + struct mpr121_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 mpr121_irq_attach(const struct mpr121_config_s *state, + xcpt_t isr, void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Keymap for 4x3 MPR121 Capacitive Keypad + * Keys named 11 and 10 were replaced with 'B' a 'A' + */ + +static const uint32_t g_mpr121_keymap[] = +{ + '0', '1', '2', '3', + '4', '5', '6', '7', + '8', '9', 'A', 'B', +}; + +/* A reference to a structure of this type must be passed to the MPR121 + * driver. This structure provides information about the configuration + * of the MPR121 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. The + * memory must be writable because, under certain circumstances, the driver + * may modify frequency or other values. + */ + +static struct stm32_mpr121config_s g_mpr121config = +{ + .config = + { + .irq_attach = mpr121_irq_attach, + .keymap = g_mpr121_keymap, + }, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Attach the MPR121 interrupt handler to the GPIO interrupt */ + +static int mpr121_irq_attach(const struct mpr121_config_s *state, + xcpt_t isr, void *arg) +{ + irqstate_t flags; + + iinfo("mpr121_irq_attach\n"); + + flags = enter_critical_section(); + + /* Setup interrupt for Falling Edge */ + + stm32_gpiosetevent(BOARD_MPR121_GPIO_INT, false, true, true, isr, arg); + + leave_critical_section(flags); + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: board_mpr121_initialize + * + * Description: + * Initialize and register the MPR121 gesture sensor. + * + * Input Parameters: + * devno - The device number, used to build the device path as /dev/gestN + * busno - The I2C bus number + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int board_mpr121_initialize(int devno, int busno) +{ + struct i2c_master_s *i2c; + char devpath[14]; + int ret; + + iinfo("Initializing MPR121!\n"); + + /* Configure the GPIO interrupt */ + + stm32_configgpio(BOARD_MPR121_GPIO_INT); + + /* Initialize I2C */ + + i2c = stm32_i2cbus_initialize(busno); + if (i2c == NULL) + { + return -ENODEV; + } + + /* Save this i2c in the config */ + + g_mpr121config.config.i2c_dev = i2c; + g_mpr121config.config.i2c_addr = MPR121_I2C_ADDR; + + /* Then register the capacitive keypad */ + + snprintf(devpath, sizeof(devpath), "/dev/keypad%d", devno); + ret = mpr121_register(&g_mpr121config.config, devpath); + if (ret < 0) + { + ierr("ERROR: Failed registering APDS-9960!\n"); + } + + return ret; +} diff --git a/boards/arm/stm32/stm32f4discovery/configs/mpr121_keypad/defconfig b/boards/arm/stm32/stm32f4discovery/configs/mpr121_keypad/defconfig new file mode 100644 index 0000000000000..8090b5152f057 --- /dev/null +++ b/boards/arm/stm32/stm32f4discovery/configs/mpr121_keypad/defconfig @@ -0,0 +1,58 @@ +# +# 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_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_BUTTONS=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_DEBUG_FEATURES=y +CONFIG_EXAMPLES_HELLO=y +CONFIG_EXAMPLES_KEYBOARD=y +CONFIG_EXAMPLES_KEYBOARD_DEVPATH="/dev/keypad0" +CONFIG_FS_PROCFS=y +CONFIG_HAVE_CXX=y +CONFIG_HAVE_CXXINITIALIZE=y +CONFIG_I2C=y +CONFIG_INIT_ENTRYPOINT="nsh_main" +CONFIG_INPUT=y +CONFIG_INPUT_MPR121_KEYPAD=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_LPWORK=y +CONFIG_SCHED_WAITPID=y +CONFIG_START_DAY=6 +CONFIG_START_MONTH=12 +CONFIG_START_YEAR=2011 +CONFIG_STM32_I2C1=y +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 diff --git a/boards/arm/stm32/stm32f4discovery/include/board.h b/boards/arm/stm32/stm32f4discovery/include/board.h index 7392eff96cbfd..4f850570856aa 100644 --- a/boards/arm/stm32/stm32f4discovery/include/board.h +++ b/boards/arm/stm32/stm32f4discovery/include/board.h @@ -448,6 +448,13 @@ #define BOARD_APDS9960_GPIO_INT GPIO_APDS9960_INT +/* IRQ Pin for MPR121 Capacitive Keypad */ + +#define GPIO_MPR121_INT \ + (GPIO_INPUT|GPIO_PULLUP|GPIO_EXTI|GPIO_PORTB|GPIO_PIN0) + +#define BOARD_MPR121_GPIO_INT GPIO_MPR121_INT + /* LIS3DSH */ #define GPIO_LIS3DSH_EXT0 \ diff --git a/boards/arm/stm32/stm32f4discovery/src/stm32_bringup.c b/boards/arm/stm32/stm32f4discovery/src/stm32_bringup.c index ecb11f7e6e6d1..a49ce53519d1e 100644 --- a/boards/arm/stm32/stm32f4discovery/src/stm32_bringup.c +++ b/boards/arm/stm32/stm32f4discovery/src/stm32_bringup.c @@ -60,6 +60,10 @@ #include "stm32_apds9960.h" #endif +#ifdef CONFIG_INPUT_MPR121_KEYPAD +#include "stm32_mpr121.h" +#endif + #ifdef CONFIG_CL_MFRC522 #include "stm32_mfrc522.h" #endif @@ -272,6 +276,16 @@ int stm32_bringup(void) } #endif +#ifdef CONFIG_INPUT_MPR121_KEYPAD + /* Initialize MPR121 using I2C1 bus to /dev/keypad0 */ + + ret = board_mpr121_initialize(0, 1); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: board_mpr121_initialize failed: %d\n", ret); + } +#endif + #ifdef CONFIG_LCD_ST7032 ret = stm32_st7032init("/dev/slcd0"); if (ret < 0) diff --git a/drivers/input/CMakeLists.txt b/drivers/input/CMakeLists.txt index 1b0883391889f..236da9ca944cd 100644 --- a/drivers/input/CMakeLists.txt +++ b/drivers/input/CMakeLists.txt @@ -138,5 +138,9 @@ if(CONFIG_INPUT) list(APPEND SRCS goldfish_events.c) endif() + if(CONFIG_INPUT_MPR121_KEYPAD) + list(APPEND SRCS mpr121.c) + endif() + target_sources(drivers PRIVATE ${SRCS}) endif() diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index d1695ee6f26da..bf7693373982d 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -815,4 +815,20 @@ endif # INPUT_KMATRIX_I2C endif # INPUT_KMATRIX +config INPUT_MPR121_KEYPAD + bool "MPR121 Touch Keypad Driver" + default n + select INPUT_KEYBOARD + ---help--- + Enable support for keypad matrix powered by MPR121. + +if INPUT_MPR121_KEYPAD + +config INPUT_MPR121_KEYPAD_BUFSIZE + int "Keyboard matrix buffer size" + default 64 + ---help--- + Size of the keyboard event buffer for each open file descriptor. + +endif # INPUT_MPR121_KEYPAD endif # INPUT diff --git a/drivers/input/Make.defs b/drivers/input/Make.defs index 66862bbbde3b9..1c3d7432b019e 100644 --- a/drivers/input/Make.defs +++ b/drivers/input/Make.defs @@ -138,6 +138,10 @@ ifeq ($(CONFIG_INPUT_GOLDFISH_EVENTS),y) CSRCS += goldfish_events.c endif +ifeq ($(CONFIG_INPUT_MPR121_KEYPAD),y) + CSRCS += mpr121.c +endif + # Include input device driver build support DEPPATH += --dep-path input diff --git a/drivers/input/mpr121.c b/drivers/input/mpr121.c new file mode 100644 index 0000000000000..512012ce38501 --- /dev/null +++ b/drivers/input/mpr121.c @@ -0,0 +1,572 @@ +/**************************************************************************** + * drivers/input/mpr121.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 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mpr121.h" + +/* This driver is for MPR121 Capacitive Keypad + * usually found at Aliexpress. This keypad has + * 12 keys numbered from 0 to 11, starting from + * 0 at bottom left and ending with 11 at top + * right: + * + * +---+---+---+ + * | 3 | 7 | 11| + * +---+---+---+ + * | 2 | 6 | 10| + * +---+---+---+ + * | 1 | 5 | 9 | + * +---+---+---+ + * | 0 | 4 | 8 | + * +---+---+---+ + * o o o o o + * | | | | | + * | | | | +- VCC + * | | | +--- IRQ + * | | +----- SCL + * | +------- SDA + * +--------- GND + * + */ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct mpr121_dev_s +{ + FAR const struct mpr121_config_s *config; /* Board configuration data */ + + mutex_t lock; /* Exclusive access to device */ + struct work_s work; /* Work queue to process keys after IRQ */ + + /* Current and previous state of the matrix (bitfield) */ + + FAR uint8_t *state; /* Current state bitmap */ + + /* Keyboard lower-half registration */ + + struct keyboard_lowerhalf_s lower; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Work queue */ + +static void mpr121_worker(FAR void *arg); + +/**************************************************************************** + * Name: mpr121_int_handler + * + * Description: + * Interrupt handler (ISR) for MPR121 IRQ pin. + * + ****************************************************************************/ + +static int mpr121_int_handler(int irq, FAR void *context, FAR void *arg) +{ + int ret; + + FAR struct mpr121_dev_s *priv = (FAR struct mpr121_dev_s *)arg; + + DEBUGASSERT(priv != NULL); + + /* Transfer processing to the worker thread. Since MPR121 interrupts + * are disabled while the work is pending, no special action should be + * required to protect the work queue. + */ + + DEBUGASSERT(priv->work.worker == NULL); + ret = work_queue(LPWORK, &priv->work, mpr121_worker, priv, 0); + if (ret != 0) + { + ierr("ERROR: Failed to queue work: %d\n", ret); + } + + return OK; +} + +/**************************************************************************** + * Name: mpr121_getreg8 + * + * Description: + * Read from an 8-bit mpr121 register + * + ****************************************************************************/ + +static uint8_t mpr121_getreg8(FAR struct mpr121_dev_s *priv, + uint8_t regaddr) +{ + struct i2c_msg_s msg[2]; + uint8_t regval; + int ret; + + /* Setup 8-bit mpr121 address write message */ + + msg[0].frequency = 400000; /* I2C frequency 400 KHz */ + msg[0].addr = priv->config->i2c_addr; /* 7-bit address */ + msg[0].flags = 0; /* Write transaction, START */ + msg[0].buffer = ®addr; /* Transfer from this addr */ + msg[0].length = 1; /* Send 1 byte following the addr + * (no STOP) */ + + /* Set up the 8-bit mpr121 data read message */ + + msg[1].frequency = 400000; /* I2C frequency 400 KHz */ + msg[1].addr = priv->config->i2c_addr; /* 7-bit address */ + msg[1].flags = I2C_M_READ; /* Read transac., Re-START */ + msg[1].buffer = ®val; /* Transfer to this address */ + msg[1].length = 1; /* Recv 1 byte following the addr + * (then STOP) */ + + /* Perform the transfer */ + + ret = I2C_TRANSFER(priv->config->i2c_dev, msg, 2); + if (ret < 0) + { + ierr("ERROR: I2C_TRANSFER failed: %d\n", ret); + return 0; + } + +#ifdef CONFIG_MPR121_KEYPAD_REGDEBUG + _err("%02x->%02x\n", regaddr, regval); +#endif + return regval; +} + +/**************************************************************************** + * Name: mpr121_putreg8 + * + * Description: + * Write a value to an 8-bit mpr121 register + * + ****************************************************************************/ + +static int mpr121_putreg8(FAR struct mpr121_dev_s *priv, + uint8_t regaddr, uint8_t regval) +{ + /* 8-bit data read sequence: + * + * Start-I2C_Write_Address-MPR121_Reg_Address-MPR121_Write_Data-STOP + */ + + struct i2c_msg_s msg; + uint8_t txbuffer[2]; + int ret; + +#ifdef CONFIG_MPR121_REGDEBUG + _err("%02x<-%02x\n", regaddr, regval); +#endif + + /* Setup to the data to be transferred. Two bytes: The mpr121 register + * address followed by one byte of data. + */ + + txbuffer[0] = regaddr; + txbuffer[1] = regval; + + /* Setup 8-bit mpr121 address write message */ + + msg.frequency = 400000; /* I2C frequency 400 KHz */ + msg.addr = priv->config->i2c_addr; /* 7-bit address */ + msg.flags = 0; /* Write transaction, beginning with START */ + msg.buffer = txbuffer; /* Transfer from this address */ + msg.length = 2; /* Send two byte following the address + * (then STOP) */ + + /* Perform the transfer */ + + ret = I2C_TRANSFER(priv->config->i2c_dev, &msg, 1); + if (ret < 0) + { + ierr("ERROR: I2C_TRANSFER failed: %d\n", ret); + return -EIO; + } + + return OK; +} + +/**************************************************************************** + * Name: mpr121_get_state + * + * Description: + * Get the current state of a key at position (key_idx) + * + ****************************************************************************/ + +static bool mpr121_get_state(FAR struct mpr121_dev_s *priv, + uint8_t key_idx) +{ + uint8_t byte_idx = key_idx / 8; + uint8_t bit_idx = key_idx % 8; + + return (priv->state[byte_idx] >> bit_idx) & 1; +} + +/**************************************************************************** + * Name: mpr121_set_state + * + * Description: + * Set the current state of a key at position (key_idx) + * + ****************************************************************************/ + +static void mpr121_set_state(FAR struct mpr121_dev_s *priv, + uint8_t key_idx, bool pressed) +{ + uint8_t byte_idx = key_idx / 8; + uint8_t bit_idx = key_idx % 8; + + if (pressed) + { + priv->state[byte_idx] |= (1 << bit_idx); + } + else + { + priv->state[byte_idx] &= ~(1 << bit_idx); + } +} + +/**************************************************************************** + * Name: mpr121_worker + * + * Description: + * The worker is executed every time the MPR121 issue an IRQ (goes low) + * + ****************************************************************************/ + +static void mpr121_worker(FAR void *arg) +{ + FAR struct mpr121_dev_s *priv = (FAR struct mpr121_dev_s *)arg; + uint32_t keycode; + uint16_t status; + uint8_t lo; + uint8_t hi; + int ret; + int i; + + DEBUGASSERT(priv != NULL); + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return; + } + + /* Read the status touched keys */ + + lo = mpr121_getreg8(priv, MPR121_ELE0_7_TS); + hi = mpr121_getreg8(priv, MPR121_ELE8_11_TS); + iinfo("Pressed: LO = 0x%02X | HI = 0x%02X", lo, hi); + + /* Only lower 4 bits of hi byte are valid (ELE8..ELE11) */ + + status = (uint16_t)lo | ((uint16_t)(hi & 0x0f) << 8); + + /* Sweep all the previous saved keys and generate event + * for each new key pressed or released + */ + + for (i = 0; i < MPR121_NUMKEY; i++) + { + if (mpr121_get_state(priv, i) != (bool)(status & 1 << i)) + { + /* Generate keyboard event */ + + keycode = priv->config->keymap[i]; + keyboard_event(&priv->lower, (uint16_t)keycode, + (status & 1 << i) ? KEYBOARD_PRESS : KEYBOARD_RELEASE); + + iinfo("Key [%d]: %s (code %lu)\n", i, + (status & 1 << i) ? "PRESS" : "RELEASE", + (unsigned long)keycode); + } + + mpr121_set_state(priv, i, (bool)(status & 1 << i)); + } + + nxmutex_unlock(&priv->lock); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mpr121_register + * + * Description: + * Configure and register a keyboard matrix device. + * + ****************************************************************************/ + +int mpr121_register(FAR const struct mpr121_config_s *config, + FAR const char *devpath) +{ + FAR struct mpr121_dev_s *priv; + int i; + int ret; + uint8_t state_size; + uint8_t regval; + + /* Validate configuration */ + + DEBUGASSERT(config != NULL); + DEBUGASSERT(devpath != NULL); + DEBUGASSERT(config->keymap != NULL); + + iinfo("Registering MPR121 Capacitive Keypad as %s\n", devpath); + + /* Allocate driver instance */ + + priv = kmm_zalloc(sizeof(struct mpr121_dev_s)); + if (!priv) + { + ierr("ERROR: kmm_zalloc(%zu) failed\n", sizeof(struct mpr121_dev_s)); + return -ENOMEM; + } + + /* Calculate bitmap sizes */ + + state_size = (MPR121_NUMKEY + 7) / 8; + + /* Allocate state bitmap */ + + priv->state = kmm_zalloc(state_size); + if (!priv->state) + { + ierr("ERROR: Failed to allocate state bitmap\n"); + kmm_free(priv); + return -ENOMEM; + } + + /* Initialize device structure */ + + priv->config = config; + + nxmutex_init(&priv->lock); + + /* Soft Reset the MPR121 */ + + ret = mpr121_putreg8(priv, MPR121_SRST, 0x63); + if (ret < 0) + { + ierr("Failed to reset MPR121\n"); + goto errout_with_priv; + } + + /* Force Stop Mode, although it should be in this mode after reset */ + + ret = mpr121_putreg8(priv, MPR121_ECR, 0x00); + if (ret < 0) + { + ierr("Failed to force MPR121 into Stop Mode\n"); + goto errout_with_priv; + } + + /* Probe the device, AFE Conf1 = 0x10 and Conf2 = 0x24 */ + + regval = mpr121_getreg8(priv, MPR121_AFE1); + if (regval != 0x10) + { + ierr("Error MPR121_AFE1 = 0x%02X instead of 0x10\n", regval); + goto errout_with_priv; + } + + regval = mpr121_getreg8(priv, MPR121_AFE2); + if (regval != 0x24) + { + ierr("Error MPR121_AFE2 = 0x%02X instead of 0x24\n", regval); + goto errout_with_priv; + } + + /* Setup the Baseline Tracking: MHD / NHD / NCL / FDL (see AN3944) */ + + /* Rising */ + + ret = mpr121_putreg8(priv, MPR121_MHDR, 0x01); + ret |= mpr121_putreg8(priv, MPR121_NHDR, 0x01); + ret |= mpr121_putreg8(priv, MPR121_NCLR, 0x0e); + ret |= mpr121_putreg8(priv, MPR121_FDLR, 0x00); + + /* Falling */ + + ret |= mpr121_putreg8(priv, MPR121_MHDF, 0x01); + ret |= mpr121_putreg8(priv, MPR121_NHDF, 0x05); + ret |= mpr121_putreg8(priv, MPR121_NCLF, 0x01); + ret |= mpr121_putreg8(priv, MPR121_FDLF, 0x00); + + /* Touched */ + + ret |= mpr121_putreg8(priv, MPR121_NHDT, 0x00); + ret |= mpr121_putreg8(priv, MPR121_NCLT, 0x00); + ret |= mpr121_putreg8(priv, MPR121_FDLT, 0x00); + + if (ret < 0) + { + ierr("Failed to setup the baseline tracking\n"); + goto errout_with_priv; + } + + /* Configure each electrode Touch / Release thresholds (all 12 channels) + * + * Touch threshold > Release threshold always, with hysteresis recommended + * at ~50 % of touch threshold. + */ + + for (i = 0; i < MPR121_NUMKEY; i++) + { + ret = mpr121_putreg8(priv, MPR121_REG_TOUCH_THRESH(i), + MPR121_TOUCH_THRESHOLD); + if (ret < 0) + { + ierr("Failed to set the touch threshold\n"); + goto errout_with_priv; + } + + ret = mpr121_putreg8(priv, MPR121_REG_REL_THRESH(i), + MPR121_RELEASE_THRESHOLD); + if (ret < 0) + { + ierr("Failed to set the release threshold\n"); + goto errout_with_priv; + } + } + + /* Configure Debounce = 2 consecutive samples required for touch / release + * + * Bits [2:0] = touch debounce + * bits [6:4] = release debounce + */ + + ret = mpr121_putreg8(priv, MPR121_DT_DR, 0x22); + if (ret < 0) + { + ierr("Failed to setup the debounce\n"); + goto errout_with_priv; + } + + /* Configure AFE (Analog Front End) & Filter configuration + * + * We are keeping the original values here: + * AFE1: FFI = 6 samples, CDC = 16 uA + * AFE2: CDT = 0.5 us, SFI = 4 samples, ESI = 1 ms + * Note: Increase ESI to reduce noise, but also reduces response time + */ + + ret = mpr121_putreg8(priv, MPR121_AFE1, MPR121_AFE1_VALUE); + ret |= mpr121_putreg8(priv, MPR121_AFE2, MPR121_AFE2_VALUE); + if (ret < 0) + { + ierr("Failed to configure the Analog Front End and Filter\n"); + goto errout_with_priv; + } + + /* Auto-Configuration (see AN3890), optional but recommended! + * + * When we know the Vdd we can automatically tunes CDC/CDT per electrode: + * + * AUTOCONF0: ACE=1 (enable), ARE=1 (retry), BVA=11 (baseline=5MSB) + * FFI same as AFE1 bits [7:6] => 0x0B + * AUTOCONF1: ACFIE=0, ARFIE=0, OORIE=0, SCTS=0 => 0x00 + */ + + ret = mpr121_putreg8(priv, MPR121_AUTOCONF0, 0x0b); + ret |= mpr121_putreg8(priv, MPR121_AUTOCONF1, 0x00); + ret |= mpr121_putreg8(priv, MPR121_UPLIMIT, MPR121_UPLIMIT_VALUE); + ret |= mpr121_putreg8(priv, MPR121_LOWLIMIT, MPR121_LOWLIMIT_VALUE); + ret |= mpr121_putreg8(priv, MPR121_TARGETLIMIT, MPR121_TARGETLIMIT_VALUE); + if (ret < 0) + { + ierr("Failed to setup the Auto Configuration limits\n"); + goto errout_with_priv; + } + + /* Enter Run Mode writing the ECR register + * + * ECR: + * CL [7:6] = 10 => baseline loaded from 5 upper bits data + * ELEPROX_EN [5:4] = 00 => proximity disabled + * ELE_EN [3:0] = 1100 => ELE0..ELE11 (12 electrodes active) + */ + + ret = mpr121_putreg8(priv, MPR121_ECR, MPR121_ECR_VALUE); + if (ret < 0) + { + ierr("Failed to enter in Run Mode\n"); + goto errout_with_priv; + } + + /* Register as keyboard device */ + + ret = keyboard_register(&priv->lower, devpath, + CONFIG_INPUT_MPR121_KEYPAD_BUFSIZE); + if (ret < 0) + { + ierr("ERROR: keyboard_register() failed: %d\n", ret); + goto errout_with_priv; + } + + iinfo("MPR121 Capacitive Keypad registered as %s\n", devpath); + + /* Attach to the interrupt */ + + priv->config->irq_attach(priv->config, mpr121_int_handler, priv); + + return OK; + +errout_with_priv: + nxmutex_destroy(&priv->lock); + kmm_free(priv->state); + kmm_free(priv); + return ret; +} diff --git a/drivers/input/mpr121.h b/drivers/input/mpr121.h new file mode 100644 index 0000000000000..9af7c6b3ce631 --- /dev/null +++ b/drivers/input/mpr121.h @@ -0,0 +1,213 @@ +/**************************************************************************** + * drivers/input/mpr121.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 __DRIVERS_INPUT_MPR121_H +#define __DRIVERS_INPUT_MPR121_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* MPR121 Address */ + +#define MPR121_ELE0_7_TS (0x00) /* Bits 0-7: E(n) Touch Status */ +#define MPR121_ELE8_11_TS (0x01) /* Bits 0-7: E(n)TS, OVCF */ +#define MPR121_ELE0_7_OOR (0x02) /* Bits 0-7: E(n)OOR status */ +#define MPR121_ELE8_11_OOR (0x03) /* Bits 0-7: E(n)OOR, ACFF, ARFF */ +#define MPR121_ELE0_FDL (0x04) /* Bits 0-7: ELE0 Filtered Data LSB */ +#define MPR121_ELE0_FDM (0x05) /* Bits 0-1: ELE0 Filtered Data MSB */ +#define MPR121_ELE1_FDL (0x06) /* Bits 0-7: ELE1 Filtered Data LSB */ +#define MPR121_ELE1_FDM (0x07) /* Bits 0-1: ELE1 Filtered Data MSB */ +#define MPR121_ELE2_FDL (0x08) /* Bits 0-7: ELE2 Filtered Data LSB */ +#define MPR121_ELE2_FDM (0x09) /* Bits 0-1: ELE2 Filtered Data MSB */ +#define MPR121_ELE3_FDL (0x0a) /* Bits 0-7: ELE3 Filtered Data LSB */ +#define MPR121_ELE3_FDM (0x0b) /* Bits 0-1: ELE3 Filtered Data MSB */ +#define MPR121_ELE4_FDL (0x0c) /* Bits 0-7: ELE4 Filtered Data LSB */ +#define MPR121_ELE4_FDM (0x0d) /* Bits 0-1: ELE4 Filtered Data MSB */ +#define MPR121_ELE5_FDL (0x0e) /* Bits 0-7: ELE5 Filtered Data LSB */ +#define MPR121_ELE5_FDM (0x0f) /* Bits 0-1: ELE5 Filtered Data MSB */ +#define MPR121_ELE6_FDL (0x10) /* Bits 0-7: ELE6 Filtered Data LSB */ +#define MPR121_ELE6_FDM (0x11) /* Bits 0-1: ELE6 Filtered Data MSB */ +#define MPR121_ELE7_FDL (0x12) /* Bits 0-7: ELE7 Filtered Data LSB */ +#define MPR121_ELE7_FDM (0x13) /* Bits 0-1: ELE7 Filtered Data MSB */ +#define MPR121_ELE8_FDL (0x14) /* Bits 0-7: ELE8 Filtered Data LSB */ +#define MPR121_ELE8_FDM (0x15) /* Bits 0-1: ELE8 Filtered Data MSB */ +#define MPR121_ELE9_FDL (0x16) /* Bits 0-7: ELE9 Filtered Data LSB */ +#define MPR121_ELE9_FDM (0x17) /* Bits 0-1: ELE9 Filtered Data MSB */ +#define MPR121_ELE10_FDL (0x18) /* Bits 0-7: ELE10 Filtered Data LSB */ +#define MPR121_ELE10_FDM (0x19) /* Bits 0-1: ELE10 Filtered Data MSB */ +#define MPR121_ELE11_FDL (0x1a) /* Bits 0-7: ELE11 Filtered Data LSB */ +#define MPR121_ELE11_FDM (0x1b) /* Bits 0-1: ELE11 Filtered Data MSB */ +#define MPR121_ELE12_FDL (0x1c) /* Bits 0-7: ELEPROX Filtered Data LSB */ +#define MPR121_ELE12_FDM (0x1d) /* Bits 0-1: ELEPROX Filtered Data MSB */ +#define MPR121_ELE0_BV (0x1e) /* Bits 0-7: ELE0 Baseline Value */ +#define MPR121_ELE1_BV (0x1f) /* Bits 0-7: ELE1 Baseline Value */ +#define MPR121_ELE2_BV (0x20) /* Bits 0-7: ELE2 Baseline Value */ +#define MPR121_ELE3_BV (0x21) /* Bits 0-7: ELE3 Baseline Value */ +#define MPR121_ELE4_BV (0x22) /* Bits 0-7: ELE4 Baseline Value */ +#define MPR121_ELE5_BV (0x23) /* Bits 0-7: ELE5 Baseline Value */ +#define MPR121_ELE6_BV (0x24) /* Bits 0-7: ELE6 Baseline Value */ +#define MPR121_ELE7_BV (0x25) /* Bits 0-7: ELE7 Baseline Value */ +#define MPR121_ELE8_BV (0x26) /* Bits 0-7: ELE8 Baseline Value */ +#define MPR121_ELE9_BV (0x27) /* Bits 0-7: ELE9 Baseline Value */ +#define MPR121_ELE10_BV (0x28) /* Bits 0-7: ELE10 Baseline Value */ +#define MPR121_ELE11_BV (0x29) /* Bits 0-7: ELE11 Baseline Value */ +#define MPR121_ELE12_BV (0x2a) /* Bits 0-7: ELEPROX Baseline Value */ +#define MPR121_MHDR (0x2b) /* Bits 0-5: MHD Rising */ +#define MPR121_NHDR (0x2c) /* Bits 0-5: NHD Rising */ +#define MPR121_NCLR (0x2d) /* Bits 0-7: NCL Rising */ +#define MPR121_FDLR (0x2e) /* Bits 0-7: FDL Rising */ +#define MPR121_MHDF (0x2f) /* Bits 0-5: MHD Falling */ +#define MPR121_NHDF (0x30) /* Bits 0-5: NHD Falling */ +#define MPR121_NCLF (0x31) /* Bits 0-7: NCL Falling */ +#define MPR121_FDLF (0x32) /* Bits 0-7: FDL Falling */ +#define MPR121_NHDT (0x33) /* Bits 0-5: NHD Touched */ +#define MPR121_NCLT (0x34) /* Bits 0-7: NCL Touched */ +#define MPR121_FDLT (0x35) /* Bits 0-7: FDL Touched */ +#define MPR121_MHDPROXR (0x36) /* Bits 0-5: ELEPROX MHD Rising */ +#define MPR121_NHDPROXR (0x37) /* Bits 0-5: ELEPROX NHD Rising */ +#define MPR121_NCLPROXR (0x38) /* Bits 0-7: ELEPROX NCL Rising */ +#define MPR121_FDLPROXR (0x39) /* Bits 0-7: ELEPROX FDL Rising */ +#define MPR121_MHDPROXF (0x3a) /* Bits 0-5: ELEPROX MHD Falling */ +#define MPR121_NHDPROXF (0x3b) /* Bits 0-5: ELEPROX NHD Falling */ +#define MPR121_NCLPROXF (0x3c) /* Bits 0-7: ELEPROX NCL Falling */ +#define MPR121_FDLPROXF (0x3d) /* Bits 0-7: ELEPROX FDL Falling */ +#define MPR121_NHDPROXT (0x3e) /* Bits 0-5: ELEPROX NHD Touched */ +#define MPR121_NCLPROXT (0x3f) /* Bits 0-7: ELEPROX NCL Touched */ +#define MPR121_FDLPROXT (0x40) /* Bits 0-7: ELEPROX FDL Touched */ +#define MPR121_E0TTH (0x41) /* Bits 0-7: ELE0 Touch Threshold */ +#define MPR121_E0RTH (0x42) /* Bits 0-7: ELE0 Release Threshold */ +#define MPR121_E1TTH (0x43) /* Bits 0-7: ELE1 Touch Threshold */ +#define MPR121_E1RTH (0x44) /* Bits 0-7: ELE1 Release Threshold */ +#define MPR121_E2TTH (0x45) /* Bits 0-7: ELE2 Touch Threshold */ +#define MPR121_E2RTH (0x46) /* Bits 0-7: ELE2 Release Threshold */ +#define MPR121_E3TTH (0x47) /* Bits 0-7: ELE3 Touch Threshold */ +#define MPR121_E3RTH (0x48) /* Bits 0-7: ELE3 Release Threshold */ +#define MPR121_E4TTH (0x49) /* Bits 0-7: ELE4 Touch Threshold */ +#define MPR121_E4RTH (0x4a) /* Bits 0-7: ELE4 Release Threshold */ +#define MPR121_E5TTH (0x4b) /* Bits 0-7: ELE5 Touch Threshold */ +#define MPR121_E5RTH (0x4c) /* Bits 0-7: ELE5 Release Threshold */ +#define MPR121_E6TTH (0x4d) /* Bits 0-7: ELE6 Touch Threshold */ +#define MPR121_E6RTH (0x4e) /* Bits 0-7: ELE6 Release Threshold */ +#define MPR121_E7TTH (0x4f) /* Bits 0-7: ELE7 Touch Threshold */ +#define MPR121_E7RTH (0x50) /* Bits 0-7: ELE7 Release Threshold */ +#define MPR121_E8TTH (0x51) /* Bits 0-7: ELE8 Touch Threshold */ +#define MPR121_E8RTH (0x52) /* Bits 0-7: ELE8 Release Threshold */ +#define MPR121_E9TTH (0x53) /* Bits 0-7: ELE9 Touch Threshold */ +#define MPR121_E9RTH (0x54) /* Bits 0-7: ELE9 Release Threshold */ +#define MPR121_E10TTH (0x55) /* Bits 0-7: ELE10 Touch Threshold */ +#define MPR121_E10RTH (0x56) /* Bits 0-7: ELE10 Release Threshold */ +#define MPR121_E11TTH (0x57) /* Bits 0-7: ELE11 Touch Threshold */ +#define MPR121_E11RTH (0x58) /* Bits 0-7: ELE11 Release Threshold */ +#define MPR121_E12TTH (0x59) /* Bits 0-7: ELE12 Touch Threshold */ +#define MPR121_E12RTH (0x5a) /* Bits 0-7: ELE12 Release Threshold */ +#define MPR121_DT_DR (0x5b) /* Bits 0-2,4-6: Debounce Touch & Release */ +#define MPR121_AFE1 (0x5c) /* Bits 0-5,6-7: CDC and FFI */ +#define MPR121_AFE2 (0x5d) /* Bits 0-2,3-4,5-7: ESI, SFI, CDT */ +#define MPR121_ECR (0x5e) /* Bits 0-4,4-5,6-7: ELE, ELEPROX, CL */ +#define MPR121_CDC0 (0x5f) /* Bits 0-5: ELE0 Electrode Current */ +#define MPR121_CDC1 (0x60) /* Bits 0-5: ELE1 Electrode Current */ +#define MPR121_CDC2 (0x61) /* Bits 0-5: ELE2 Electrode Current */ +#define MPR121_CDC3 (0x62) /* Bits 0-5: ELE3 Electrode Current */ +#define MPR121_CDC4 (0x63) /* Bits 0-5: ELE4 Electrode Current */ +#define MPR121_CDC5 (0x64) /* Bits 0-5: ELE5 Electrode Current */ +#define MPR121_CDC6 (0x65) /* Bits 0-5: ELE6 Electrode Current */ +#define MPR121_CDC7 (0x66) /* Bits 0-5: ELE7 Electrode Current */ +#define MPR121_CDC8 (0x67) /* Bits 0-5: ELE8 Electrode Current */ +#define MPR121_CDC9 (0x68) /* Bits 0-5: ELE9 Electrode Current */ +#define MPR121_CDC10 (0x69) /* Bits 0-5: ELE10 Electrode Current */ +#define MPR121_CDC11 (0x6a) /* Bits 0-5: ELE11 Electrode Current */ +#define MPR121_CDC12 (0x6b) /* Bits 0-5: ELE12 Electrode Current */ +#define MPR121_CDT0_CDT1 (0x6c) /* Bits 0-2,4-6: E0 & E1 Charge time */ +#define MPR121_CDT2_CDT3 (0x6d) /* Bits 0-2,4-6: E2 & E3 Charge time */ +#define MPR121_CDT4_CDT5 (0x6e) /* Bits 0-2,4-6: E4 & E5 Charge time */ +#define MPR121_CDT6_CDT7 (0x6f) /* Bits 0-2,4-6: E6 & E7 Charge time */ +#define MPR121_CDT8_CDT9 (0x70) /* Bits 0-2,4-6: E8 & E9 Charge time */ +#define MPR121_CDT10_CDT11 (0x71) /* Bits 0-2,4-6: E10 & E11 Charge time */ +#define MPR121_CDT12 (0x72) /* Bits 0-2: E12 Charge time */ +#define MPR121_CTL0 (0x73) /* Bits 0-7: GPIO Control Register 0 */ +#define MPR121_CTL1 (0x74) /* Bits 0-7: GPIO Control Register 0 */ +#define MPR121_DAT (0x75) /* Bits 0-7: GPIO Data Register */ +#define MPR121_DIR (0x76) /* Bits 0-7: GPIO Direction Control Register */ +#define MPR121_EN (0x77) /* Bits 0-7: GPIO Enable Register */ +#define MPR121_SET (0x78) /* Bits 0-7: GPIO Data Set Register */ +#define MPR121_CLR (0x79) /* Bits 0-7: GPIO Data Clear Register */ +#define MPR121_TOG (0x7a) /* Bits 0-7: GPIO Data Toggle Register */ +#define MPR121_AUTOCONF0 (0x7b) /* Bits 0,1,2-3,4-5,6-7: ACE, ARE, BVA, RETRY, AFES */ +#define MPR121_AUTOCONF1 (0x7c) /* Bits 0,1,3,7: ACFIE, ARFIE, OORIE, SCTS */ +#define MPR121_UPLIMIT (0x7d) /* Bits 0-7: Auto Config USL Register */ +#define MPR121_LOWLIMIT (0x7e) /* Bits 0-7: Auto Config LSL Register */ +#define MPR121_TARGETLIMIT (0x7f) /* Bits 0-7: Auto Config Target Level Register */ +#define MPR121_SRST (0x80) /* Bits 0-7: Soft Reset Register */ + +/* Factory Reserved Region (0x81~0xff) */ + +/* Tuning defaults for a standard 3.3 V keypad */ + +#define MPR121_TOUCH_THRESHOLD 12 /* Lower means more sensitive */ +#define MPR121_RELEASE_THRESHOLD 6 /* Must be < touch threshold */ + +/* Touch / Release thresholds (0x41..0x64, 2 bytes each) */ + +#define MPR121_REG_TOUCH_THRESH(n) (0x41 + (n) * 2) +#define MPR121_REG_REL_THRESH(n) (0x42 + (n) * 2) + +/* AFE1: FFI=6 samples (00), CDC=16uA charge (10000) => 0x10 */ + +#define MPR121_AFE1_VALUE 0x10 + +/* AFE2: CDT=0.5 us (001), SFI=4 samples (00), ESI=1 ms (001) => 0x24 */ + +#define MPR121_AFE2_VALUE 0x24 + +/* Auto-config: limit registers (Vdd = 3.3 V, target ~70 % of Vdd) */ + +#define MPR121_UPLIMIT_VALUE 200 /* (Vdd - 0.7) / Vdd * 256 */ +#define MPR121_LOWLIMIT_VALUE 130 /* UPLIMIT * 0.65 */ +#define MPR121_TARGETLIMIT_VALUE 180 /* UPLIMIT * 0.9 */ + +/* ECR: CL=10 (use 5 MSB of baseline), ELEPROX_EN=0, ELE_EN=1100 (ELE0-11) */ + +#define MPR121_ECR_VALUE 0xCC /* Run mode, all 12 electrodes */ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __DRIVERS_INPUT_MPR121_H */ diff --git a/include/nuttx/input/mpr121.h b/include/nuttx/input/mpr121.h new file mode 100644 index 0000000000000..face27066f927 --- /dev/null +++ b/include/nuttx/input/mpr121.h @@ -0,0 +1,96 @@ +/**************************************************************************** + * include/nuttx/input/mpr121.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 __INCLUDE_NUTTX_INPUT_MPR121_H +#define __INCLUDE_NUTTX_INPUT_MPR121_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Number of keys used by MPR121 */ + +#define MPR121_NUMKEY 12 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef uint32_t mpr121_pin_t; + +/* Keypad matrix configuration structure passed to mpr121_register() */ + +struct mpr121_config_s +{ + CODE int (*irq_attach)(FAR const struct mpr121_config_s *state, + xcpt_t isr, FAR void *arg); + FAR struct i2c_master_s *i2c_dev; + uint8_t i2c_addr; + + /* Keymap: keycode[MPR121_NUMKEY] to map the keys of MPR121 keypad */ + + FAR const uint32_t *keymap; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/**************************************************************************** + * Name: mpr121_register + * + * Description: + * Configure and register a keypad matrix device. This will create the + * /dev/keypadN device node and enable keyboard scanning. + * + * Input Parameters: + * config - The keyboard matrix configuration. This structure is not + * copied; it must persist for the lifetime of the driver. + * devpath - The device path for the /dev/keypadN device. + * + * Returned Value: + * Zero is returned on success. Otherwise, a negated errno value is + * returned to indicate the nature of the failure. + * + ****************************************************************************/ + +int mpr121_register(FAR const struct mpr121_config_s *config, + FAR const char *devpath); + +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_NUTTX_INPUT_MPR121_H */