diff --git a/boards/arm/mimxrt1050_evk/Kconfig.defconfig b/boards/arm/mimxrt1050_evk/Kconfig.defconfig index 5c44a5a690c0ff..cbffed013c7038 100644 --- a/boards/arm/mimxrt1050_evk/Kconfig.defconfig +++ b/boards/arm/mimxrt1050_evk/Kconfig.defconfig @@ -42,6 +42,9 @@ config GPIO_MCUX_IGPIO_5 endif # GPIO_MCUX_IGPIO +config I2C + default y if LVGL + if I2C_MCUX_LPI2C config I2C_1 @@ -66,6 +69,19 @@ config UART_MCUX_LPUART_3 endif # UART_MCUX_LPUART +config KSCAN + default y if LVGL + +if KSCAN + +config KSCAN_FT5336 + default y + +config KSCAN_INIT_PRIORITY + default 60 + +endif # KSCAN + if NETWORKING config NET_L2_ETHERNET @@ -81,6 +97,12 @@ if LVGL config LVGL_DISPLAY_DEV_NAME default "ELCDIF_1" +config LVGL_POINTER_KSCAN + default y + +config LVGL_POINTER_KSCAN_DEV_NAME + default "FT5336" + config LVGL_HOR_RES default 480 diff --git a/boards/arm/mimxrt1050_evk/mimxrt1050_evk.dts b/boards/arm/mimxrt1050_evk/mimxrt1050_evk.dts index 98dba455159d47..80be9d6096a525 100644 --- a/boards/arm/mimxrt1050_evk/mimxrt1050_evk.dts +++ b/boards/arm/mimxrt1050_evk/mimxrt1050_evk.dts @@ -120,6 +120,13 @@ arduino_serial: &uart3 {}; int1-gpios = <&gpio1 10 0>; int2-gpios = <&gpio1 11 0>; }; + + ft5336@38 { + compatible = "focaltech,ft5336"; + reg = <0x38>; + label = "FT5336"; + int-gpios = <&gpio1 11 0>; + }; }; &uart1 { diff --git a/boards/arm/mimxrt1060_evk/Kconfig.defconfig b/boards/arm/mimxrt1060_evk/Kconfig.defconfig index 1cc953dbe4355a..1c89c8a89347be 100644 --- a/boards/arm/mimxrt1060_evk/Kconfig.defconfig +++ b/boards/arm/mimxrt1060_evk/Kconfig.defconfig @@ -27,6 +27,9 @@ config GPIO_MCUX_IGPIO_5 endif # GPIO_MCUX_IGPIO +config I2C + default y if LVGL + if I2C_MCUX_LPI2C config I2C_1 @@ -44,6 +47,19 @@ config UART_MCUX_LPUART_3 endif # UART_MCUX_LPUART +config KSCAN + default y if LVGL + +if KSCAN + +config KSCAN_FT5336 + default y + +config KSCAN_INIT_PRIORITY + default 60 + +endif # KSCAN + if NETWORKING config NET_L2_ETHERNET @@ -59,6 +75,12 @@ if LVGL config LVGL_DISPLAY_DEV_NAME default "ELCDIF_1" +config LVGL_POINTER_KSCAN + default y + +config LVGL_POINTER_KSCAN_DEV_NAME + default "FT5336" + config LVGL_HOR_RES default 480 diff --git a/boards/arm/mimxrt1060_evk/mimxrt1060_evk.dts b/boards/arm/mimxrt1060_evk/mimxrt1060_evk.dts index a4c6291935d801..84e248994cad6f 100644 --- a/boards/arm/mimxrt1060_evk/mimxrt1060_evk.dts +++ b/boards/arm/mimxrt1060_evk/mimxrt1060_evk.dts @@ -110,6 +110,13 @@ arduino_serial: &uart3 {}; &i2c1 { status = "okay"; + + ft5336@38 { + compatible = "focaltech,ft5336"; + reg = <0x38>; + label = "FT5336"; + int-gpios = <&gpio1 11 0>; + }; }; &uart1 { diff --git a/boards/arm/mimxrt1064_evk/Kconfig.defconfig b/boards/arm/mimxrt1064_evk/Kconfig.defconfig index b5b02f26c626d3..b10166cbc6d1db 100644 --- a/boards/arm/mimxrt1064_evk/Kconfig.defconfig +++ b/boards/arm/mimxrt1064_evk/Kconfig.defconfig @@ -26,6 +26,9 @@ config GPIO_MCUX_IGPIO_5 endif # GPIO_MCUX_IGPIO +config I2C + default y if LVGL + if I2C_MCUX_LPI2C config I2C_1 @@ -40,6 +43,19 @@ config UART_MCUX_LPUART_1 endif # UART_MCUX_LPUART +config KSCAN + default y if LVGL + +if KSCAN + +config KSCAN_FT5336 + default y + +config KSCAN_INIT_PRIORITY + default 60 + +endif # KSCAN + if NETWORKING config NET_L2_ETHERNET @@ -55,6 +71,12 @@ if LVGL config LVGL_DISPLAY_DEV_NAME default "ELCDIF_1" +config LVGL_POINTER_KSCAN + default y + +config LVGL_POINTER_KSCAN_DEV_NAME + default "FT5336" + config LVGL_HOR_RES default 480 diff --git a/boards/arm/mimxrt1064_evk/mimxrt1064_evk.dts b/boards/arm/mimxrt1064_evk/mimxrt1064_evk.dts index 019764ad509858..cc31f82053ab4e 100644 --- a/boards/arm/mimxrt1064_evk/mimxrt1064_evk.dts +++ b/boards/arm/mimxrt1064_evk/mimxrt1064_evk.dts @@ -119,6 +119,13 @@ arduino_i2c: &i2c1 {}; }; }; }; + + ft5336@38 { + compatible = "focaltech,ft5336"; + reg = <0x38>; + label = "FT5336"; + int-gpios = <&gpio1 11 0>; + }; }; &uart1 { diff --git a/drivers/kscan/CMakeLists.txt b/drivers/kscan/CMakeLists.txt index 7d7a9114eb8333..4e88cf6e71d17e 100644 --- a/drivers/kscan/CMakeLists.txt +++ b/drivers/kscan/CMakeLists.txt @@ -2,6 +2,7 @@ zephyr_library() +zephyr_library_sources_ifdef(CONFIG_KSCAN_FT5336 kscan_ft5336.c) zephyr_library_sources_ifdef(CONFIG_KSCAN_XEC kscan_mchp_xec.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE kscan_handlers.c) diff --git a/drivers/kscan/Kconfig b/drivers/kscan/Kconfig index 2e63103b3bf072..34b223c6137cdd 100644 --- a/drivers/kscan/Kconfig +++ b/drivers/kscan/Kconfig @@ -10,6 +10,7 @@ menuconfig KSCAN if KSCAN +source "drivers/kscan/Kconfig.ft5336" source "drivers/kscan/Kconfig.xec" module = KSCAN diff --git a/drivers/kscan/Kconfig.ft5336 b/drivers/kscan/Kconfig.ft5336 new file mode 100644 index 00000000000000..1b876b1198dd82 --- /dev/null +++ b/drivers/kscan/Kconfig.ft5336 @@ -0,0 +1,16 @@ +# Copyright (c) 2020 NXP +# SPDX-License-Identifier: Apache-2.0 + +menuconfig KSCAN_FT5336 + bool "FT5336 capacitive touch panel driver" + depends on I2C && HAS_DTS_I2C + help + Enable driver for the FT5336 capacitive touch panel controller. + +if KSCAN_FT5336 + +config KSCAN_FT5336_PERIOD + int "Sample period (ms)" + default 10 + +endif # KSCAN_FT5336 diff --git a/drivers/kscan/kscan_ft5336.c b/drivers/kscan/kscan_ft5336.c new file mode 100644 index 00000000000000..71ee165ed4166d --- /dev/null +++ b/drivers/kscan/kscan_ft5336.c @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2020 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +LOG_MODULE_REGISTER(ft5336, CONFIG_KSCAN_LOG_LEVEL); + +#define FT5406_DATA_SIZE 0x20 + +enum ft5336_event { + FT5336_EVENT_DOWN = 0, + FT5336_EVENT_UP = 1, + FT5336_EVENT_CONTACT = 2, + FT5336_EVENT_RESERVED = 3, +}; + +struct ft5336_config { + char *i2c_name; + u8_t i2c_address; +}; + +struct ft5336_data { + struct device *i2c; + kscan_callback_t callback; + struct k_work work; + struct k_timer timer; + struct device *dev; +}; + +static int ft5336_read(struct device *dev) +{ + const struct ft5336_config *config = dev->config->config_info; + struct ft5336_data *data = dev->driver_data; + u8_t buffer[FT5406_DATA_SIZE]; + u8_t event; + u16_t row, column; + bool pressed; + + if (i2c_burst_read(data->i2c, config->i2c_address, 1, buffer, + sizeof(buffer))) { + LOG_ERR("Could not read point"); + return -EIO; + } + + event = buffer[2] >> 6; + pressed = (event == FT5336_EVENT_DOWN) || + (event == FT5336_EVENT_CONTACT); + + row = ((buffer[2] & 0x0f) << 8) | buffer[3]; + column = ((buffer[4] & 0x0f) << 8) | buffer[5]; + + data->callback(dev, row, column, pressed); + + return 0; +} + +static void ft5336_timer_handler(struct k_timer *timer) +{ + struct ft5336_data *data = + CONTAINER_OF(timer, struct ft5336_data, timer); + + k_work_submit(&data->work); +} + +static void ft5336_work_handler(struct k_work *work) +{ + struct ft5336_data *data = + CONTAINER_OF(work, struct ft5336_data, work); + + ft5336_read(data->dev); +} + +static int ft5336_configure(struct device *dev, kscan_callback_t callback) +{ + struct ft5336_data *data = dev->driver_data; + + if (!callback) { + return -EINVAL; + } + + data->callback = callback; + + return 0; +} + +static int ft5336_enable_callback(struct device *dev) +{ + struct ft5336_data *data = dev->driver_data; + + k_timer_start(&data->timer, K_MSEC(CONFIG_KSCAN_FT5336_PERIOD), + K_MSEC(CONFIG_KSCAN_FT5336_PERIOD)); + + return 0; +} + +static int ft5336_disable_callback(struct device *dev) +{ + struct ft5336_data *data = dev->driver_data; + + k_timer_stop(&data->timer); + + return 0; +} + +static int ft5336_init(struct device *dev) +{ + const struct ft5336_config *config = dev->config->config_info; + struct ft5336_data *data = dev->driver_data; + + data->i2c = device_get_binding(config->i2c_name); + if (data->i2c == NULL) { + LOG_ERR("Could not find I2C device"); + return -EINVAL; + } + + if (i2c_reg_write_byte(data->i2c, config->i2c_address, 0, 0)) { + LOG_ERR("Could not enable"); + return -EINVAL; + } + + data->dev = dev; + + k_work_init(&data->work, ft5336_work_handler); + k_timer_init(&data->timer, ft5336_timer_handler, NULL); + + return 0; +} + + +static const struct kscan_driver_api ft5336_driver_api = { + .config = ft5336_configure, + .enable_callback = ft5336_enable_callback, + .disable_callback = ft5336_disable_callback, +}; + +static const struct ft5336_config ft5336_config = { + .i2c_name = DT_INST_0_FOCALTECH_FT5336_BUS_NAME, + .i2c_address = DT_INST_0_FOCALTECH_FT5336_BASE_ADDRESS, +}; + +static struct ft5336_data ft5336_data; + +DEVICE_AND_API_INIT(ft5336, DT_INST_0_FOCALTECH_FT5336_LABEL, ft5336_init, + &ft5336_data, &ft5336_config, + POST_KERNEL, CONFIG_KSCAN_INIT_PRIORITY, + &ft5336_driver_api); diff --git a/dts/bindings/kscan/focaltech,ft5336.yaml b/dts/bindings/kscan/focaltech,ft5336.yaml new file mode 100644 index 00000000000000..98d4862e43c36a --- /dev/null +++ b/dts/bindings/kscan/focaltech,ft5336.yaml @@ -0,0 +1,13 @@ +# Copyright (c) 2020 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: FT5336 capacitive touch panel + +compatible: "focaltech,ft5336" + +include: [kscan.yaml, i2c-device.yaml] + +properties: + int-gpios: + type: phandle-array + required: false diff --git a/dts/bindings/kscan/kscan.yaml b/dts/bindings/kscan/kscan.yaml index ec28b3af949705..74fe974d95287b 100644 --- a/dts/bindings/kscan/kscan.yaml +++ b/dts/bindings/kscan/kscan.yaml @@ -8,11 +8,5 @@ include: base.yaml bus: kscan properties: - "#address-cells": - required: true - const: 1 - "#size-cells": - type: int - const: 0 label: required: true diff --git a/dts/bindings/kscan/microchip,xec-kscan.yaml b/dts/bindings/kscan/microchip,xec-kscan.yaml index 59c2f467c1e047..318d70a036238f 100644 --- a/dts/bindings/kscan/microchip,xec-kscan.yaml +++ b/dts/bindings/kscan/microchip,xec-kscan.yaml @@ -8,6 +8,12 @@ compatible: "microchip,xec-kscan" include: kscan.yaml properties: + "#address-cells": + required: true + const: 1 + "#size-cells": + type: int + const: 0 reg: required: true diff --git a/include/drivers/kscan.h b/include/drivers/kscan.h index dc95917920753c..6caa5bfdd61bb9 100644 --- a/include/drivers/kscan.h +++ b/include/drivers/kscan.h @@ -41,7 +41,7 @@ extern "C" { * @param row Describes row change. * @param pressed Describes the kind of key event. */ -typedef void (*kscan_callback_t)(struct device *dev, u8_t row, u8_t column, +typedef void (*kscan_callback_t)(struct device *dev, u32_t row, u32_t column, bool pressed); /** diff --git a/lib/gui/lvgl/Kconfig b/lib/gui/lvgl/Kconfig index 852f13ef061a7b..5e766ef5574ed5 100644 --- a/lib/gui/lvgl/Kconfig +++ b/lib/gui/lvgl/Kconfig @@ -18,6 +18,28 @@ config LVGL_DISPLAY_DEV_NAME help Name of the display device to use for rendering. +config LVGL_POINTER_KSCAN + bool "Keyboard scan pointer input" + depends on KSCAN + help + Enable keyboard scan pointer input + +if LVGL_POINTER_KSCAN + +config LVGL_POINTER_KSCAN_DEV_NAME + string "Keyboard scan device name for pointer input" + default "KSCAN" + help + Name of the keyboard scan device to use for pointer input. + +config LVGL_POINTER_KSCAN_MSGQ_COUNT + int "Keyboard scan message queue count maximum" + default 10 + help + Maximum number of items in the keyboard scan message queue. + +endif # LVGL_POINTER_KSCAN + config LVGL_HOR_RES int "Horizontal Screen Resolution" default 320 diff --git a/lib/gui/lvgl/lvgl.c b/lib/gui/lvgl/lvgl.c index f7e7c39f9cff8d..c310000b740dc9 100644 --- a/lib/gui/lvgl/lvgl.c +++ b/lib/gui/lvgl/lvgl.c @@ -11,6 +11,9 @@ #ifdef CONFIG_LVGL_FILESYSTEM #include "lvgl_fs.h" #endif +#ifdef CONFIG_LVGL_POINTER_KSCAN +#include +#endif #include LV_MEM_CUSTOM_INCLUDE #define LOG_LEVEL CONFIG_LVGL_LOG_LEVEL @@ -146,6 +149,75 @@ static int lvgl_allocate_rendering_buffers(lv_disp_drv_t *disp_drv) } #endif /* CONFIG_LVGL_BUFFER_ALLOC_STATIC */ +#ifdef CONFIG_LVGL_POINTER_KSCAN +K_MSGQ_DEFINE(kscan_msgq, sizeof(lv_indev_data_t), + CONFIG_LVGL_POINTER_KSCAN_MSGQ_COUNT, 4); + +static void lvgl_pointer_kscan_callback(struct device *dev, u32_t row, + u32_t col, bool pressed) +{ + lv_indev_data_t data = { + .point.x = col, + .point.y = row, + .state = pressed ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL, + }; + + if (k_msgq_put(&kscan_msgq, &data, K_NO_WAIT) != 0) { + LOG_ERR("Could put input data into queue"); + } +} + +static bool lvgl_pointer_kscan_read(lv_indev_drv_t *drv, lv_indev_data_t *data) +{ + static lv_indev_data_t prev = { + .point.x = 0, + .point.y = 0, + .state = LV_INDEV_STATE_REL, + }; + + lv_indev_data_t curr; + + if (k_msgq_get(&kscan_msgq, &curr, K_NO_WAIT) == 0) { + prev = curr; + } + + *data = prev; + + return k_msgq_num_used_get(&kscan_msgq) > 0; +} + +static int lvgl_pointer_kscan_init(void) +{ + struct device *kscan_dev = + device_get_binding(CONFIG_LVGL_POINTER_KSCAN_DEV_NAME); + + lv_indev_drv_t indev_drv; + + if (kscan_dev == NULL) { + LOG_ERR("Keyboard scan device not found."); + return -ENODEV; + } + + if (kscan_config(kscan_dev, lvgl_pointer_kscan_callback) < 0) { + LOG_ERR("Could not configure keyboard scan device."); + return -ENODEV; + } + + lv_indev_drv_init(&indev_drv); + indev_drv.type = LV_INDEV_TYPE_POINTER; + indev_drv.read_cb = lvgl_pointer_kscan_read; + + if (lv_indev_drv_register(&indev_drv) == NULL) { + LOG_ERR("Failed to register input device."); + return -EPERM; + } + + kscan_enable_callback(kscan_dev); + + return 0; +} +#endif /* CONFIG_LVGL_POINTER_KSCAN */ + static int lvgl_init(struct device *dev) { ARG_UNUSED(dev); @@ -184,6 +256,10 @@ static int lvgl_init(struct device *dev) return -EPERM; } +#ifdef CONFIG_LVGL_POINTER_KSCAN + lvgl_pointer_kscan_init(); +#endif /* CONFIG_LVGL_POINTER_KSCAN */ + return 0; } diff --git a/samples/display/lvgl/README.rst b/samples/display/lvgl/README.rst index 3ceb0df94392c3..adbd3af52e63cc 100644 --- a/samples/display/lvgl/README.rst +++ b/samples/display/lvgl/README.rst @@ -7,7 +7,10 @@ Overview ******** This sample application displays "Hello World" in the center of the screen -and a counter at the bottom which increments every second. +and a counter at the bottom which increments every second. If an input driver +is supported, such as the touch panel controller on mimxrt10{50,60,64}_evk +boards, "Hello World" is enclosed in a button that changes to the toggled state +when touched. Requirements ************ diff --git a/samples/display/lvgl/prj.conf b/samples/display/lvgl/prj.conf index 64efb35cb77b59..fb4814a9af2a67 100644 --- a/samples/display/lvgl/prj.conf +++ b/samples/display/lvgl/prj.conf @@ -7,3 +7,5 @@ CONFIG_LOG=y CONFIG_LVGL=y CONFIG_LVGL_OBJ_LABEL=y +CONFIG_LVGL_OBJ_CONTAINER=y +CONFIG_LVGL_OBJ_BUTTON=y diff --git a/samples/display/lvgl/src/main.c b/samples/display/lvgl/src/main.c index ebe89eb7726687..1231456973b41a 100644 --- a/samples/display/lvgl/src/main.c +++ b/samples/display/lvgl/src/main.c @@ -30,7 +30,17 @@ void main(void) return; } - hello_world_label = lv_label_create(lv_scr_act(), NULL); + if (IS_ENABLED(CONFIG_LVGL_POINTER_KSCAN)) { + lv_obj_t *hello_world_button; + + hello_world_button = lv_btn_create(lv_scr_act(), NULL); + lv_obj_align(hello_world_button, NULL, LV_ALIGN_CENTER, 0, 0); + lv_btn_set_fit(hello_world_button, LV_FIT_TIGHT); + hello_world_label = lv_label_create(hello_world_button, NULL); + } else { + hello_world_label = lv_label_create(lv_scr_act(), NULL); + } + lv_label_set_text(hello_world_label, "Hello world!"); lv_obj_align(hello_world_label, NULL, LV_ALIGN_CENTER, 0, 0); diff --git a/samples/drivers/kscan/src/main.c b/samples/drivers/kscan/src/main.c index c7aebebc3a3163..30d7ccc06679a2 100644 --- a/samples/drivers/kscan/src/main.c +++ b/samples/drivers/kscan/src/main.c @@ -120,7 +120,7 @@ static void typematic_callback(struct k_timer *timer) LOG_INF("Typematic : %u\n", last_key); } -static void kb_callback(struct device *dev, u8_t row, u8_t col, bool pressed) +static void kb_callback(struct device *dev, u32_t row, u32_t col, bool pressed) { ARG_UNUSED(dev); last_key = keymap[col][row]; diff --git a/tests/drivers/kscan/kscan_api/src/test_kscan.c b/tests/drivers/kscan/kscan_api/src/test_kscan.c index f8e1d6c1136ce8..8fec24a56b4fd4 100644 --- a/tests/drivers/kscan/kscan_api/src/test_kscan.c +++ b/tests/drivers/kscan/kscan_api/src/test_kscan.c @@ -14,7 +14,7 @@ #define KSCAN_DEV_NAME DT_KSCAN_0_NAME #endif -static void kb_callback(struct device *dev, u8_t row, u8_t col, bool pressed) +static void kb_callback(struct device *dev, u32_t row, u32_t col, bool pressed) { ARG_UNUSED(dev); ARG_UNUSED(row);