diff --git a/graphics/lvgl/Kconfig b/graphics/lvgl/Kconfig index aedf75bfd..338651050 100644 --- a/graphics/lvgl/Kconfig +++ b/graphics/lvgl/Kconfig @@ -200,6 +200,16 @@ config LV_PORT_KEYPAD_KEY_END_MAP_BIT endif # LV_PORT_USE_KEYPAD +config LV_USE_TOUCHPAD_INTERFACE + depends on INPUT_TOUCHSCREEN + bool "Touchpad input interface" + default n + +config LV_TOUCHPAD_INTERFACE_DEFAULT_DEVICEPATH + depends on LV_USE_TOUCHPAD_INTERFACE + string "Touchpad default device path" + default "/dev/input0" + if LV_PORT_USE_ENCODER config LV_PORT_ENCODER_DEFAULT_DEVICEPATH diff --git a/testing/drivertest/Makefile b/testing/drivertest/Makefile index 55ac693d1..2882101dc 100644 --- a/testing/drivertest/Makefile +++ b/testing/drivertest/Makefile @@ -128,4 +128,9 @@ MAINSRC += drivertest_regulator.c PROGNAME += cmocka_driver_regulator endif +ifneq ($(CONFIG_LV_USE_TOUCHPAD_INTERFACE),) +MAINSRC += drivertest_touchpanel.c +PROGNAME += cmocka_driver_touchpanel +endif + include $(APPDIR)/Application.mk diff --git a/testing/drivertest/drivertest_touchpanel.c b/testing/drivertest/drivertest_touchpanel.c new file mode 100644 index 000000000..639c210ca --- /dev/null +++ b/testing/drivertest/drivertest_touchpanel.c @@ -0,0 +1,724 @@ +/**************************************************************************** + * apps/testing/drivertest/drivertest_touchpanel.c + * + * 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 + +/**************************************************************************** + * Private Type + ****************************************************************************/ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define SQUARE_X 40 +#define SQUARE_Y 80 +#define CIRCULAR_PIX 25 +#define SQUARE_PIX 20 +#define DISTANCE_PIX 15 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct +{ + int fd; + lv_indev_drv_t indev_drv; + lv_indev_t *indev; + bool is_square; + + union + { + struct + { + lv_obj_t *canvas; + lv_color_t *canvas_buf; + bool left_top; + bool right_top; + bool left_bottom; + bool right_bottom; + + float line_k; + + /* left_top to right_bottom */ + + float line_b_up_1; + float line_b_down_1; + + /* right_top to left_bottom */ + + float line_b_up_2; + float line_b_down_2; + }; + + struct + { + lv_obj_t *slider_h; + lv_obj_t *slider_v; + lv_obj_t *arc_slider; + int16_t diameter; + lv_point_t arc_indic_pos; + lv_obj_t *label_pass; + }; + }; +}touchpad_s; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: canvas_frame + ****************************************************************************/ + +void darw_canvas_frame(touchpad_s *touchpad) +{ + lv_draw_rect_dsc_t rect_dsc; + int i_hor; + int j_ver; + lv_draw_line_dsc_t line_dsc; + lv_point_t line_point[2]; + + /* Draw squares */ + + int sum_x = LV_HOR_RES / SQUARE_X; + int sum_y = LV_VER_RES / SQUARE_Y; + + lv_draw_rect_dsc_init(&rect_dsc); + rect_dsc.bg_opa = LV_OPA_COVER; + rect_dsc.bg_color = lv_palette_main(LV_PALETTE_GREY); + rect_dsc.border_width = 1; + + /* Draw HOR top and middle and bottom */ + + for (i_hor = 0; i_hor < sum_x; i_hor++) + { + lv_canvas_draw_rect(touchpad->canvas, i_hor * SQUARE_X, 0, + SQUARE_X, SQUARE_Y, &rect_dsc); + lv_canvas_draw_rect(touchpad->canvas, i_hor * SQUARE_X, + LV_VER_RES - SQUARE_Y, SQUARE_X, SQUARE_Y, &rect_dsc); + } + + /* Draw VER left and middle and right */ + + for (j_ver = 0; j_ver < sum_y; j_ver++) + { + lv_canvas_draw_rect(touchpad->canvas, 0, j_ver * SQUARE_Y, + SQUARE_X, SQUARE_Y, &rect_dsc); + lv_canvas_draw_rect(touchpad->canvas, LV_HOR_RES - SQUARE_X, + j_ver * SQUARE_Y, SQUARE_X, SQUARE_Y, &rect_dsc); + } + + /* Draw line diagonal */ + + lv_draw_line_dsc_init(&line_dsc); + + /* left_top to right_bottom */ + + line_point[0].x = SQUARE_Y / 2; + line_point[0].y = 0; + line_point[1].x = LV_HOR_RES; + line_point[1].y = LV_VER_RES - SQUARE_Y / 2; + lv_canvas_draw_line(touchpad->canvas, line_point, 2, &line_dsc); + + line_point[0].x = 0; + line_point[0].y = SQUARE_Y / 2; + line_point[1].x = LV_HOR_RES - SQUARE_Y / 2; + line_point[1].y = LV_VER_RES; + lv_canvas_draw_line(touchpad->canvas, line_point, 2, &line_dsc); + + /* right_top to left_bottom */ + + line_point[0].x = LV_HOR_RES - SQUARE_Y / 2; + line_point[0].y = 0; + line_point[1].x = 0; + line_point[1].y = LV_VER_RES - SQUARE_Y / 2; + lv_canvas_draw_line(touchpad->canvas, line_point, 2, &line_dsc); + + line_point[0].x = LV_HOR_RES; + line_point[0].y = SQUARE_Y / 2; + line_point[1].x = SQUARE_Y / 2; + line_point[1].y = LV_VER_RES; + lv_canvas_draw_line(touchpad->canvas, line_point, 2, &line_dsc); + + /* left_top to right_bottom */ + + touchpad->line_k = (LV_VER_RES - SQUARE_Y / 2) / + (float)(LV_HOR_RES - SQUARE_X / 2); + touchpad->line_b_up_1 = (SQUARE_Y / 2 - LV_VER_RES) / + (float)(LV_HOR_RES - SQUARE_X / 2) * (SQUARE_X / 2); + touchpad->line_b_down_1 = SQUARE_Y / 2; + + /* right_top to left_bottom */ + + touchpad->line_b_up_2 = LV_VER_RES - SQUARE_Y / 2; + touchpad->line_b_down_2 = + (LV_HOR_RES * LV_VER_RES - (SQUARE_Y * SQUARE_Y / 4)) / + (float)(LV_HOR_RES - SQUARE_X / 2); +} + +/**************************************************************************** + * Name: draw_track_line + ****************************************************************************/ + +void draw_track_line(lv_obj_t *canvas, lv_point_t pos) +{ + int index_x; + int index_y; + for (index_x = pos.x - 1; index_x < pos.x + 2; index_x++) + { + for (index_y = pos.y - 1; index_y < pos.y + 2; index_y++) + { + lv_canvas_set_px(canvas, index_x, index_y, + lv_palette_main(LV_PALETTE_RED)); + } + } +} + +/**************************************************************************** + * Name: draw_touch_fill + ****************************************************************************/ + +void draw_touch_fill(touchpad_s *touchpad, lv_point_t pos) +{ + lv_draw_rect_dsc_t rect_dsc; + lv_draw_line_dsc_t line_dsc; + lv_point_t line_point[2]; + + if (pos.x < 1 || pos.x > LV_HOR_RES || pos.y < 1 || pos.y > LV_VER_RES) + { + return; + } + + /* Calculate square index */ + + int x_index = pos.x / SQUARE_X; + int y_index = pos.y / SQUARE_Y; + + lv_draw_rect_dsc_init(&rect_dsc); + rect_dsc.bg_opa = LV_OPA_COVER; + rect_dsc.bg_color = lv_palette_main(LV_PALETTE_BLUE); + rect_dsc.border_width = 1; + + if (x_index == 0 || x_index == LV_HOR_RES / SQUARE_X - 1 + || y_index == 0 || y_index == LV_VER_RES / SQUARE_Y - 1) + { + lv_canvas_draw_rect(touchpad->canvas, x_index * SQUARE_X, + y_index * SQUARE_Y, SQUARE_X, SQUARE_Y, &rect_dsc); + draw_track_line(touchpad->canvas, pos); + } + + /* left_top */ + + if (pos.x < SQUARE_X && pos.y < SQUARE_Y) + { + touchpad->left_top = true; + } + + /* right_bottom */ + + if (pos.x > (LV_HOR_RES - SQUARE_X) && pos.y > (LV_VER_RES - SQUARE_Y)) + { + touchpad->right_bottom = true; + } + + /* right_top */ + + if (pos.x > (LV_HOR_RES - SQUARE_X) && pos.y < SQUARE_Y) + { + touchpad->right_top = true; + } + + /* left_bottom */ + + if (pos.x < SQUARE_X && pos.y > (LV_VER_RES - SQUARE_Y)) + { + touchpad->left_bottom = true; + } + + if (touchpad->left_top || touchpad->right_bottom) + { + if (pos.y < (touchpad->line_k * pos.x + + touchpad->line_b_up_1) || + pos.y > (touchpad->line_k * pos.x + + touchpad->line_b_down_1)) + { + touchpad->left_top = false; + touchpad->right_bottom = false; + } + else + { + draw_track_line(touchpad->canvas, pos); + } + } + + if (touchpad->right_top || touchpad->left_bottom) + { + if (pos.y < (-touchpad->line_k * pos.x + + touchpad->line_b_up_2) || + pos.y > (-touchpad->line_k * pos.x + + touchpad->line_b_down_2)) + { + touchpad->right_top = false; + touchpad->left_bottom = false; + } + else + { + draw_track_line(touchpad->canvas, pos); + } + } + + /* fill blue color: left_top to right_bottom */ + + if (touchpad->left_top && touchpad->right_bottom) + { + lv_draw_line_dsc_init(&line_dsc); + line_dsc.opa = LV_OPA_COVER; + line_dsc.color = lv_palette_main(LV_PALETTE_BLUE); + line_dsc.width = SQUARE_Y; + + line_point[0].x = 0; + line_point[0].y = 0; + line_point[1].x = LV_HOR_RES; + line_point[1].y = LV_VER_RES; + lv_canvas_draw_line(touchpad->canvas, line_point, 2, &line_dsc); + } + + /* fill blue color: right_top to left_bottom */ + + if (touchpad->left_bottom && touchpad->right_top) + { + lv_draw_line_dsc_init(&line_dsc); + line_dsc.opa = LV_OPA_COVER; + line_dsc.color = lv_palette_main(LV_PALETTE_BLUE); + line_dsc.width = SQUARE_Y; + + line_point[0].x = LV_HOR_RES; + line_point[0].y = 0; + line_point[1].x = 0; + line_point[1].y = LV_VER_RES; + lv_canvas_draw_line(touchpad->canvas, line_point, 2, &line_dsc); + } +} + +/**************************************************************************** + * Name: create_slider + ****************************************************************************/ + +lv_obj_t *create_slider(bool is_horizontal) +{ + lv_obj_t *slider = lv_slider_create(lv_scr_act()); + if (is_horizontal) + { + lv_obj_set_size(slider, LV_HOR_RES, lv_pct(3)); + lv_obj_set_pos(slider, 0, LV_VER_RES / 2); + lv_slider_set_range(slider, 0, LV_HOR_RES); + } + else + { + lv_obj_set_size(slider, lv_pct(3), LV_VER_RES); + lv_obj_set_pos(slider, LV_HOR_RES / 2, 0); + lv_slider_set_range(slider, 0, LV_VER_RES); + } + + lv_slider_set_value(slider, SQUARE_PIX, LV_ANIM_OFF); + return slider; +} + +/**************************************************************************** + * Name: create_arc + ****************************************************************************/ + +lv_obj_t *create_arc(int16_t diameter) +{ + lv_obj_t *arc_slider = lv_arc_create(lv_scr_act()); + lv_obj_set_size(arc_slider, diameter, diameter); + lv_arc_set_rotation(arc_slider, 180); + lv_arc_set_bg_angles(arc_slider, 0, 360); + lv_arc_set_range(arc_slider, 0, diameter * 2); + lv_arc_set_value(arc_slider, 0); + lv_obj_center(arc_slider); + return arc_slider; +} + +/**************************************************************************** + * Name: circle_hit_test + ****************************************************************************/ + +bool circle_hit_test(touchpad_s *touchpad, lv_point_t pos) +{ + int16_t distance; + if (abs(pos.x - touchpad->arc_indic_pos.x) < CIRCULAR_PIX && + abs(pos.y - touchpad->arc_indic_pos.y) < CIRCULAR_PIX) + { + distance = sqrt((pos.x - LV_HOR_RES / 2) * (pos.x - LV_HOR_RES / 2) + + (pos.y - LV_VER_RES / 2) * (pos.y - LV_VER_RES / 2)); + return abs(distance - touchpad->diameter / 2) <= SQUARE_PIX; + } + + return false; +} + +/**************************************************************************** + * Name: touchpad_read + ****************************************************************************/ + +void touchpad_read(lv_indev_drv_t *drv, lv_indev_data_t *data) +{ + touchpad_s *touchpad = drv->user_data; + struct touch_sample_s sample; + lv_point_t pos; + int nbytes; + uint8_t touch_flags; + + /* Read one sample */ + + nbytes = read(touchpad->fd, &sample, + sizeof(struct touch_sample_s)); + + /* Handle unexpected return values */ + + if (nbytes != sizeof(struct touch_sample_s)) + { + return; + } + + touch_flags = sample.point[0].flags; + if (touch_flags & TOUCH_DOWN || touch_flags & TOUCH_MOVE) + { + pos.x = sample.point[0].x; + pos.y = sample.point[0].y; + if (touchpad->is_square == true) + { + draw_touch_fill(touchpad, pos); + } + else + { + if (touchpad->slider_v == NULL && + pos.x - lv_slider_get_value(touchpad->slider_h) > 0 && + pos.x - lv_slider_get_value(touchpad->slider_h) + < DISTANCE_PIX && + abs(pos.y - LV_VER_RES / 2 - DISTANCE_PIX) < SQUARE_PIX) + { + lv_slider_set_value(touchpad->slider_h, pos.x, LV_ANIM_ON); + } + else if (touchpad->arc_slider == NULL && + LV_HOR_RES - lv_slider_get_value(touchpad->slider_h) + < SQUARE_PIX) + { + if (touchpad->slider_v == NULL) + { + touchpad->slider_v = create_slider(false); + } + + if (LV_VER_RES - pos.y - + lv_slider_get_value(touchpad->slider_v) > 0 && + LV_VER_RES - pos.y - + lv_slider_get_value(touchpad->slider_v) < DISTANCE_PIX && + abs(pos.x - LV_HOR_RES / 2 - DISTANCE_PIX) < SQUARE_PIX) + { + lv_slider_set_value(touchpad->slider_v, + LV_VER_RES - pos.y, LV_ANIM_ON); + } + } + + if (touchpad->slider_v && + LV_VER_RES - lv_slider_get_value(touchpad->slider_v) + < SQUARE_PIX) + { + if (touchpad->arc_slider == NULL) + { + touchpad->arc_slider = create_arc(touchpad->diameter); + } + + if (circle_hit_test(touchpad, pos)) + { + int16_t diff = pos.x - touchpad->arc_indic_pos.x; + if (pos.y > LV_VER_RES / 2) + { + diff = -diff; + } + + lv_arc_set_value(touchpad->arc_slider, + lv_arc_get_value(touchpad->arc_slider) + diff); + touchpad->arc_indic_pos.x = pos.x; + touchpad->arc_indic_pos.y = pos.y; + } + } + + if (touchpad->arc_slider && + touchpad->diameter * 2 - + lv_arc_get_value(touchpad->arc_slider) < DISTANCE_PIX) + { + if (touchpad->label_pass == NULL) + { + static lv_style_t style; + lv_style_init(&style); + lv_style_set_bg_color(&style, + lv_palette_main(LV_PALETTE_GREEN)); + lv_obj_add_style(lv_scr_act(), &style, 0); + + touchpad->label_pass = lv_label_create(lv_scr_act()); + lv_obj_set_size(touchpad->label_pass, + LV_HOR_RES, LV_VER_RES); + lv_label_set_text(touchpad->label_pass, "PASS"); + lv_obj_set_style_text_align(touchpad->label_pass, + LV_TEXT_ALIGN_CENTER, 0); + lv_obj_center(touchpad->label_pass); + } + } + } + } + else if (touch_flags & TOUCH_UP) + { + if (touchpad->is_square == true) + { + touchpad->left_top = false; + touchpad->right_top = false; + touchpad->left_bottom = false; + touchpad->right_bottom = false; + } + } +} + +/**************************************************************************** + * Name: touchpad_init + ****************************************************************************/ + +bool touchpad_init(touchpad_s **touchpad, int screen_shape) +{ + touchpad_s *tmp_touchpad; + const char *device_path = CONFIG_LV_TOUCHPAD_INTERFACE_DEFAULT_DEVICEPATH; + + if (touchpad == NULL) + { + LV_LOG_ERROR("touchpad is NULL"); + return false; + } + + tmp_touchpad = malloc(sizeof(touchpad_s)); + if (tmp_touchpad == NULL) + { + LV_LOG_ERROR("touchpad_s malloc failed"); + return false; + } + + *touchpad = tmp_touchpad; + + tmp_touchpad->fd = open(device_path, O_RDONLY | O_NONBLOCK); + if (tmp_touchpad->fd < 0) + { + LV_LOG_ERROR("touchpad %s open failed: %d", + device_path, errno); + return false; + } + + tmp_touchpad->is_square = (bool)screen_shape; + + lv_indev_drv_init(&(tmp_touchpad->indev_drv)); + tmp_touchpad->indev_drv.type = LV_INDEV_TYPE_POINTER; + tmp_touchpad->indev_drv.read_cb = touchpad_read; + +#if (LV_USE_USER_DATA != 0) + tmp_touchpad->indev_drv.user_data = tmp_touchpad; +#else +#error LV_USE_USER_DATA must be enabled +#endif + tmp_touchpad->indev = lv_indev_drv_register(&(tmp_touchpad->indev_drv)); + if (tmp_touchpad->indev == NULL) + { + LV_LOG_ERROR("touchpad indev is NULL"); + return false; + } + + if (tmp_touchpad->is_square == true) + { + tmp_touchpad->canvas = lv_canvas_create(lv_scr_act()); + tmp_touchpad->left_top = false; + tmp_touchpad->right_top = false; + tmp_touchpad->left_bottom = false; + tmp_touchpad->right_bottom = false; + tmp_touchpad->canvas_buf = (lv_color_t *)malloc( + LV_CANVAS_BUF_SIZE_TRUE_COLOR(LV_HOR_RES, LV_VER_RES) * + sizeof(lv_color_t)); + if (tmp_touchpad->canvas_buf == NULL) + { + LV_LOG_ERROR("canvas_buf malloc failed"); + return false; + } + + lv_obj_set_size(tmp_touchpad->canvas, LV_HOR_RES, LV_VER_RES); + lv_canvas_set_buffer(tmp_touchpad->canvas, tmp_touchpad->canvas_buf, + LV_HOR_RES, LV_VER_RES, LV_IMG_CF_TRUE_COLOR); + lv_canvas_fill_bg(tmp_touchpad->canvas, + lv_palette_lighten(LV_PALETTE_GREY, 3), + LV_OPA_COVER); + lv_obj_center(tmp_touchpad->canvas); + darw_canvas_frame(tmp_touchpad); + } + else + { + /* Create and configure a horizontal slider */ + + tmp_touchpad->slider_h = create_slider(true); + tmp_touchpad->slider_v = NULL; + tmp_touchpad->arc_slider = NULL; + tmp_touchpad->label_pass = NULL; + tmp_touchpad->diameter = + LV_HOR_RES > LV_VER_RES ? LV_VER_RES : LV_HOR_RES; + tmp_touchpad->diameter = tmp_touchpad->diameter * 3 / 4; + tmp_touchpad->arc_indic_pos.x = + (LV_HOR_RES - tmp_touchpad->diameter) / 2; + tmp_touchpad->arc_indic_pos.y = LV_VER_RES / 2; + } + + return true; +} + +void touchpad_deinit(touchpad_s *touchpad) +{ + if (touchpad == NULL) + { + return; + } + + if (touchpad->canvas_buf) + { + free(touchpad->canvas_buf); + } + + if (touchpad->indev) + { + lv_indev_delete(touchpad->indev); + } + + if (touchpad->fd > 0) + { + close(touchpad->fd); + } + + free(touchpad); +} + +/**************************************************************************** + * Name: lvgl_init + ****************************************************************************/ + +void lvgl_init(void) +{ + lv_init(); +#if defined(CONFIG_LV_USE_SYSLOG_INTERFACE) + lv_syslog_interface_init(); +#endif +#if defined(CONFIG_LV_USE_LCDDEV_INTERFACE) + lv_lcddev_interface_init(NULL, 0); +#endif +#if defined(CONFIG_LV_USE_FBDEV_INTERFACE) + lv_fbdev_interface_init(NULL, 0); +#endif +} + +/**************************************************************************** + * Name: show_usage + ****************************************************************************/ + +static void show_usage(void) +{ + LV_LOG_USER("Usage: -s 0 -- circular screen\n"); + LV_LOG_USER("Usage: -s 1 -- square screen\n"); + exit(EXIT_FAILURE); +} + +/**************************************************************************** + * Name: touchpanel_main + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + touchpad_s *touchpad = NULL; + int ch; + int screen_shape = 0; + + while ((ch = getopt(argc, argv, "hs::")) != -1) + { + switch (ch) + { + case 'h': + show_usage(); + break; + case 's': + screen_shape = atoi(optarg); + break; + } + } + + if (screen_shape != 0 && screen_shape != 1) + { + show_usage(); + return 1; + } + + /* LVGL interface initialization */ + + lvgl_init(); + + if (!touchpad_init(&touchpad, screen_shape)) + { + LV_LOG_ERROR("touchpad_init init failed"); + goto errout; + } + + while (1) + { + lv_timer_handler(); + usleep(10 * 1000); + } + +errout: + touchpad_deinit(touchpad); + + LV_LOG_USER("Terminating!\n"); + return 0; +}