commit 53eed78aa837f412eeb73073ccce612d38d1cade Author: fancy <258828110.@qq.com> Date: Thu Oct 26 08:39:12 2023 -0700 Add lvgl simulator code. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ddd143a --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Ignore build folder +build/ + +# Ignore UI folder, this should be commited in its own repository +ui/ + +# Ignore all dot folders, with below exceptions +.*/ + +# VSCode +!.vscode/ +!.vscode/* diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..5044fd0 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "lv_examples"] + path = lv_examples + url = https://github.com/lvgl/lv_examples.git +[submodule "lv_drivers"] + path = lv_drivers + url = https://github.com/lvgl/lv_drivers.git +[submodule "lvgl"] + path = lvgl + url = https://github.com/lvgl/lvgl.git diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a4545b3 --- /dev/null +++ b/Makefile @@ -0,0 +1,58 @@ +# +# Makefile +# WARNING: relies on invocation setting current working directory to Makefile location +# This is done in .vscode/task.json +# +PROJECT ?= lvgl-sdl +MAKEFLAGS := -j $(shell nproc) +SRC_EXT := c +OBJ_EXT := o +CC ?= gcc + +SRC_DIR := ./ +WORKING_DIR := ./build +BUILD_DIR := $(WORKING_DIR)/obj +BIN_DIR := $(WORKING_DIR)/bin +UI_DIR := ui + +WARNINGS := -Wall -Wextra \ + -Wshadow -Wundef -Wmaybe-uninitialized -Wmissing-prototypes -Wno-discarded-qualifiers \ + -Wno-unused-function -Wno-error=strict-prototypes -Wpointer-arith -fno-strict-aliasing -Wno-error=cpp -Wuninitialized \ + -Wno-unused-parameter -Wno-missing-field-initializers -Wno-format-nonliteral -Wno-cast-qual -Wunreachable-code -Wno-switch-default \ + -Wreturn-type -Wmultichar -Wformat-security -Wno-ignored-qualifiers -Wno-error=pedantic -Wno-sign-compare -Wno-error=missing-prototypes -Wdouble-promotion -Wclobbered -Wdeprecated \ + -Wempty-body -Wshift-negative-value -Wstack-usage=2048 \ + -Wtype-limits -Wsizeof-pointer-memaccess -Wpointer-arith + +CFLAGS := -O0 -g $(WARNINGS) + +# Add simulator define to allow modification of source +DEFINES := -D SIMULATOR=1 -D LV_BUILD_TEST=0 + +# Include simulator inc folder first so lv_conf.h from custom UI can be used instead +INC := -I./ui/simulator/inc/ -I./ -I./lvgl/ +LDLIBS := -lSDL2 -lm +BIN := $(BIN_DIR)/demo + +COMPILE = $(CC) $(CFLAGS) $(INC) $(DEFINES) + +# Automatically include all source files +SRCS := $(shell find $(SRC_DIR) -type f -name '*.c' -not -path '*/\.*') +OBJECTS := $(patsubst $(SRC_DIR)%,$(BUILD_DIR)/%,$(SRCS:.$(SRC_EXT)=.$(OBJ_EXT))) + +all: default + +$(BUILD_DIR)/%.$(OBJ_EXT): $(SRC_DIR)/%.$(SRC_EXT) + @echo 'Building project file: $<' + @mkdir -p $(dir $@) + @$(COMPILE) -c -o "$@" "$<" + +default: $(OBJECTS) + @mkdir -p $(BIN_DIR) + $(CC) -o $(BIN) $(OBJECTS) $(LDFLAGS) ${LDLIBS} + +clean: + rm -rf $(WORKING_DIR) + +install: ${BIN} + install -d ${DESTDIR}/usr/lib/${PROJECT}/bin + install $< ${DESTDIR}/usr/lib/${PROJECT}/bin/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..b6cc791 --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# Simulator project for LVGL embedded GUI Library + +The [LVGL](https://github.com/lvgl/lvgl) is written mainly for microcontrollers and embedded systems however you can run the library **on your PC** as well without any embedded hardware. The code written on PC can be simply copied when your are using an embedded system. + +Using a PC simulator instead of an embedded hardware has several advantages: +* **Costs $0** because you you don't have to buy or design PCB +* **Fast** because you don't have to design and manufacture PCB +* **Collaborative** because any number of developers can work in the same environment +* **Developer friendly** because much easier and faster to debug on PC + +## Requirements +This project is configured for VSCode and only tested on Linux, although this may work on OSx or WSL. It requires a working version of GCC, GDB and make in your path. + +To allow debugging inside VSCode you will also require a GDB [extension](https://marketplace.visualstudio.com/items?itemName=webfreak.debug) or other suitable debugger. + +* **SDL** a low level driver library to use graphics, handle mouse, keyboard etc. + +## Usage + +### Get the PC project + +Clone the PC project and the related sub modules: + +``` +git clone --recursive https://github.com/lvgl/lv_sim_vscode_sdl +``` + +### Install SDL +You can download SDL from https://www.libsdl.org/ + +On on Linux you can install it via terminal: +``` +sudo apt-get update && sudo apt-get install -y build-essential libsdl2-dev +``` + +### Setup +To allow custom UI code an `lv_conf.h` file placed at `ui/simulator/inc` will automatically override this projects lv_conf.h file. By default code under `ui` is ignored so you can reuse this repository for multiple projects. You will need to place a call from `main.c` to your UI's entry function. + +To build and debug, press F5. You should now have your UI displayed in a new window and can access all the debug features of VSCode through GDB. + +To allow temporary modification between simulator and device code, a SIMULATOR=1 define is added globally. diff --git a/licence.txt b/licence.txt new file mode 100644 index 0000000..beaef1d --- /dev/null +++ b/licence.txt @@ -0,0 +1,8 @@ +MIT licence +Copyright (c) 2016 Gábor Kiss-Vámosi + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/lv_conf.h b/lv_conf.h new file mode 100644 index 0000000..4d00965 --- /dev/null +++ b/lv_conf.h @@ -0,0 +1,523 @@ +/** + * @file lv_conf.h + * Configuration file for v8.0.0 + */ + +/* + * COPY THIS FILE AS `lv_conf.h` NEXT TO the `lvgl` FOLDER + */ + +#if 1 /*Set it to "1" to enable content*/ + +#ifndef LV_CONF_H +#define LV_CONF_H +/*clang-format off*/ + +#include + +// ================== Add by xiao ================== start +/*BMP decoder library*/ +#define LV_USE_BMP 1 +/*API for open, read, etc*/ +#define LV_USE_FS_POSIX 1 +#if LV_USE_FS_POSIX + #define LV_FS_POSIX_LETTER 'A' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #define LV_FS_POSIX_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ + #define LV_FS_POSIX_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ +#endif +// ================== Add by xiao ================== end + +/*==================== + COLOR SETTINGS + *====================*/ + +/*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/ +#define LV_COLOR_DEPTH 32 + +/*Swap the 2 bytes of RGB565 color. Useful if the display has a 8 bit interface (e.g. SPI)*/ +#define LV_COLOR_16_SWAP 0 + +/*Enable more complex drawing routines to manage screens transparency. + *Can be used if the UI is above an other layer, e.g. an OSD menu or video player. + *Requires `LV_COLOR_DEPTH = 32` colors and the screen's `bg_opa` should be set to non LV_OPA_COVER value*/ +#define LV_COLOR_SCREEN_TRANSP 0 + +/*Images pixels with this color will not be drawn if they are chroma keyed)*/ +#define LV_COLOR_CHROMA_KEY lv_color_hex(0x00ff00) /*pure green*/ + +/*========================= + MEMORY SETTINGS + *=========================*/ + +/*1: use custom malloc/free, 0: use the built-in `lv_mem_alloc()` and `lv_mem_free()`*/ +#define LV_MEM_CUSTOM 0 +#if LV_MEM_CUSTOM == 0 +/*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/ +# define LV_MEM_SIZE (1024U * 1024U) /*[bytes]*/ + +/*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/ +# define LV_MEM_ADR 0 /*0: unused*/ +#else /*LV_MEM_CUSTOM*/ +# define LV_MEM_CUSTOM_INCLUDE /*Header for the dynamic memory function*/ +# define LV_MEM_CUSTOM_ALLOC malloc +# define LV_MEM_CUSTOM_FREE free +# define LV_MEM_CUSTOM_REALLOC realloc +#endif /*LV_MEM_CUSTOM*/ + +/*Use the standard `memcpy` and `memset` instead of LVGL's own functions. (Might or might not be faster).*/ +#define LV_MEMCPY_MEMSET_STD 0 + +/*==================== + HAL SETTINGS + *====================*/ + +/*Default display refresh period. LVG will redraw changed ares with this period time*/ +#define LV_DISP_DEF_REFR_PERIOD 30 /*[ms]*/ + +/*Input device read period in milliseconds*/ +#define LV_INDEV_DEF_READ_PERIOD 30 /*[ms]*/ + +/*Use a custom tick source that tells the elapsed time in milliseconds. + *It removes the need to manually update the tick with `lv_tick_inc()`)*/ +#define LV_TICK_CUSTOM 0 +#if LV_TICK_CUSTOM +#define LV_TICK_CUSTOM_INCLUDE "Arduino.h" /*Header for the system time function*/ +#define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis()) /*Expression evaluating to current system time in ms*/ +#endif /*LV_TICK_CUSTOM*/ + +/*Default Dot Per Inch. Used to initialize default sizes such as widgets sized, style paddings. + *(Not so important, you can adjust it to modify default sizes and spaces)*/ +#define LV_DPI_DEF 130 /*[px/inch]*/ + +/*======================= + * FEATURE CONFIGURATION + *=======================*/ + +/*------------- + * Drawing + *-----------*/ + +/*Enable complex draw engine. + *Required to draw shadow, gradient, rounded corners, circles, arc, skew lines, image transformations or any masks*/ +#define LV_DRAW_COMPLEX 1 +#if LV_DRAW_COMPLEX != 0 + +/*Allow buffering some shadow calculation. + *LV_SHADOW_CACHE_SIZE is the max. shadow size to buffer, where shadow size is `shadow_width + radius` + *Caching has LV_SHADOW_CACHE_SIZE^2 RAM cost*/ +#define LV_SHADOW_CACHE_SIZE 0 +#endif /*LV_DRAW_COMPLEX*/ + +/*Default image cache size. Image caching keeps the images opened. + *If only the built-in image formats are used there is no real advantage of caching. (I.e. if no new image decoder is added) + *With complex image decoders (e.g. PNG or JPG) caching can save the continuous open/decode of images. + *However the opened images might consume additional RAM. + *0: to disable caching*/ +#define LV_IMG_CACHE_DEF_SIZE 0 + +/*Maximum buffer size to allocate for rotation. Only used if software rotation is enabled in the display driver.*/ +#define LV_DISP_ROT_MAX_BUF (10*1024) +/*------------- + * GPU + *-----------*/ + +/*Use STM32's DMA2D (aka Chrom Art) GPU*/ +#define LV_USE_GPU_STM32_DMA2D 0 +#if LV_USE_GPU_STM32_DMA2D +/*Must be defined to include path of CMSIS header of target processor +e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ +#define LV_GPU_DMA2D_CMSIS_INCLUDE +#endif + +/*Use NXP's PXP GPU iMX RTxxx platforms*/ +#define LV_USE_GPU_NXP_PXP 0 +#if LV_USE_GPU_NXP_PXP +/*1: Add default bare metal and FreeRTOS interrupt handling routines for PXP (lv_gpu_nxp_pxp_osa.c) + * and call lv_gpu_nxp_pxp_init() automatically during lv_init(). Note that symbol FSL_RTOS_FREE_RTOS + * has to be defined in order to use FreeRTOS OSA, otherwise bare-metal implementation is selected. + *0: lv_gpu_nxp_pxp_init() has to be called manually before lv_init() + */ +#define LV_USE_GPU_NXP_PXP_AUTO_INIT 0 +#endif + +/*Use NXP's VG-Lite GPU iMX RTxxx platforms*/ +#define LV_USE_GPU_NXP_VG_LITE 0 + +/*------------- + * Logging + *-----------*/ + +/*Enable the log module*/ +#define LV_USE_LOG 1 +#if LV_USE_LOG + +/*How important log should be added: + *LV_LOG_LEVEL_TRACE A lot of logs to give detailed information + *LV_LOG_LEVEL_INFO Log important events + *LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't cause a problem + *LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail + *LV_LOG_LEVEL_USER Only logs added by the user + *LV_LOG_LEVEL_NONE Do not log anything*/ +# define LV_LOG_LEVEL LV_LOG_LEVEL_WARN + +/*1: Print the log with 'printf'; + *0: User need to register a callback with `lv_log_register_print_cb()`*/ +# define LV_LOG_PRINTF 1 + +/*Enable/disable LV_LOG_TRACE in modules that produces a huge number of logs*/ +# define LV_LOG_TRACE_MEM 1 +# define LV_LOG_TRACE_TIMER 1 +# define LV_LOG_TRACE_INDEV 1 +# define LV_LOG_TRACE_DISP_REFR 1 +# define LV_LOG_TRACE_EVENT 1 +# define LV_LOG_TRACE_OBJ_CREATE 1 +# define LV_LOG_TRACE_LAYOUT 1 +# define LV_LOG_TRACE_ANIM 1 + +#endif /*LV_USE_LOG*/ + +/*------------- + * Asserts + *-----------*/ + +/*Enable asserts if an operation is failed or an invalid data is found. + *If LV_USE_LOG is enabled an error message will be printed on failure*/ +#define LV_USE_ASSERT_NULL 1 /*Check if the parameter is NULL. (Very fast, recommended)*/ +#define LV_USE_ASSERT_MALLOC 1 /*Checks is the memory is successfully allocated or no. (Very fast, recommended)*/ +#define LV_USE_ASSERT_STYLE 1 /*Check if the styles are properly initialized. (Very fast, recommended)*/ +#define LV_USE_ASSERT_MEM_INTEGRITY 1 /*Check the integrity of `lv_mem` after critical operations. (Slow)*/ +#define LV_USE_ASSERT_OBJ 1 /*Check the object's type and existence (e.g. not deleted). (Slow)*/ + +/*Add a custom handler when assert happens e.g. to restart the MCU*/ +#define LV_ASSERT_HANDLER_INCLUDE +#define LV_ASSERT_HANDLER while(1); /*Halt by default*/ + +/*------------- + * Others + *-----------*/ + +/*1: Show CPU usage and FPS count in the right bottom corner*/ +#define LV_USE_PERF_MONITOR 1 + +/*1: Show the used memory and the memory fragmentation in the left bottom corner + * Requires LV_MEM_CUSTOM = 0*/ +#define LV_USE_MEM_MONITOR 1 + +/*1: Draw random colored rectangles over the redrawn areas*/ +#define LV_USE_REFR_DEBUG 0 + +/*Change the built in (v)snprintf functions*/ +#define LV_SPRINTF_CUSTOM 0 +#if LV_SPRINTF_CUSTOM +# define LV_SPRINTF_INCLUDE +# define lv_snprintf snprintf +# define lv_vsnprintf vsnprintf +#else /*LV_SPRINTF_CUSTOM*/ +# define LV_SPRINTF_USE_FLOAT 0 +#endif /*LV_SPRINTF_CUSTOM*/ + +#define LV_USE_USER_DATA 1 + +/*Garbage Collector settings + *Used if lvgl is binded to higher level language and the memory is managed by that language*/ +#define LV_ENABLE_GC 0 +#if LV_ENABLE_GC != 0 +# define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/ +#endif /*LV_ENABLE_GC*/ + +/*===================== + * COMPILER SETTINGS + *====================*/ + +/*For big endian systems set to 1*/ +#define LV_BIG_ENDIAN_SYSTEM 0 + +/*Define a custom attribute to `lv_tick_inc` function*/ +#define LV_ATTRIBUTE_TICK_INC + +/*Define a custom attribute to `lv_timer_handler` function*/ +#define LV_ATTRIBUTE_TIMER_HANDLER + +/*Define a custom attribute to `lv_disp_flush_ready` function*/ +#define LV_ATTRIBUTE_FLUSH_READY + +/*Required alignment size for buffers*/ +#define LV_ATTRIBUTE_MEM_ALIGN_SIZE + +/*Will be added where memories needs to be aligned (with -Os data might not be aligned to boundary by default). + * E.g. __attribute__((aligned(4)))*/ +#define LV_ATTRIBUTE_MEM_ALIGN + +/*Attribute to mark large constant arrays for example font's bitmaps*/ +#define LV_ATTRIBUTE_LARGE_CONST + +/*Complier prefix for a big array declaration in RAM*/ +#define LV_ATTRIBUTE_LARGE_RAM_ARRAY + +/*Place performance critical functions into a faster memory (e.g RAM)*/ +#define LV_ATTRIBUTE_FAST_MEM + +/*Prefix variables that are used in GPU accelerated operations, often these need to be placed in RAM sections that are DMA accessible*/ +#define LV_ATTRIBUTE_DMA + +/*Export integer constant to binding. This macro is used with constants in the form of LV_ that + *should also appear on LVGL binding API such as Micropython.*/ +#define LV_EXPORT_CONST_INT(int_value) struct _silence_gcc_warning /*The default value just prevents GCC warning*/ + +/*Extend the default -32k..32k coordinate range to -4M..4M by using int32_t for coordinates instead of int16_t*/ +#define LV_USE_LARGE_COORD 0 + +/*================== + * FONT USAGE + *===================*/ + +/*Montserrat fonts with ASCII range and some symbols using bpp = 4 + *https://fonts.google.com/specimen/Montserrat*/ +#define LV_FONT_MONTSERRAT_8 1 +#define LV_FONT_MONTSERRAT_10 1 +#define LV_FONT_MONTSERRAT_12 1 +#define LV_FONT_MONTSERRAT_14 1 +#define LV_FONT_MONTSERRAT_16 1 +#define LV_FONT_MONTSERRAT_18 1 +#define LV_FONT_MONTSERRAT_20 1 +#define LV_FONT_MONTSERRAT_22 1 +#define LV_FONT_MONTSERRAT_24 1 +#define LV_FONT_MONTSERRAT_26 1 +#define LV_FONT_MONTSERRAT_28 1 +#define LV_FONT_MONTSERRAT_30 1 +#define LV_FONT_MONTSERRAT_32 1 +#define LV_FONT_MONTSERRAT_34 1 +#define LV_FONT_MONTSERRAT_36 1 +#define LV_FONT_MONTSERRAT_38 1 +#define LV_FONT_MONTSERRAT_40 1 +#define LV_FONT_MONTSERRAT_42 1 +#define LV_FONT_MONTSERRAT_44 1 +#define LV_FONT_MONTSERRAT_46 1 +#define LV_FONT_MONTSERRAT_48 1 + +/*Demonstrate special features*/ +#define LV_FONT_MONTSERRAT_12_SUBPX 1 +#define LV_FONT_MONTSERRAT_28_COMPRESSED 1 /*bpp = 3*/ +#define LV_FONT_DEJAVU_16_PERSIAN_HEBREW 1 /*Hebrew, Arabic, Perisan letters and all their forms*/ +#define LV_FONT_SIMSUN_16_CJK 1 /*1000 most common CJK radicals*/ + +/*Pixel perfect monospace fonts*/ +#define LV_FONT_UNSCII_8 1 +#define LV_FONT_UNSCII_16 1 + +/*Optionally declare custom fonts here. + *You can use these fonts as default font too and they will be available globally. + *E.g. #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) LV_FONT_DECLARE(my_font_2)*/ +#define LV_FONT_CUSTOM_DECLARE + +/*Always set a default font*/ +#define LV_FONT_DEFAULT &lv_font_montserrat_14 + +/*Enable handling large font and/or fonts with a lot of characters. + *The limit depends on the font size, font face and bpp. + *Compiler error will be triggered if a font needs it.*/ +#define LV_FONT_FMT_TXT_LARGE 1 + +/*Enables/disables support for compressed fonts.*/ +#define LV_USE_FONT_COMPRESSED 1 + +/*Enable subpixel rendering*/ +#define LV_USE_FONT_SUBPX 1 +#if LV_USE_FONT_SUBPX +/*Set the pixel order of the display. Physical order of RGB channels. Doesn't matter with "normal" fonts.*/ +#define LV_FONT_SUBPX_BGR 0 /*0: RGB; 1:BGR order*/ +#endif + +/*================= + * TEXT SETTINGS + *=================*/ + +/** + * Select a character encoding for strings. + * Your IDE or editor should have the same character encoding + * - LV_TXT_ENC_UTF8 + * - LV_TXT_ENC_ASCII + */ +#define LV_TXT_ENC LV_TXT_ENC_UTF8 + + /*Can break (wrap) texts on these chars*/ +#define LV_TXT_BREAK_CHARS " ,.;:-_" + +/*If a word is at least this long, will break wherever "prettiest" + *To disable, set to a value <= 0*/ +#define LV_TXT_LINE_BREAK_LONG_LEN 0 + +/*Minimum number of characters in a long word to put on a line before a break. + *Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/ +#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3 + +/*Minimum number of characters in a long word to put on a line after a break. + *Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/ +#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3 + +/*The control character to use for signalling text recoloring.*/ +#define LV_TXT_COLOR_CMD "#" + +/*Support bidirectional texts. Allows mixing Left-to-Right and Right-to-Left texts. + *The direction will be processed according to the Unicode Bidirectioanl Algorithm: + *https://www.w3.org/International/articles/inline-bidi-markup/uba-basics*/ +#define LV_USE_BIDI 1 +#if LV_USE_BIDI +/*Set the default direction. Supported values: + *`LV_BASE_DIR_LTR` Left-to-Right + *`LV_BASE_DIR_RTL` Right-to-Left + *`LV_BASE_DIR_AUTO` detect texts base direction*/ +#define LV_BIDI_BASE_DIR_DEF LV_BASE_DIR_AUTO +#endif + +/*Enable Arabic/Persian processing + *In these languages characters should be replaced with an other form based on their position in the text*/ +#define LV_USE_ARABIC_PERSIAN_CHARS 1 + +/*================== + * WIDGET USAGE + *================*/ + +/*Documentation of the widgets: https://docs.lvgl.io/latest/en/html/widgets/index.html*/ + +#define LV_USE_ARC 1 + +#define LV_USE_ANIMIMG 1 + +#define LV_USE_BAR 1 + +#define LV_USE_BTN 1 + +#define LV_USE_BTNMATRIX 1 + +#define LV_USE_CANVAS 1 + +#define LV_USE_CHECKBOX 1 + + +#define LV_USE_DROPDOWN 1 /*Requires: lv_label*/ + +#define LV_USE_IMG 1 /*Requires: lv_label*/ + +#define LV_USE_LABEL 1 +#if LV_USE_LABEL +# define LV_LABEL_TEXT_SELECTION 1 /*Enable selecting text of the label*/ +# define LV_LABEL_LONG_TXT_HINT 1 /*Store some extra info in labels to speed up drawing of very long texts*/ +#endif + +#define LV_USE_LINE 1 + +#define LV_USE_ROLLER 1 /*Requires: lv_label*/ +#if LV_USE_ROLLER +# define LV_ROLLER_INF_PAGES 7 /*Number of extra "pages" when the roller is infinite*/ +#endif + +#define LV_USE_SLIDER 1 /*Requires: lv_bar*/ + +#define LV_USE_SWITCH 1 + +#define LV_USE_TEXTAREA 1 /*Requires: lv_label*/ +#if LV_USE_TEXTAREA != 0 +# define LV_TEXTAREA_DEF_PWD_SHOW_TIME 1500 /*ms*/ +#endif + +#define LV_USE_TABLE 1 + +/*================== + * EXTRA COMPONENTS + *==================*/ + +/*----------- + * Widgets + *----------*/ +#define LV_USE_CALENDAR 1 +#if LV_USE_CALENDAR +# define LV_CALENDAR_WEEK_STARTS_MONDAY 0 +# if LV_CALENDAR_WEEK_STARTS_MONDAY +# define LV_CALENDAR_DEFAULT_DAY_NAMES {"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"} +# else +# define LV_CALENDAR_DEFAULT_DAY_NAMES {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"} +# endif + +# define LV_CALENDAR_DEFAULT_MONTH_NAMES {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"} +# define LV_USE_CALENDAR_HEADER_ARROW 1 +# define LV_USE_CALENDAR_HEADER_DROPDOWN 1 +#endif /*LV_USE_CALENDAR*/ + +#define LV_USE_CHART 1 + +#define LV_USE_COLORWHEEL 1 + +#define LV_USE_IMGBTN 1 + +#define LV_USE_KEYBOARD 1 + +#define LV_USE_LED 1 + +#define LV_USE_LIST 1 + +#define LV_USE_METER 1 + +#define LV_USE_MSGBOX 1 + +#define LV_USE_SPINBOX 1 + +#define LV_USE_SPINNER 1 + +#define LV_USE_TABVIEW 1 + +#define LV_USE_TILEVIEW 1 + +#define LV_USE_WIN 1 + +#define LV_USE_SPAN 1 +#if LV_USE_SPAN +/*A line text can contain maximum num of span descriptor */ +# define LV_SPAN_SNIPPET_STACK_SIZE 64 +#endif + +/*----------- + * Themes + *----------*/ +/*A simple, impressive and very complete theme*/ +#define LV_USE_THEME_DEFAULT 1 +#if LV_USE_THEME_DEFAULT + +/*0: Light mode; 1: Dark mode*/ +# define LV_THEME_DEFAULT_DARK 0 + +/*1: Enable grow on press*/ +# define LV_THEME_DEFAULT_GROW 1 + +/*Default transition time in [ms]*/ +# define LV_THEME_DEFAULT_TRANSITON_TIME 80 +#endif /*LV_USE_THEME_DEFAULT*/ + +/*An very simple them that is a good starting point for a custom theme*/ + #define LV_USE_THEME_BASIC 1 + +/*A theme designed for monochrome displays*/ +#define LV_USE_THEME_MONO 1 + +/*----------- + * Layouts + *----------*/ + +/*A layout similar to Flexbox in CSS.*/ +#define LV_USE_FLEX 1 + +/*A layout similar to Grid in CSS.*/ +#define LV_USE_GRID 1 + +/*================== +* EXAMPLES +*==================*/ + +/*Enable the examples to be built with the library*/ +#define LV_BUILD_EXAMPLES 1 + +/*--END OF LV_CONF_H--*/ + +#endif /*LV_CONF_H*/ + +#endif /*End of "Content enable"*/ diff --git a/lv_demo_conf.h b/lv_demo_conf.h new file mode 100644 index 0000000..cf6dcbf --- /dev/null +++ b/lv_demo_conf.h @@ -0,0 +1,56 @@ +/** + * @file lv_demo_conf.h + * Configuration file for v8.0.0 + * + */ +/* + * COPY THIS FILE AS lv_demo_conf.h + */ + +#if 1 /*Set it to "1" to enable the content*/ + +#ifndef LV_EX_CONF_H +#define LV_EX_CONF_H + + +/******************* + * GENERAL SETTING + *******************/ +#define LV_EX_PRINTF 1 /*Enable printf-ing data in demoes and examples*/ +#define LV_EX_KEYBOARD 1 /*Add PC keyboard support to some examples (`lv_drivers` repository is required)*/ +#define LV_EX_MOUSEWHEEL 1 /*Add 'encoder' (mouse wheel) support to some examples (`lv_drivers` repository is required)*/ + +/********************* + * DEMO USAGE + *********************/ + +/*Show some widget*/ +#define LV_USE_DEMO_WIDGETS 1 +#if LV_USE_DEMO_WIDGETS +#define LV_DEMO_WIDGETS_SLIDESHOW 0 +#endif + +/*Printer demo, optimized for 800x480*/ +#define LV_USE_DEMO_PRINTER 0 + +/*Demonstrate the usage of encoder and keyboard*/ +#define LV_USE_DEMO_KEYPAD_AND_ENCODER 1 + +/*Benchmark your system*/ +#define LV_USE_DEMO_BENCHMARK 1 + +/*Stress test for LVGL*/ +#define LV_USE_DEMO_STRESS 1 + +/*Music player demo*/ +#define LV_USE_DEMO_MUSIC 1 +#if LV_USE_DEMO_MUSIC +# define LV_DEMO_MUSIC_LANDSCAPE 0 +# define LV_DEMO_MUSIC_LARGE 0 +#define LV_DEMO_MUSIC_AUTO_PLAY 0 +#endif + +#endif /*LV_EX_CONF_H*/ + +#endif /*End of "Content enable"*/ + diff --git a/lv_drivers/.gitignore b/lv_drivers/.gitignore new file mode 100644 index 0000000..fd73aaa --- /dev/null +++ b/lv_drivers/.gitignore @@ -0,0 +1,2 @@ +**/*.o +**/*.d \ No newline at end of file diff --git a/lv_drivers/CMakeLists.txt b/lv_drivers/CMakeLists.txt new file mode 100644 index 0000000..7135a41 --- /dev/null +++ b/lv_drivers/CMakeLists.txt @@ -0,0 +1,2 @@ +file(GLOB_RECURSE SOURCES ./*.c) +add_library(lv_drivers STATIC ${SOURCES}) diff --git a/lv_drivers/LICENSE b/lv_drivers/LICENSE new file mode 100644 index 0000000..cc227ab --- /dev/null +++ b/lv_drivers/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 LittlevGL + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lv_drivers/README.md b/lv_drivers/README.md new file mode 100644 index 0000000..d67d3c1 --- /dev/null +++ b/lv_drivers/README.md @@ -0,0 +1,7 @@ +# Display and Touch pad drivers + +Display controller and touchpad driver to can be directly used with [LittlevGL](https://littlevgl.com). + +To learn more about using drivers in LittlevGL visit the [Porting guide](https://docs.lvgl.io/latest/en/html/porting/index.html). + +If you used a new display or touch pad driver with LittlevGL please share it with other people! diff --git a/lv_drivers/display/GC9A01.c b/lv_drivers/display/GC9A01.c new file mode 100644 index 0000000..3f0df64 --- /dev/null +++ b/lv_drivers/display/GC9A01.c @@ -0,0 +1,579 @@ +/** + * @file GC9A01.c + * + **/ + + +/********************* + * INCLUDES + *********************/ +#include "GC9A01.h" +#if USE_GC9A01 + +#include +#include +#include + +#include LV_DRV_DISP_INCLUDE +#include LV_DRV_DELAY_INCLUDE + +/********************* + * DEFINES + *********************/ +#define GC9A01_BAUD 2000000 /*< 2,5 MHz (400 ns)*/ + +#define GC9A01_CMD_MODE 0 +#define GC9A01_DATA_MODE 1 + +#define GC9A01_HOR_RES 240 +#define GC9A01_VER_RES 240 + +/* GC9A01 Commands that we know of. Limited documentation */ +#define GC9A01_INVOFF 0x20 +#define GC9A01_INVON 0x21 +#define GC9A01_DISPON 0x29 +#define GC9A01_CASET 0x2A +#define GC9A01_RASET 0x2B +#define GC9A01_RAMWR 0x2C +#define GC9A01_COLMOD 0x3A +#define GC9A01_MADCTL 0x36 +#define GC9A01_MADCTL_MY 0x80 +#define GC9A01_MADCTL_MX 0x40 +#define GC9A01_MADCTL_MV 0x20 +#define GC9A01_MADCTL_RGB 0x00 +#define GC9A01_DISFNCTRL 0xB6 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void GC9A01_command(uint8_t cmd); +static void GC9A01_data(uint8_t data); +static void GC9A01_set_addr_win(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1); + +/********************** + * STATIC VARIABLES + **********************/ +// Documentation on op codes for GC9A01 are very hard to find. +// Will document should they be found. +static struct GC9A01_function GC9A01_cfg_script[] = { + { GC9A01_START, GC9A01_START}, + { GC9A01_CMD, 0xEF}, + + { GC9A01_CMD, 0xEB}, + { GC9A01_DATA, 0x14}, + + { GC9A01_CMD, 0xFE}, // Inter Register Enable1 + { GC9A01_CMD, 0xEF}, // Inter Register Enable2 + + { GC9A01_CMD, 0xEB}, + { GC9A01_DATA, 0x14}, + + { GC9A01_CMD, 0x84}, + { GC9A01_DATA, 0x40}, + + { GC9A01_CMD, 0x85}, + { GC9A01_DATA, 0xFF}, + + { GC9A01_CMD, 0x86}, + { GC9A01_DATA, 0xFF}, + + { GC9A01_CMD, 0x87}, + { GC9A01_DATA, 0xFF}, + + { GC9A01_CMD, 0x88}, + { GC9A01_DATA, 0x0A}, + + { GC9A01_CMD, 0x89}, + { GC9A01_DATA, 0x21}, + + { GC9A01_CMD, 0x8A}, + { GC9A01_DATA, 0x00}, + + { GC9A01_CMD, 0x8B}, + { GC9A01_DATA, 0x80}, + + { GC9A01_CMD, 0x8C}, + { GC9A01_DATA, 0x01}, + + { GC9A01_CMD, 0x8D}, + { GC9A01_DATA, 0x01}, + + { GC9A01_CMD, 0x8E}, + { GC9A01_DATA, 0xFF}, + + { GC9A01_CMD, 0x8F}, + { GC9A01_DATA, 0xFF}, + + { GC9A01_CMD, GC9A01_DISFNCTRL}, // Display Function Control + { GC9A01_DATA, 0x00}, + { GC9A01_DATA, 0x00}, + + { GC9A01_CMD, GC9A01_MADCTL}, // Memory Access Control + { GC9A01_DATA, 0x48}, // Set the display direction 0,1,2,3 four directions + + { GC9A01_CMD, GC9A01_COLMOD}, // COLMOD: Pixel Format Set + { GC9A01_DATA, 0x05}, // 16 Bits per pixel + + { GC9A01_CMD, 0x90}, + { GC9A01_DATA, 0x08}, + { GC9A01_DATA, 0x08}, + { GC9A01_DATA, 0x08}, + { GC9A01_DATA, 0x08}, + + { GC9A01_CMD, 0xBD}, + { GC9A01_DATA, 0x06}, + + { GC9A01_CMD, 0xBC}, + { GC9A01_DATA, 0x00}, + + { GC9A01_CMD, 0xFF}, + { GC9A01_DATA, 0x60}, + { GC9A01_DATA, 0x01}, + { GC9A01_DATA, 0x04}, + + { GC9A01_CMD, 0xC3}, // Power Control 2 + { GC9A01_DATA, 0x13}, + { GC9A01_CMD, 0xC4}, // Power Control 3 + { GC9A01_DATA, 0x13}, + + { GC9A01_CMD, 0xC9}, // Power Control 4 + { GC9A01_DATA, 0x22}, + + { GC9A01_CMD, 0xBE}, + { GC9A01_DATA, 0x11}, + + { GC9A01_CMD, 0xE1}, + { GC9A01_DATA, 0x10}, + { GC9A01_DATA, 0x0E}, + + { GC9A01_CMD, 0xDF}, + { GC9A01_DATA, 0x21}, + { GC9A01_DATA, 0x0C}, + { GC9A01_DATA, 0x02}, + + { GC9A01_CMD, 0xF0}, // SET_GAMMA1 + { GC9A01_DATA, 0x45}, + { GC9A01_DATA, 0x09}, + { GC9A01_DATA, 0x08}, + { GC9A01_DATA, 0x08}, + { GC9A01_DATA, 0x26}, + { GC9A01_DATA, 0x2A}, + + { GC9A01_CMD, 0xF1}, // SET_GAMMA2 + { GC9A01_DATA, 0x43}, + { GC9A01_DATA, 0x70}, + { GC9A01_DATA, 0x72}, + { GC9A01_DATA, 0x36}, + { GC9A01_DATA, 0x37}, + { GC9A01_DATA, 0x6F}, + + { GC9A01_CMD, 0xF2}, // SET_GAMMA3 + { GC9A01_DATA, 0x45}, + { GC9A01_DATA, 0x09}, + { GC9A01_DATA, 0x08}, + { GC9A01_DATA, 0x08}, + { GC9A01_DATA, 0x26}, + { GC9A01_DATA, 0x2A}, + + { GC9A01_CMD, 0xF3}, // SET_GAMMA4 + { GC9A01_DATA, 0x43}, + { GC9A01_DATA, 0x70}, + { GC9A01_DATA, 0x72}, + { GC9A01_DATA, 0x36}, + { GC9A01_DATA, 0x37}, + { GC9A01_DATA, 0x6F}, + + { GC9A01_CMD, 0xED}, + { GC9A01_DATA, 0x1B}, + { GC9A01_DATA, 0x0B}, + + { GC9A01_CMD, 0xAE}, + { GC9A01_DATA, 0x77}, + + { GC9A01_CMD, 0xCD}, + { GC9A01_DATA, 0x63}, + + { GC9A01_CMD, 0x70}, + { GC9A01_DATA, 0x07}, + { GC9A01_DATA, 0x07}, + { GC9A01_DATA, 0x04}, + { GC9A01_DATA, 0x0E}, + { GC9A01_DATA, 0x0F}, + { GC9A01_DATA, 0x09}, + { GC9A01_DATA, 0x07}, + { GC9A01_DATA, 0x08}, + { GC9A01_DATA, 0x03}, + + { GC9A01_CMD, 0xE8}, + { GC9A01_DATA, 0x34}, + + { GC9A01_CMD, 0x62}, + { GC9A01_DATA, 0x18}, + { GC9A01_DATA, 0x0D}, + { GC9A01_DATA, 0x71}, + { GC9A01_DATA, 0xED}, + { GC9A01_DATA, 0x70}, + { GC9A01_DATA, 0x70}, + { GC9A01_DATA, 0x18}, + { GC9A01_DATA, 0x0F}, + { GC9A01_DATA, 0x71}, + { GC9A01_DATA, 0xEF}, + { GC9A01_DATA, 0x70}, + { GC9A01_DATA, 0x70}, + + { GC9A01_CMD, 0x63}, + { GC9A01_DATA, 0x18}, + { GC9A01_DATA, 0x11}, + { GC9A01_DATA, 0x71}, + { GC9A01_DATA, 0xF1}, + { GC9A01_DATA, 0x70}, + { GC9A01_DATA, 0x70}, + { GC9A01_DATA, 0x18}, + { GC9A01_DATA, 0x13}, + { GC9A01_DATA, 0x71}, + { GC9A01_DATA, 0xF3}, + { GC9A01_DATA, 0x70}, + { GC9A01_DATA, 0x70}, + + { GC9A01_CMD, 0x64}, + { GC9A01_DATA, 0x28}, + { GC9A01_DATA, 0x29}, + { GC9A01_DATA, 0xF1}, + { GC9A01_DATA, 0x01}, + { GC9A01_DATA, 0xF1}, + { GC9A01_DATA, 0x00}, + { GC9A01_DATA, 0x07}, + + { GC9A01_CMD, 0x66}, + { GC9A01_DATA, 0x3C}, + { GC9A01_DATA, 0x00}, + { GC9A01_DATA, 0xCD}, + { GC9A01_DATA, 0x67}, + { GC9A01_DATA, 0x45}, + { GC9A01_DATA, 0x45}, + { GC9A01_DATA, 0x10}, + { GC9A01_DATA, 0x00}, + { GC9A01_DATA, 0x00}, + { GC9A01_DATA, 0x00}, + + { GC9A01_CMD, 0x67}, + { GC9A01_DATA, 0x00}, + { GC9A01_DATA, 0x3C}, + { GC9A01_DATA, 0x00}, + { GC9A01_DATA, 0x00}, + { GC9A01_DATA, 0x00}, + { GC9A01_DATA, 0x01}, + { GC9A01_DATA, 0x54}, + { GC9A01_DATA, 0x10}, + { GC9A01_DATA, 0x32}, + { GC9A01_DATA, 0x98}, + + { GC9A01_CMD, 0x74}, + { GC9A01_DATA, 0x10}, + { GC9A01_DATA, 0x85}, + { GC9A01_DATA, 0x80}, + { GC9A01_DATA, 0x00}, + { GC9A01_DATA, 0x00}, + { GC9A01_DATA, 0x4E}, + { GC9A01_DATA, 0x00}, + + { GC9A01_CMD, 0x98}, + { GC9A01_DATA, 0x3E}, + { GC9A01_DATA, 0x07}, + + { GC9A01_CMD, 0x35}, // Tearing Effect Line ON + { GC9A01_CMD, 0x21}, // Display Inversion ON + + { GC9A01_CMD, 0x11}, // Sleep Out Mode + { GC9A01_DELAY, 120}, + { GC9A01_CMD, GC9A01_DISPON}, // Display ON + { GC9A01_DELAY, 255}, + { GC9A01_END, GC9A01_END}, +}; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Write a command to the GC9A01 + * @param cmd the command + */ +static void GC9A01_command(uint8_t cmd) +{ + LV_DRV_DISP_CMD_DATA(GC9A01_CMD_MODE); + LV_DRV_DISP_SPI_WR_BYTE(cmd); +} + +/** + * Write data to the GC9A01 + * @param data the data + */ +static void GC9A01_data(uint8_t data) +{ + LV_DRV_DISP_CMD_DATA(GC9A01_DATA_MODE); + LV_DRV_DISP_SPI_WR_BYTE(data); +} + +static int GC9A01_data_array(uint8_t *buf, uint32_t len) +{ + uint8_t *pt = buf; + + for (uint32_t lp = 0; lp < len; lp++, pt++) + { + LV_DRV_DISP_SPI_WR_BYTE(*pt); + } + return 0; +} + +static int GC9A01_databuf(uint32_t len, uint8_t *buf) +{ + uint32_t byte_left = len; + uint8_t *pt = buf; + + while (byte_left) + { + if (byte_left > 64) + { + LV_DRV_DISP_SPI_WR_ARRAY((char*)pt, 64); + byte_left = byte_left - 64; + pt = pt + 64; + } + else + { + LV_DRV_DISP_SPI_WR_ARRAY((char*)pt, byte_left); + byte_left=0; + } + } + + return 0; +} + +// hard reset of the tft controller +// ---------------------------------------------------------- +static void GC9A01_hard_reset( void ) +{ + LV_DRV_DISP_SPI_CS(0); // Low to listen to us + + LV_DRV_DISP_RST(1); + LV_DRV_DELAY_MS(50); + LV_DRV_DISP_RST(0); + LV_DRV_DELAY_MS(50); + LV_DRV_DISP_RST(1); + LV_DRV_DELAY_MS(50); +} + +// Configuration of the tft controller +// ---------------------------------------------------------- +static void GC9A01_run_cfg_script(void) +{ + int i = 0; + int end_script = 0; + + do { + switch (GC9A01_cfg_script[i].cmd) + { + case GC9A01_START: + break; + case GC9A01_CMD: + GC9A01_command( GC9A01_cfg_script[i].data & 0xFF ); + break; + case GC9A01_DATA: + GC9A01_data( GC9A01_cfg_script[i].data & 0xFF ); + break; + case GC9A01_DELAY: + LV_DRV_DELAY_MS(GC9A01_cfg_script[i].data); + break; + case GC9A01_END: + end_script = 1; + } + i++; + } while (!end_script); +} + +void GC9A01_drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) { + // Rudimentary clipping + if((x >= GC9A01_HOR_RES) || (y >= GC9A01_VER_RES)) return; + if((y+h-1) >= GC9A01_VER_RES) h = GC9A01_VER_RES - y; + + LV_DRV_DISP_SPI_CS(0); // Listen to us + + GC9A01_set_addr_win(x, y, x, y + h - 1); + + uint8_t hi = color >> 8, lo = color; + + while (h--) { + GC9A01_data(hi); + GC9A01_data(lo); + } + + LV_DRV_DISP_SPI_CS(1); +} + +void GC9A01_drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) { + // Rudimentary clipping + if((x >= GC9A01_HOR_RES) || (y >= GC9A01_VER_RES)) return; + if((x+w-1) >= GC9A01_HOR_RES) w = GC9A01_HOR_RES - x; + + LV_DRV_DISP_SPI_CS(0); // Listen to us + + GC9A01_set_addr_win(x, y, x + w - 1, y); + + uint8_t hi = color >> 8, lo = color; + + while (w--) { + GC9A01_data(hi); + GC9A01_data(lo); + } + + LV_DRV_DISP_SPI_CS(1); +} + + +// Pass 8-bit (each) R,G,B, get back 16-bit packed color +uint16_t GC9A01_Color565(uint8_t r, uint8_t g, uint8_t b) { + return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3); +} + +void GC9A01_invertDisplay(bool i) +{ + GC9A01_command(i ? GC9A01_INVON : GC9A01_INVOFF); +} + +void GC9A01_drawPixel(int16_t x, int16_t y, uint16_t color) +{ + if((x < 0) ||(x >= GC9A01_HOR_RES) || (y < 0) || (y >= GC9A01_VER_RES)) return; + + LV_DRV_DISP_SPI_CS(0); // Listen to us + GC9A01_set_addr_win(x, y, x, y); + + uint8_t hi = color >> 8, lo = color; + + GC9A01_data(hi); + GC9A01_data(lo); + + LV_DRV_DISP_SPI_CS(1); +} + +void GC9A01_fillScreen(uint16_t color) { + GC9A01_fillRect(0, 0, GC9A01_HOR_RES, GC9A01_VER_RES, color); +} + +// fill a rectangle +void GC9A01_fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) { + // rudimentary clipping (drawChar w/big text requires this) + if((x >= GC9A01_HOR_RES) || (y >= GC9A01_VER_RES)) return; + if((x + w - 1) >= GC9A01_HOR_RES) w = GC9A01_HOR_RES - x; + if((y + h - 1) >= GC9A01_VER_RES) h = GC9A01_VER_RES - y; + + LV_DRV_DISP_SPI_CS(0); // Listen to us + + GC9A01_set_addr_win(x, y, x + w - 1, y + h - 1); + + uint8_t hi = color >> 8, lo = color; + + for (y = h; y > 0; y--) + { + for (x = w; x > 0; x--) + { + GC9A01_data(hi); + GC9A01_data(lo); + } + } + LV_DRV_DISP_SPI_CS(1); +} + +void GC9A01_setRotation(uint8_t m) { + + GC9A01_command(GC9A01_MADCTL); + m %= 4; // can't be higher than 3 + switch (m) { + case 0: + GC9A01_data(GC9A01_MADCTL_MX | GC9A01_MADCTL_MY | GC9A01_MADCTL_RGB); + + // _xstart = _colstart; + // _ystart = _rowstart; + break; + case 1: + GC9A01_data(GC9A01_MADCTL_MY | GC9A01_MADCTL_MV | GC9A01_MADCTL_RGB); + + // _ystart = _colstart; + // _xstart = _rowstart; + break; + case 2: + GC9A01_data(GC9A01_MADCTL_RGB); + + // _xstart = _colstart; + // _ystart = _rowstart; + break; + + case 3: + GC9A01_data(GC9A01_MADCTL_MX | GC9A01_MADCTL_MV | GC9A01_MADCTL_RGB); + + // _ystart = _colstart; + // _xstart = _rowstart; + break; + } +} + +/** + * Initialize the GC9A01 + */ +int GC9A01_init(void) +{ + LV_DRV_DISP_SPI_FREQ(GC9A01_SPI_BAUD); + LV_DRV_DISP_SPI_MODE(GC9A01_SPI_BITS, GC9A01_SPI_MODE); + + GC9A01_hard_reset(); + GC9A01_run_cfg_script(); + + // GC9A01_fillScreen(0x0000); // Black + // GC9A01_fillScreen(0xFFFF); // White + GC9A01_fillScreen(0xAAAA); // ? + + return 0; +} + +static void GC9A01_set_addr_win(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) +{ + uint16_t x_start = x0 + GC9A01_XSTART, x_end = x1 + GC9A01_XSTART; + uint16_t y_start = y0 + GC9A01_YSTART, y_end = y1 + GC9A01_YSTART; + + GC9A01_command(GC9A01_CASET); // Column addr set + GC9A01_data(x_start >> 8); + GC9A01_data(x_start & 0xFF); // XSTART + GC9A01_data(x_end >> 8); + GC9A01_data(x_end & 0xFF); // XEND + + GC9A01_command(GC9A01_RASET); // Row addr set + GC9A01_data(y_start >> 8); + GC9A01_data(y_start & 0xFF); // YSTART + GC9A01_data(y_end >> 8); + GC9A01_data(y_end & 0xFF); // YEND + + GC9A01_command(GC9A01_RAMWR); +} + +void GC9A01_flush(struct _disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t *color_p) +{ + LV_DRV_DISP_SPI_CS(0); // Listen to us + + GC9A01_set_addr_win(area->x1, area->y1, area->x2, area->y2); + int32_t len = (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1) * 2; + + LV_DRV_DISP_CMD_DATA(GC9A01_DATA_MODE); + LV_DRV_DISP_SPI_WR_ARRAY((char*)color_p, len); + + LV_DRV_DISP_SPI_CS(1); + lv_disp_flush_ready(disp_drv); /* Indicate you are ready with the flushing*/ +} + +#endif \ No newline at end of file diff --git a/lv_drivers/display/GC9A01.h b/lv_drivers/display/GC9A01.h new file mode 100644 index 0000000..eeed192 --- /dev/null +++ b/lv_drivers/display/GC9A01.h @@ -0,0 +1,88 @@ +/** + * @file GC9A01.h + * + **/ + +#ifndef GC9A01_H +#define GC9A01_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifndef LV_DRV_NO_CONF +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_drv_conf.h" +#else +#include "../../lv_drv_conf.h" +#endif +#endif + +#if USE_GC9A01 + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +// #include "../../lv_drv_conf.h" +// #if USE_GC9A01 != 0 + +// #include +// #include "lvgl/lv_misc/lv_color.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/* Init script function */ +struct GC9A01_function { + uint16_t cmd; + uint16_t data; +}; + +/* Init script commands */ +enum GC9A01_cmd { + GC9A01_START, + GC9A01_END, + GC9A01_CMD, + GC9A01_DATA, + GC9A01_DELAY +}; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +int GC9A01_init(void); +void GC9A01_flush(struct _disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p); +void GC9A01_fill(int32_t x1, int32_t y1, int32_t x2, int32_t y2, lv_color_t color); +void GC9A01_map(int32_t x1, int32_t y1, int32_t x2, int32_t y2, lv_color_t * color_p); +void GC9A01_setRotation(uint8_t m); +void GC9A01_fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); +void GC9A01_fillScreen(uint16_t color); +uint16_t GC9A01_Color565(uint8_t r, uint8_t g, uint8_t b); +void GC9A01_invertDisplay(bool i); +void GC9A01_drawPixel(int16_t x, int16_t y, uint16_t color); +void GC9A01_drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); +void GC9A01_drawFastVLine(int16_t x, int16_t y, int16_t w, uint16_t color); + +/********************** + * MACROS + **********************/ + +#endif /* USE_GC9A01 */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* GC9A01_H */ \ No newline at end of file diff --git a/lv_drivers/display/ILI9341.c b/lv_drivers/display/ILI9341.c new file mode 100644 index 0000000..8d61d6e --- /dev/null +++ b/lv_drivers/display/ILI9341.c @@ -0,0 +1,432 @@ +/** + * @file ILI9341.c + * + * ILI9341.pdf [ILI9341_DS_V1.13_20110805] + * + * [references] + * - https://www.newhavendisplay.com/app_notes/ILI9341.pdf + * - Linux Source [v5.9-rc4] "drivers/staging/fbtft/fb_ili9341.c" + * - https://github.com/adafruit/Adafruit_ILI9341/blob/master/Adafruit_ILI9341.cpp + * - https://os.mbed.com/users/dreschpe/code/SPI_TFT_ILI9341 + * + */ + +/********************* + * INCLUDES + *********************/ +#include "ILI9341.h" +#if USE_ILI9341 != 0 + +#include +#include +#include LV_DRV_DISP_INCLUDE +#include LV_DRV_DELAY_INCLUDE + +/********************* + * DEFINES + *********************/ +#define ILI9341_CMD_MODE 0 +#define ILI9341_DATA_MODE 1 + +#define ILI9341_TFTWIDTH 240 +#define ILI9341_TFTHEIGHT 320 + +/* Level 1 Commands -------------- [section] Description */ + +#define ILI9341_NOP 0x00 /* [8.2.1 ] No Operation / Terminate Frame Memory Write */ +#define ILI9341_SWRESET 0x01 /* [8.2.2 ] Software Reset */ +#define ILI9341_RDDIDIF 0x04 /* [8.2.3 ] Read Display Identification Information */ +#define ILI9341_RDDST 0x09 /* [8.2.4 ] Read Display Status */ +#define ILI9341_RDDPM 0x0A /* [8.2.5 ] Read Display Power Mode */ +#define ILI9341_RDDMADCTL 0x0B /* [8.2.6 ] Read Display MADCTL */ +#define ILI9341_RDDCOLMOD 0x0C /* [8.2.7 ] Read Display Pixel Format */ +#define ILI9341_RDDIM 0x0D /* [8.2.8 ] Read Display Image Mode */ +#define ILI9341_RDDSM 0x0E /* [8.2.9 ] Read Display Signal Mode */ +#define ILI9341_RDDSDR 0x0F /* [8.2.10] Read Display Self-Diagnostic Result */ +#define ILI9341_SLPIN 0x10 /* [8.2.11] Enter Sleep Mode */ +#define ILI9341_SLPOUT 0x11 /* [8.2.12] Leave Sleep Mode */ +#define ILI9341_PTLON 0x12 /* [8.2.13] Partial Display Mode ON */ +#define ILI9341_NORON 0x13 /* [8.2.14] Normal Display Mode ON */ +#define ILI9341_DINVOFF 0x20 /* [8.2.15] Display Inversion OFF */ +#define ILI9341_DINVON 0x21 /* [8.2.16] Display Inversion ON */ +#define ILI9341_GAMSET 0x26 /* [8.2.17] Gamma Set */ +#define ILI9341_DISPOFF 0x28 /* [8.2.18] Display OFF*/ +#define ILI9341_DISPON 0x29 /* [8.2.19] Display ON*/ +#define ILI9341_CASET 0x2A /* [8.2.20] Column Address Set */ +#define ILI9341_PASET 0x2B /* [8.2.21] Page Address Set */ +#define ILI9341_RAMWR 0x2C /* [8.2.22] Memory Write */ +#define ILI9341_RGBSET 0x2D /* [8.2.23] Color Set (LUT for 16-bit to 18-bit color depth conversion) */ +#define ILI9341_RAMRD 0x2E /* [8.2.24] Memory Read */ +#define ILI9341_PTLAR 0x30 /* [8.2.25] Partial Area */ +#define ILI9341_VSCRDEF 0x33 /* [8.2.26] Veritcal Scrolling Definition */ +#define ILI9341_TEOFF 0x34 /* [8.2.27] Tearing Effect Line OFF */ +#define ILI9341_TEON 0x35 /* [8.2.28] Tearing Effect Line ON */ +#define ILI9341_MADCTL 0x36 /* [8.2.29] Memory Access Control */ +#define MADCTL_MY 0x80 /* MY row address order */ +#define MADCTL_MX 0x40 /* MX column address order */ +#define MADCTL_MV 0x20 /* MV row / column exchange */ +#define MADCTL_ML 0x10 /* ML vertical refresh order */ +#define MADCTL_MH 0x04 /* MH horizontal refresh order */ +#define MADCTL_RGB 0x00 /* RGB Order [default] */ +#define MADCTL_BGR 0x08 /* BGR Order */ +#define ILI9341_VSCRSADD 0x37 /* [8.2.30] Vertical Scrolling Start Address */ +#define ILI9341_IDMOFF 0x38 /* [8.2.31] Idle Mode OFF */ +#define ILI9341_IDMON 0x39 /* [8.2.32] Idle Mode ON */ +#define ILI9341_PIXSET 0x3A /* [8.2.33] Pixel Format Set */ +#define ILI9341_WRMEMCONT 0x3C /* [8.2.34] Write Memory Continue */ +#define ILI9341_RDMEMCONT 0x3E /* [8.2.35] Read Memory Continue */ +#define ILI9341_SETSCANTE 0x44 /* [8.2.36] Set Tear Scanline */ +#define ILI9341_GETSCAN 0x45 /* [8.2.37] Get Scanline */ +#define ILI9341_WRDISBV 0x51 /* [8.2.38] Write Display Brightness Value */ +#define ILI9341_RDDISBV 0x52 /* [8.2.39] Read Display Brightness Value */ +#define ILI9341_WRCTRLD 0x53 /* [8.2.40] Write Control Display */ +#define ILI9341_RDCTRLD 0x54 /* [8.2.41] Read Control Display */ +#define ILI9341_WRCABC 0x55 /* [8.2.42] Write Content Adaptive Brightness Control Value */ +#define ILI9341_RDCABC 0x56 /* [8.2.43] Read Content Adaptive Brightness Control Value */ +#define ILI9341_WRCABCMIN 0x5E /* [8.2.44] Write CABC Minimum Brightness */ +#define ILI9341_RDCABCMIN 0x5F /* [8.2.45] Read CABC Minimum Brightness */ +#define ILI9341_RDID1 0xDA /* [8.2.46] Read ID1 - Manufacturer ID (user) */ +#define ILI9341_RDID2 0xDB /* [8.2.47] Read ID2 - Module/Driver version (supplier) */ +#define ILI9341_RDID3 0xDC /* [8.2.48] Read ID3 - Module/Driver version (user) */ + +/* Level 2 Commands -------------- [section] Description */ + +#define ILI9341_IFMODE 0xB0 /* [8.3.1 ] Interface Mode Control */ +#define ILI9341_FRMCTR1 0xB1 /* [8.3.2 ] Frame Rate Control (In Normal Mode/Full Colors) */ +#define ILI9341_FRMCTR2 0xB2 /* [8.3.3 ] Frame Rate Control (In Idle Mode/8 colors) */ +#define ILI9341_FRMCTR3 0xB3 /* [8.3.4 ] Frame Rate control (In Partial Mode/Full Colors) */ +#define ILI9341_INVTR 0xB4 /* [8.3.5 ] Display Inversion Control */ +#define ILI9341_PRCTR 0xB5 /* [8.3.6 ] Blanking Porch Control */ +#define ILI9341_DISCTRL 0xB6 /* [8.3.7 ] Display Function Control */ +#define ILI9341_ETMOD 0xB7 /* [8.3.8 ] Entry Mode Set */ +#define ILI9341_BLCTRL1 0xB8 /* [8.3.9 ] Backlight Control 1 - Grayscale Histogram UI mode */ +#define ILI9341_BLCTRL2 0xB9 /* [8.3.10] Backlight Control 2 - Grayscale Histogram still picture mode */ +#define ILI9341_BLCTRL3 0xBA /* [8.3.11] Backlight Control 3 - Grayscale Thresholds UI mode */ +#define ILI9341_BLCTRL4 0xBB /* [8.3.12] Backlight Control 4 - Grayscale Thresholds still picture mode */ +#define ILI9341_BLCTRL5 0xBC /* [8.3.13] Backlight Control 5 - Brightness Transition time */ +#define ILI9341_BLCTRL7 0xBE /* [8.3.14] Backlight Control 7 - PWM Frequency */ +#define ILI9341_BLCTRL8 0xBF /* [8.3.15] Backlight Control 8 - ON/OFF + PWM Polarity*/ +#define ILI9341_PWCTRL1 0xC0 /* [8.3.16] Power Control 1 - GVDD */ +#define ILI9341_PWCTRL2 0xC1 /* [8.3.17] Power Control 2 - step-up factor for operating voltage */ +#define ILI9341_VMCTRL1 0xC5 /* [8.3.18] VCOM Control 1 - Set VCOMH and VCOML */ +#define ILI9341_VMCTRL2 0xC7 /* [8.3.19] VCOM Control 2 - VCOM offset voltage */ +#define ILI9341_NVMWR 0xD0 /* [8.3.20] NV Memory Write */ +#define ILI9341_NVMPKEY 0xD1 /* [8.3.21] NV Memory Protection Key */ +#define ILI9341_RDNVM 0xD2 /* [8.3.22] NV Memory Status Read */ +#define ILI9341_RDID4 0xD3 /* [8.3.23] Read ID4 - IC Device Code */ +#define ILI9341_PGAMCTRL 0xE0 /* [8.3.24] Positive Gamma Control */ +#define ILI9341_NGAMCTRL 0xE1 /* [8.3.25] Negative Gamma Correction */ +#define ILI9341_DGAMCTRL1 0xE2 /* [8.3.26] Digital Gamma Control 1 */ +#define ILI9341_DGAMCTRL2 0xE3 /* [8.3.27] Digital Gamma Control 2 */ +#define ILI9341_IFCTL 0xF6 /* [8.3.28] 16bits Data Format Selection */ + +/* Extended Commands --------------- [section] Description*/ + +#define ILI9341_PWCTRLA 0xCB /* [8.4.1] Power control A */ +#define ILI9341_PWCTRLB 0xCF /* [8.4.2] Power control B */ +#define ILI9341_TIMECTRLA_INT 0xE8 /* [8.4.3] Internal Clock Driver timing control A */ +#define ILI9341_TIMECTRLA_EXT 0xE9 /* [8.4.4] External Clock Driver timing control A */ +#define ILI9341_TIMECTRLB 0xEA /* [8.4.5] Driver timing control B (gate driver timing control) */ +#define ILI9341_PWSEQCTRL 0xED /* [8.4.6] Power on sequence control */ +#define ILI9341_GAM3CTRL 0xF2 /* [8.4.7] Enable 3 gamma control */ +#define ILI9341_PUMPRATIO 0xF7 /* [8.4.8] Pump ratio control */ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static inline void ili9341_write(int mode, uint8_t data); +static inline void ili9341_write_array(int mode, uint8_t *data, uint16_t len); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initialize the ILI9341 display controller + */ +void ili9341_init(void) +{ + uint8_t data[15]; + + /* hardware reset */ + LV_DRV_DISP_SPI_CS(1); + LV_DRV_DISP_CMD_DATA(ILI9341_DATA_MODE); + LV_DRV_DISP_RST(0); + LV_DRV_DELAY_US(50); + LV_DRV_DISP_RST(1); + LV_DRV_DELAY_MS(5); + + /* software reset */ + ili9341_write(ILI9341_CMD_MODE, ILI9341_SWRESET); + LV_DRV_DELAY_MS(5); + ili9341_write(ILI9341_CMD_MODE, ILI9341_DISPOFF); + + /* startup sequence */ + ili9341_write(ILI9341_CMD_MODE, ILI9341_PWCTRLB); + data[0] = 0x00; + data[1] = 0x83; + data[2] = 0x30; + ili9341_write_array(ILI9341_DATA_MODE, data, 3); + + ili9341_write(ILI9341_CMD_MODE, ILI9341_PWSEQCTRL); + data[0] = 0x64; + data[1] = 0x03; + data[2] = 0x12; + data[3] = 0x81; + ili9341_write_array(ILI9341_DATA_MODE, data, 4); + + ili9341_write(ILI9341_CMD_MODE, ILI9341_TIMECTRLA_INT); + data[0] = 0x85; + data[1] = 0x01; + data[2] = 0x79; + ili9341_write_array(ILI9341_DATA_MODE, data, 3); + + ili9341_write(ILI9341_CMD_MODE, ILI9341_PWCTRLA); + data[0] = 0x39; + data[1] = 0x2c; + data[2] = 0x00; + data[3] = 0x34; + data[4] = 0x02; + ili9341_write_array(ILI9341_DATA_MODE, data, 5); + + ili9341_write(ILI9341_CMD_MODE, ILI9341_PUMPRATIO); + ili9341_write(ILI9341_DATA_MODE, 0x20); + + ili9341_write(ILI9341_CMD_MODE, ILI9341_TIMECTRLB); + data[0] = 0x00; + data[1] = 0x00; + ili9341_write_array(ILI9341_DATA_MODE, data, 2); + + /* power control */ + ili9341_write(ILI9341_CMD_MODE, ILI9341_PWCTRL1); + ili9341_write(ILI9341_DATA_MODE, 0x26); + + ili9341_write(ILI9341_CMD_MODE, ILI9341_PWCTRL2); + ili9341_write(ILI9341_DATA_MODE, 0x11); + + /* VCOM */ + ili9341_write(ILI9341_CMD_MODE, ILI9341_VMCTRL1); + data[0] = 0x35; + data[1] = 0x3e; + ili9341_write_array(ILI9341_DATA_MODE, data, 2); + + ili9341_write(ILI9341_CMD_MODE, ILI9341_VMCTRL2); + ili9341_write(ILI9341_DATA_MODE, 0xbe); + + /* set orientation */ + ili9341_rotate(0, ILI9341_BGR); + + /* 16 bit pixel */ + ili9341_write(ILI9341_CMD_MODE, ILI9341_PIXSET); + ili9341_write(ILI9341_DATA_MODE, 0x55); + + /* frame rate */ + ili9341_write(ILI9341_CMD_MODE, ILI9341_FRMCTR1); + data[0] = 0x00; + data[1] = 0x1b; + ili9341_write_array(ILI9341_DATA_MODE, data, 2); + +#if ILI9341_GAMMA + /* gamma curve set */ + ili9341_write(ILI9341_CMD_MODE, ILI9341_GAMSET); + ili9341_write(ILI9341_DATA_MODE, 0x01); + + /* positive gamma correction */ + ili9341_write(ILI9341_CMD_MODE, ILI9341_PGAMCTRL); + data[0] = 0x1f; + data[1] = 0x1a; + data[2] = 0x18; + data[3] = 0x0a; + data[4] = 0x0f; + data[5] = 0x06; + data[6] = 0x45; + data[7] = 0x87; + data[8] = 0x32; + data[9] = 0x0a; + data[10] = 0x07; + data[11] = 0x02; + data[12] = 0x07; + data[13] = 0x05; + data[14] = 0x00; + ili9341_write_array(ILI9341_DATA_MODE, data, 15); + + /* negative gamma correction */ + ili9341_write(ILI9341_CMD_MODE, ILI9341_NGAMCTRL); + data[0] = 0x00; + data[1] = 0x25; + data[2] = 0x27; + data[3] = 0x05; + data[4] = 0x10; + data[5] = 0x09; + data[6] = 0x3a; + data[7] = 0x78; + data[8] = 0x4d; + data[9] = 0x05; + data[10] = 0x18; + data[11] = 0x0d; + data[12] = 0x38; + data[13] = 0x3a; + data[14] = 0x1f; + ili9341_write_array(ILI9341_DATA_MODE, data, 15); +#endif + + /* window horizontal */ + ili9341_write(ILI9341_CMD_MODE, ILI9341_CASET); + data[0] = 0; + data[1] = 0; + data[2] = (ILI9341_HOR_RES - 1) >> 8; + data[3] = (ILI9341_HOR_RES - 1); + ili9341_write_array(ILI9341_DATA_MODE, data, 4); + + /* window vertical */ + ili9341_write(ILI9341_CMD_MODE, ILI9341_PASET); + data[0] = 0; + data[1] = 0; + data[2] = (ILI9341_VER_RES - 1) >> 8; + data[3] = (ILI9341_VER_RES - 1); + ili9341_write_array(ILI9341_DATA_MODE, data, 4); + + ili9341_write(ILI9341_CMD_MODE, ILI9341_RAMWR); + +#if ILI9341_TEARING + /* tearing effect off */ + ili9341_write(ILI9341_CMD_MODE, ILI9341_TEOFF); + + /* tearing effect on */ + ili9341_write(ILI9341_CMD_MODE, ILI9341_TEON); +#endif + + /* entry mode set */ + ili9341_write(ILI9341_CMD_MODE, ILI9341_ETMOD); + ili9341_write(ILI9341_DATA_MODE, 0x07); + + /* display function control */ + ili9341_write(ILI9341_CMD_MODE, ILI9341_DISCTRL); + data[0] = 0x0a; + data[1] = 0x82; + data[2] = 0x27; + data[3] = 0x00; + ili9341_write_array(ILI9341_DATA_MODE, data, 4); + + /* exit sleep mode */ + ili9341_write(ILI9341_CMD_MODE, ILI9341_SLPOUT); + + LV_DRV_DELAY_MS(100); + + /* display on */ + ili9341_write(ILI9341_CMD_MODE, ILI9341_DISPON); + + LV_DRV_DELAY_MS(20); +} + +void ili9341_flush(lv_disp_drv_t * drv, const lv_area_t * area, const lv_color_t * color_p) +{ + if(area->x2 < 0 || area->y2 < 0 || area->x1 > (ILI9341_HOR_RES - 1) || area->y1 > (ILI9341_VER_RES - 1)) { + lv_disp_flush_ready(drv); + return; + } + + /* Truncate the area to the screen */ + int32_t act_x1 = area->x1 < 0 ? 0 : area->x1; + int32_t act_y1 = area->y1 < 0 ? 0 : area->y1; + int32_t act_x2 = area->x2 > ILI9341_HOR_RES - 1 ? ILI9341_HOR_RES - 1 : area->x2; + int32_t act_y2 = area->y2 > ILI9341_VER_RES - 1 ? ILI9341_VER_RES - 1 : area->y2; + + int32_t y; + uint8_t data[4]; + int32_t len = len = (act_x2 - act_x1 + 1) * 2; + lv_coord_t w = (area->x2 - area->x1) + 1; + + /* window horizontal */ + ili9341_write(ILI9341_CMD_MODE, ILI9341_CASET); + data[0] = act_x1 >> 8; + data[1] = act_x1; + data[2] = act_x2 >> 8; + data[3] = act_x2; + ili9341_write_array(ILI9341_DATA_MODE, data, 4); + + /* window vertical */ + ili9341_write(ILI9341_CMD_MODE, ILI9341_PASET); + data[0] = act_y1 >> 8; + data[1] = act_y1; + data[2] = act_y2 >> 8; + data[3] = act_y2; + ili9341_write_array(ILI9341_DATA_MODE, data, 4); + + ili9341_write(ILI9341_CMD_MODE, ILI9341_RAMWR); + + for(y = act_y1; y <= act_y2; y++) { + ili9341_write_array(ILI9341_DATA_MODE, (uint8_t *)color_p, len); + color_p += w; + } + + lv_disp_flush_ready(drv); +} + +void ili9341_rotate(int degrees, bool bgr) +{ + uint8_t color_order = MADCTL_RGB; + + if(bgr) + color_order = MADCTL_BGR; + + ili9341_write(ILI9341_CMD_MODE, ILI9341_MADCTL); + + switch(degrees) { + case 270: + ili9341_write(ILI9341_DATA_MODE, MADCTL_MV | color_order); + break; + case 180: + ili9341_write(ILI9341_DATA_MODE, MADCTL_MY | color_order); + break; + case 90: + ili9341_write(ILI9341_DATA_MODE, MADCTL_MX | MADCTL_MY | MADCTL_MV | color_order); + break; + case 0: + /* fall-through */ + default: + ili9341_write(ILI9341_DATA_MODE, MADCTL_MX | color_order); + break; + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Write byte + * @param mode sets command or data mode for write + * @param byte the byte to write + */ +static inline void ili9341_write(int mode, uint8_t data) +{ + LV_DRV_DISP_CMD_DATA(mode); + LV_DRV_DISP_SPI_WR_BYTE(data); +} + +/** + * Write byte array + * @param mode sets command or data mode for write + * @param data the byte array to write + * @param len the length of the byte array + */ +static inline void ili9341_write_array(int mode, uint8_t *data, uint16_t len) +{ + LV_DRV_DISP_CMD_DATA(mode); + LV_DRV_DISP_SPI_WR_ARRAY(data, len); +} + +#endif diff --git a/lv_drivers/display/ILI9341.h b/lv_drivers/display/ILI9341.h new file mode 100644 index 0000000..32435c9 --- /dev/null +++ b/lv_drivers/display/ILI9341.h @@ -0,0 +1,67 @@ +/** + * @file ILI9341.h + * + */ + +#ifndef ILI9341_H +#define ILI9341_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include +#ifndef LV_DRV_NO_CONF +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_drv_conf.h" +#else +#include "../../lv_drv_conf.h" +#endif +#endif + +#if USE_ILI9341 + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +#if LV_COLOR_DEPTH != 16 +#error "ILI9341 currently supports 'LV_COLOR_DEPTH == 16'. Set it in lv_conf.h" +#endif + +#if LV_COLOR_16_SWAP != 1 +#error "ILI9341 SPI requires LV_COLOR_16_SWAP == 1. Set it in lv_conf.h" +#endif + +/********************* + * DEFINES + *********************/ +#define ILI9341_BGR true +#define ILI9341_RGB false + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void ili9341_init(void); +void ili9341_flush(lv_disp_drv_t * drv, const lv_area_t * area, const lv_color_t * color_p); +void ili9341_rotate(int degrees, bool bgr); +/********************** + * MACROS + **********************/ + +#endif /* USE_ILI9341 */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ILI9341_H */ diff --git a/lv_drivers/display/R61581.c b/lv_drivers/display/R61581.c new file mode 100644 index 0000000..4d21bff --- /dev/null +++ b/lv_drivers/display/R61581.c @@ -0,0 +1,425 @@ +/** + * @file R61581.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "R61581.h" +#if USE_R61581 != 0 + +#include +#include "lvgl/lv_core/lv_vdb.h" +#include LV_DRV_DISP_INCLUDE +#include LV_DRV_DELAY_INCLUDE + +/********************* + * DEFINES + *********************/ +#define R61581_CMD_MODE 0 +#define R61581_DATA_MODE 1 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void r61581_io_init(void); +static void r61581_reset(void); +static void r61581_set_tft_spec(void); +static inline void r61581_cmd_mode(void); +static inline void r61581_data_mode(void); +static inline void r61581_cmd(uint8_t cmd); +static inline void r61581_data(uint8_t data); + +/********************** + * STATIC VARIABLES + **********************/ +static bool cmd_mode = true; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initialize the R61581 display controller + * @return HW_RES_OK or any error from hw_res_t enum + */ +void r61581_init(void) +{ + r61581_io_init(); + + /*Slow mode until the PLL is not started in the display controller*/ + LV_DRV_DISP_PAR_SLOW; + + r61581_reset(); + + r61581_set_tft_spec(); + + r61581_cmd(0x13); //SET display on + + r61581_cmd(0x29); //SET display on + LV_DRV_DELAY_MS(30); + + /*Parallel to max speed*/ + LV_DRV_DISP_PAR_FAST; +} + +void r61581_flush(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_color_t * color_p) +{ + /*Return if the area is out the screen*/ + if(x2 < 0) return; + if(y2 < 0) return; + if(x1 > R61581_HOR_RES - 1) return; + if(y1 > R61581_VER_RES - 1) return; + + /*Truncate the area to the screen*/ + int32_t act_x1 = x1 < 0 ? 0 : x1; + int32_t act_y1 = y1 < 0 ? 0 : y1; + int32_t act_x2 = x2 > R61581_HOR_RES - 1 ? R61581_HOR_RES - 1 : x2; + int32_t act_y2 = y2 > R61581_VER_RES - 1 ? R61581_VER_RES - 1 : y2; + + + //Set the rectangular area + r61581_cmd(0x002A); + r61581_data(act_x1 >> 8); + r61581_data(0x00FF & act_x1); + r61581_data(act_x2 >> 8); + r61581_data(0x00FF & act_x2); + + r61581_cmd(0x002B); + r61581_data(act_y1 >> 8); + r61581_data(0x00FF & act_y1); + r61581_data(act_y2 >> 8); + r61581_data(0x00FF & act_y2); + + r61581_cmd(0x2c); + + int16_t i; + uint16_t full_w = x2 - x1 + 1; + + r61581_data_mode(); + +#if LV_COLOR_DEPTH == 16 + uint16_t act_w = act_x2 - act_x1 + 1; + for(i = act_y1; i <= act_y2; i++) { + LV_DRV_DISP_PAR_WR_ARRAY((uint16_t *)color_p, act_w); + color_p += full_w; + } +#else + int16_t j; + for(i = act_y1; i <= act_y2; i++) { + for(j = 0; j <= act_x2 - act_x1 + 1; j++) { + LV_DRV_DISP_PAR_WR_WORD(lv_color_to16(color_p[j])); + color_p += full_w; + } + } +#endif + + lv_flush_ready(); +} + +void r61581_fill(int32_t x1, int32_t y1, int32_t x2, int32_t y2, lv_color_t color) +{ + /*Return if the area is out the screen*/ + if(x2 < 0) return; + if(y2 < 0) return; + if(x1 > R61581_HOR_RES - 1) return; + if(y1 > R61581_VER_RES - 1) return; + + /*Truncate the area to the screen*/ + int32_t act_x1 = x1 < 0 ? 0 : x1; + int32_t act_y1 = y1 < 0 ? 0 : y1; + int32_t act_x2 = x2 > R61581_HOR_RES - 1 ? R61581_HOR_RES - 1 : x2; + int32_t act_y2 = y2 > R61581_VER_RES - 1 ? R61581_VER_RES - 1 : y2; + + //Set the rectangular area + r61581_cmd(0x002A); + r61581_data(act_x1 >> 8); + r61581_data(0x00FF & act_x1); + r61581_data(act_x2 >> 8); + r61581_data(0x00FF & act_x2); + + r61581_cmd(0x002B); + r61581_data(act_y1 >> 8); + r61581_data(0x00FF & act_y1); + r61581_data(act_y2 >> 8); + r61581_data(0x00FF & act_y2); + + r61581_cmd(0x2c); + + r61581_data_mode(); + + uint16_t color16 = lv_color_to16(color); + uint32_t size = (act_x2 - act_x1 + 1) * (act_y2 - act_y1 + 1); + uint32_t i; + for(i = 0; i < size; i++) { + LV_DRV_DISP_PAR_WR_WORD(color16); + } +} + +void r61581_map(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_color_t * color_p) +{ + /*Return if the area is out the screen*/ + if(x2 < 0) return; + if(y2 < 0) return; + if(x1 > R61581_HOR_RES - 1) return; + if(y1 > R61581_VER_RES - 1) return; + + /*Truncate the area to the screen*/ + int32_t act_x1 = x1 < 0 ? 0 : x1; + int32_t act_y1 = y1 < 0 ? 0 : y1; + int32_t act_x2 = x2 > R61581_HOR_RES - 1 ? R61581_HOR_RES - 1 : x2; + int32_t act_y2 = y2 > R61581_VER_RES - 1 ? R61581_VER_RES - 1 : y2; + + + //Set the rectangular area + r61581_cmd(0x002A); + r61581_data(act_x1 >> 8); + r61581_data(0x00FF & act_x1); + r61581_data(act_x2 >> 8); + r61581_data(0x00FF & act_x2); + + r61581_cmd(0x002B); + r61581_data(act_y1 >> 8); + r61581_data(0x00FF & act_y1); + r61581_data(act_y2 >> 8); + r61581_data(0x00FF & act_y2); + + r61581_cmd(0x2c); + + int16_t i; + uint16_t full_w = x2 - x1 + 1; + + r61581_data_mode(); + +#if LV_COLOR_DEPTH == 16 + uint16_t act_w = act_x2 - act_x1 + 1; + for(i = act_y1; i <= act_y2; i++) { + LV_DRV_DISP_PAR_WR_ARRAY((uint16_t *)color_p, act_w); + color_p += full_w; + } +#else + int16_t j; + for(i = act_y1; i <= act_y2; i++) { + for(j = 0; j <= act_x2 - act_x1 + 1; j++) { + LV_DRV_DISP_PAR_WR_WORD(lv_color_to16(color_p[j])); + color_p += full_w; + } + } +#endif +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Io init + */ +static void r61581_io_init(void) +{ + LV_DRV_DISP_CMD_DATA(R61581_CMD_MODE) + cmd_mode = true; +} + +/** + * Reset + */ +static void r61581_reset(void) +{ + /*Hardware reset*/ + LV_DRV_DISP_RST(1); + LV_DRV_DELAY_MS(50); + LV_DRV_DISP_RST(0); + LV_DRV_DELAY_MS(50); + LV_DRV_DISP_RST(1); + LV_DRV_DELAY_MS(50); + + /*Chip enable*/ + LV_DRV_DISP_PAR_CS(1); + LV_DRV_DELAY_MS(10); + LV_DRV_DISP_PAR_CS(0); + LV_DRV_DELAY_MS(5); + + /*Software reset*/ + r61581_cmd(0x01); + LV_DRV_DELAY_MS(20); + + r61581_cmd(0x01); + LV_DRV_DELAY_MS(20); + + r61581_cmd(0x01); + LV_DRV_DELAY_MS(20); +} + +/** + * TFT specific initialization + */ +static void r61581_set_tft_spec(void) +{ + r61581_cmd(0xB0); + r61581_data(0x00); + + r61581_cmd(0xB3); + r61581_data(0x02); + r61581_data(0x00); + r61581_data(0x00); + r61581_data(0x10); + + r61581_cmd(0xB4); + r61581_data(0x00);//0X10 + + r61581_cmd(0xB9); //PWM + r61581_data(0x01); + r61581_data(0xFF); //FF brightness + r61581_data(0xFF); + r61581_data(0x18); + + /*Panel Driving Setting*/ + r61581_cmd(0xC0); + r61581_data(0x02); + r61581_data(0x3B); + r61581_data(0x00); + r61581_data(0x00); + r61581_data(0x00); + r61581_data(0x01); + r61581_data(0x00);//NW + r61581_data(0x43); + + /*Display Timing Setting for Normal Mode */ + r61581_cmd(0xC1); + r61581_data(0x08); + r61581_data(0x15); //CLOCK + r61581_data(R61581_VFP); + r61581_data(R61581_VBP); + + /*Source/VCOM/Gate Driving Timing Setting*/ + r61581_cmd(0xC4); + r61581_data(0x15); + r61581_data(0x03); + r61581_data(0x03); + r61581_data(0x01); + + /*Interface Setting*/ + r61581_cmd(0xC6); + r61581_data((R61581_DPL << 0) | + (R61581_EPL << 1) | + (R61581_HSPL << 4) | + (R61581_VSPL << 5)); + + /*Gamma Set*/ + r61581_cmd(0xC8); + r61581_data(0x0c); + r61581_data(0x05); + r61581_data(0x0A); + r61581_data(0x6B); + r61581_data(0x04); + r61581_data(0x06); + r61581_data(0x15); + r61581_data(0x10); + r61581_data(0x00); + r61581_data(0x31); + + + r61581_cmd(0x36); + if(R61581_ORI == 0) r61581_data(0xE0); + else r61581_data(0x20); + + r61581_cmd(0x0C); + r61581_data(0x55); + + r61581_cmd(0x3A); + r61581_data(0x55); + + r61581_cmd(0x38); + + r61581_cmd(0xD0); + r61581_data(0x07); + r61581_data(0x07); + r61581_data(0x14); + r61581_data(0xA2); + + r61581_cmd(0xD1); + r61581_data(0x03); + r61581_data(0x5A); + r61581_data(0x10); + + r61581_cmd(0xD2); + r61581_data(0x03); + r61581_data(0x04); + r61581_data(0x04); + + r61581_cmd(0x11); + LV_DRV_DELAY_MS(10); + + r61581_cmd(0x2A); + r61581_data(0x00); + r61581_data(0x00); + r61581_data(((R61581_HOR_RES - 1) >> 8) & 0XFF); + r61581_data((R61581_HOR_RES - 1) & 0XFF); + + r61581_cmd(0x2B); + r61581_data(0x00); + r61581_data(0x00); + r61581_data(((R61581_VER_RES - 1) >> 8) & 0XFF); + r61581_data((R61581_VER_RES - 1) & 0XFF); + + LV_DRV_DELAY_MS(10); + + r61581_cmd(0x29); + LV_DRV_DELAY_MS(5); + + r61581_cmd(0x2C); + LV_DRV_DELAY_MS(5); +} + +/** + * Command mode + */ +static inline void r61581_cmd_mode(void) +{ + if(cmd_mode == false) { + LV_DRV_DISP_CMD_DATA(R61581_CMD_MODE) + cmd_mode = true; + } +} + +/** + * Data mode + */ +static inline void r61581_data_mode(void) +{ + if(cmd_mode != false) { + LV_DRV_DISP_CMD_DATA(R61581_DATA_MODE); + cmd_mode = false; + } +} + +/** + * Write command + * @param cmd the command + */ +static inline void r61581_cmd(uint8_t cmd) +{ + r61581_cmd_mode(); + LV_DRV_DISP_PAR_WR_WORD(cmd); +} + +/** + * Write data + * @param data the data + */ +static inline void r61581_data(uint8_t data) +{ + r61581_data_mode(); + LV_DRV_DISP_PAR_WR_WORD(data); +} +#endif diff --git a/lv_drivers/display/R61581.h b/lv_drivers/display/R61581.h new file mode 100644 index 0000000..3ba4ff9 --- /dev/null +++ b/lv_drivers/display/R61581.h @@ -0,0 +1,57 @@ +/** + * @file R61581.h + * + */ + +#ifndef R61581_H +#define R61581_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifndef LV_DRV_NO_CONF +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_drv_conf.h" +#else +#include "../../lv_drv_conf.h" +#endif +#endif + +#if USE_R61581 + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void r61581_init(void); +void r61581_flush(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_color_t * color_p); +void r61581_fill(int32_t x1, int32_t y1, int32_t x2, int32_t y2, lv_color_t color); +void r61581_map(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_color_t * color_p); +/********************** + * MACROS + **********************/ + +#endif /* USE_R61581 */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* R61581_H */ diff --git a/lv_drivers/display/SHARP_MIP.c b/lv_drivers/display/SHARP_MIP.c new file mode 100644 index 0000000..9301aff --- /dev/null +++ b/lv_drivers/display/SHARP_MIP.c @@ -0,0 +1,182 @@ +/** + * @file SHARP_MIP.c + * + */ + +/*------------------------------------------------------------------------------------------------- + * SHARP memory in pixel monochrome display series + * LS012B7DD01 (184x38 pixels.) + * LS013B7DH03 (128x128 pixels.) + * LS013B7DH05 (144x168 pixels.) + * LS027B7DH01 (400x240 pixels.) (tested) + * LS032B7DD02 (336x536 pixels.) + * LS044Q7DH01 (320x240 pixels.) + * + * These displays need periodic com inversion, there are two ways : + * - software com inversion : + * define SHARP_MIP_SOFT_COM_INVERSION 1 and set EXTMODE display pin LOW, + * call sharp_mip_com_inversion() periodically + * - hardware com inversion with EXTCOMIN display pin : + * define SHARP_MIP_SOFT_COM_INVERSION 0, + * set EXTMODE display pin HIGH and handle + * EXTCOMIN waveform (for example with mcu pwm output), + * see datasheet pages 8-12 for details + * + * draw_buf size : (LV_VER_RES / X) * (2 + LV_HOR_RES / 8) + 2 bytes, structure : + * [FRAME_HEADER (1 byte)] [GATE_ADDR (1 byte )] [LINE_DATA (LV_HOR_RES / 8 bytes)] 1st line + * [DUMMY (1 byte)] [GATE_ADDR (1 byte )] [LINE_DATA (LV_HOR_RES / 8 bytes)] 2nd line + * ........................................................................................... + * [DUMMY (1 byte)] [GATE_ADDR (1 byte )] [LINE_DATA (LV_HOR_RES / 8 bytes)] last line + * [DUMMY (2 bytes)] + * + * Since extra bytes (dummy, addresses, header) are stored in draw_buf, we need to use + * an "oversized" draw_buf. Buffer declaration in "lv_port_disp.c" becomes for example : + * static lv_disp_buf_t disp_buf; + * static uint8_t buf[(LV_VER_RES_MAX / X) * (2 + (LV_HOR_RES_MAX / 8)) + 2]; + * lv_disp_buf_init(&disp_buf, buf, NULL, LV_VER_RES_MAX * LV_HOR_RES_MAX / X); + *-----------------------------------------------------------------------------------------------*/ + +/********************* + * INCLUDES + *********************/ + +#include "SHARP_MIP.h" + +#if USE_SHARP_MIP + +#include +#include LV_DRV_DISP_INCLUDE +#include LV_DRV_DELAY_INCLUDE + +/********************* + * DEFINES + *********************/ + +#define SHARP_MIP_HEADER 0 +#define SHARP_MIP_UPDATE_RAM_FLAG (1 << 7) /* (M0) Mode flag : H -> update memory, L -> maintain memory */ +#define SHARP_MIP_COM_INVERSION_FLAG (1 << 6) /* (M1) Frame inversion flag : relevant when EXTMODE = L, */ + /* H -> outputs VCOM = H, L -> outputs VCOM = L */ +#define SHARP_MIP_CLEAR_SCREEN_FLAG (1 << 5) /* (M2) All clear flag : H -> clear all pixels */ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +#if SHARP_MIP_SOFT_COM_INVERSION +static bool_t com_output_state = false; +#endif + +/********************** + * MACROS + **********************/ + +/* + * Return the draw_buf byte index corresponding to the pixel + * relatives coordinates (x, y) in the area. + * The area is rounded to a whole screen line. + */ +#define BUFIDX(x, y) (((x) >> 3) + ((y) * (2 + (SHARP_MIP_HOR_RES >> 3))) + 2) + +/* + * Return the byte bitmask of a pixel bit corresponding + * to draw_buf arrangement (8 pixels per byte on lines). + */ +#define PIXIDX(x) SHARP_MIP_REV_BYTE(1 << ((x) & 7)) + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void sharp_mip_init(void) { + /* These displays have nothing to initialize */ +} + + +void sharp_mip_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) { + + /*Return if the area is out the screen*/ + if(area->y2 < 0) return; + if(area->y1 > SHARP_MIP_VER_RES - 1) return; + + /*Truncate the area to the screen*/ + uint16_t act_y1 = area->y1 < 0 ? 0 : area->y1; + uint16_t act_y2 = area->y2 > SHARP_MIP_VER_RES - 1 ? SHARP_MIP_VER_RES - 1 : area->y2; + + uint8_t * buf = (uint8_t *) color_p; /*Get the buffer address*/ + uint16_t buf_h = (act_y2 - act_y1 + 1); /*Number of buffer lines*/ + uint16_t buf_size = buf_h * (2 + SHARP_MIP_HOR_RES / 8) + 2; /*Buffer size in bytes */ + + /* Set lines to flush dummy byte & gate address in draw_buf*/ + for(uint16_t act_y = 0 ; act_y < buf_h ; act_y++) { + buf[BUFIDX(0, act_y) - 1] = SHARP_MIP_REV_BYTE((act_y1 + act_y + 1)); + buf[BUFIDX(0, act_y) - 2] = 0; + } + + /* Set last dummy two bytes in draw_buf */ + buf[BUFIDX(0, buf_h) - 1] = 0; + buf[BUFIDX(0, buf_h) - 2] = 0; + + /* Set frame header in draw_buf */ + buf[0] = SHARP_MIP_HEADER | + SHARP_MIP_UPDATE_RAM_FLAG; + + /* Write the frame on display memory */ + LV_DRV_DISP_SPI_CS(1); + LV_DRV_DISP_SPI_WR_ARRAY(buf, buf_size); + LV_DRV_DISP_SPI_CS(0); + + lv_disp_flush_ready(disp_drv); +} + +void sharp_mip_set_px(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa) { + (void) disp_drv; + (void) buf_w; + (void) opa; + + if (lv_color_to1(color) != 0) { + buf[BUFIDX(x, y)] |= PIXIDX(x); /*Set draw_buf pixel bit to 1 for other colors than BLACK*/ + } else { + buf[BUFIDX(x, y)] &= ~PIXIDX(x); /*Set draw_buf pixel bit to 0 for BLACK color*/ + } +} + +void sharp_mip_rounder(lv_disp_drv_t * disp_drv, lv_area_t * area) { + (void) disp_drv; + + /* Round area to a whole line */ + area->x1 = 0; + area->x2 = SHARP_MIP_HOR_RES - 1; +} + +#if SHARP_MIP_SOFT_COM_INVERSION +void sharp_mip_com_inversion(void) { + uint8_t inversion_header[2] = {0}; + + /* Set inversion header */ + if (com_output_state) { + com_output_state = false; + } else { + inversion_header[0] |= SHARP_MIP_COM_INVERSION_FLAG; + com_output_state = true; + } + + /* Write inversion header on display memory */ + LV_DRV_DISP_SPI_CS(1); + LV_DRV_DISP_SPI_WR_ARRAY(inversion_header, 2); + LV_DRV_DISP_SPI_CS(0); +} +#endif + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif diff --git a/lv_drivers/display/SHARP_MIP.h b/lv_drivers/display/SHARP_MIP.h new file mode 100644 index 0000000..c10d845 --- /dev/null +++ b/lv_drivers/display/SHARP_MIP.h @@ -0,0 +1,63 @@ +/** + * @file SHARP_MIP.h + * + */ + +#ifndef SHARP_MIP_H +#define SHARP_MIP_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#ifndef LV_DRV_NO_CONF +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_drv_conf.h" +#else +#include "../../lv_drv_conf.h" +#endif +#endif + +#if USE_SHARP_MIP + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void sharp_mip_init(void); +void sharp_mip_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p); +void sharp_mip_rounder(lv_disp_drv_t * disp_drv, lv_area_t * area); +void sharp_mip_set_px(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa); +#if SHARP_MIP_SOFT_COM_INVERSION +void sharp_mip_com_inversion(void); +#endif + +/********************** + * MACROS + **********************/ + +#endif /* USE_SHARP_MIP */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SHARP_MIP_H */ diff --git a/lv_drivers/display/SSD1963.c b/lv_drivers/display/SSD1963.c new file mode 100644 index 0000000..c961066 --- /dev/null +++ b/lv_drivers/display/SSD1963.c @@ -0,0 +1,292 @@ +/** + * @file SSD1963.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "SSD1963.h" +#if USE_SSD1963 + +#include +#include LV_DRV_DISP_INCLUDE +#include LV_DRV_DELAY_INCLUDE + +/********************* + * DEFINES + *********************/ +#define SSD1963_CMD_MODE 0 +#define SSD1963_DATA_MODE 1 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static inline void ssd1963_cmd_mode(void); +static inline void ssd1963_data_mode(void); +static inline void ssd1963_cmd(uint8_t cmd); +static inline void ssd1963_data(uint8_t data); +static void ssd1963_io_init(void); +static void ssd1963_reset(void); +static void ssd1963_set_clk(void); +static void ssd1963_set_tft_spec(void); +static void ssd1963_init_bl(void); + +/********************** + * STATIC VARIABLES + **********************/ +static bool cmd_mode = true; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void ssd1963_init(void) +{ + + LV_DRV_DISP_CMD_DATA(SSD1963_CMD_MODE); + cmd_mode = true; + + LV_DRV_DELAY_MS(250); + + + ssd1963_cmd(0x00E2); //PLL multiplier, set PLL clock to 120M + ssd1963_data(0x0023); //N=0x36 for 6.5M, 0x23 for 10M crystal + ssd1963_data(0x0002); + ssd1963_data(0x0004); + ssd1963_cmd(0x00E0); // PLL enable + ssd1963_data(0x0001); + LV_DRV_DELAY_MS(1); + ssd1963_cmd(0x00E0); + ssd1963_data(0x0003); // now, use PLL output as system clock + LV_DRV_DELAY_MS(1); + ssd1963_cmd(0x0001); // software reset + LV_DRV_DELAY_MS(1); + ssd1963_cmd(0x00E6); //PLL setting for PCLK, depends on resolution + + ssd1963_data(0x0001); //HX8257C + ssd1963_data(0x0033); //HX8257C + ssd1963_data(0x0033); //HX8257C + + + ssd1963_cmd(0x00B0); //LCD SPECIFICATION + ssd1963_data(0x0020); + ssd1963_data(0x0000); + ssd1963_data(((SSD1963_HOR_RES - 1) >> 8) & 0X00FF); //Set HDP + ssd1963_data((SSD1963_HOR_RES - 1) & 0X00FF); + ssd1963_data(((SSD1963_VER_RES - 1) >> 8) & 0X00FF); //Set VDP + ssd1963_data((SSD1963_VER_RES - 1) & 0X00FF); + ssd1963_data(0x0000); + LV_DRV_DELAY_MS(1);//Delay10us(5); + ssd1963_cmd(0x00B4); //HSYNC + ssd1963_data((SSD1963_HT >> 8) & 0X00FF); //Set HT + ssd1963_data(SSD1963_HT & 0X00FF); + ssd1963_data((SSD1963_HPS >> 8) & 0X00FF); //Set HPS + ssd1963_data(SSD1963_HPS & 0X00FF); + ssd1963_data(SSD1963_HPW); //Set HPW + ssd1963_data((SSD1963_LPS >> 8) & 0X00FF); //SetLPS + ssd1963_data(SSD1963_LPS & 0X00FF); + ssd1963_data(0x0000); + + ssd1963_cmd(0x00B6); //VSYNC + ssd1963_data((SSD1963_VT >> 8) & 0X00FF); //Set VT + ssd1963_data(SSD1963_VT & 0X00FF); + ssd1963_data((SSD1963_VPS >> 8) & 0X00FF); //Set VPS + ssd1963_data(SSD1963_VPS & 0X00FF); + ssd1963_data(SSD1963_VPW); //Set VPW + ssd1963_data((SSD1963_FPS >> 8) & 0X00FF); //Set FPS + ssd1963_data(SSD1963_FPS & 0X00FF); + + ssd1963_cmd(0x00B8); + ssd1963_data(0x000f); //GPIO is controlled by host GPIO[3:0]=output GPIO[0]=1 LCD ON GPIO[0]=1 LCD OFF + ssd1963_data(0x0001); //GPIO0 normal + + ssd1963_cmd(0x00BA); + ssd1963_data(0x0001); //GPIO[0] out 1 --- LCD display on/off control PIN + + ssd1963_cmd(0x0036); //rotation + ssd1963_data(0x0008); //RGB=BGR + + ssd1963_cmd(0x003A); //Set the current pixel format for RGB image data + ssd1963_data(0x0050); //16-bit/pixel + + ssd1963_cmd(0x00F0); //Pixel Data Interface Format + ssd1963_data(0x0003); //16-bit(565 format) data + + ssd1963_cmd(0x00BC); + ssd1963_data(0x0040); //contrast value + ssd1963_data(0x0080); //brightness value + ssd1963_data(0x0040); //saturation value + ssd1963_data(0x0001); //Post Processor Enable + + LV_DRV_DELAY_MS(1); + + ssd1963_cmd(0x0029); //display on + + ssd1963_cmd(0x00BE); //set PWM for B/L + ssd1963_data(0x0006); + ssd1963_data(0x0080); + ssd1963_data(0x0001); + ssd1963_data(0x00f0); + ssd1963_data(0x0000); + ssd1963_data(0x0000); + + ssd1963_cmd(0x00d0); + ssd1963_data(0x000d); + + //DisplayBacklightOn(); + + LV_DRV_DELAY_MS(30); +} + +void ssd1963_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) +{ + + /*Return if the area is out the screen*/ + if(area->x2 < 0) return; + if(area->y2 < 0) return; + if(area->x1 > SSD1963_HOR_RES - 1) return; + if(area->y1 > SSD1963_VER_RES - 1) return; + + /*Truncate the area to the screen*/ + int32_t act_x1 = area->x1 < 0 ? 0 : area->x1; + int32_t act_y1 = area->y1 < 0 ? 0 : area->y1; + int32_t act_x2 = area->x2 > SSD1963_HOR_RES - 1 ? SSD1963_HOR_RES - 1 : area->x2; + int32_t act_y2 = area->y2 > SSD1963_VER_RES - 1 ? SSD1963_VER_RES - 1 : area->y2; + + //Set the rectangular area + ssd1963_cmd(0x002A); + ssd1963_data(act_x1 >> 8); + ssd1963_data(0x00FF & act_x1); + ssd1963_data(act_x2 >> 8); + ssd1963_data(0x00FF & act_x2); + + ssd1963_cmd(0x002B); + ssd1963_data(act_y1 >> 8); + ssd1963_data(0x00FF & act_y1); + ssd1963_data(act_y2 >> 8); + ssd1963_data(0x00FF & act_y2); + + ssd1963_cmd(0x2c); + int16_t i; + uint16_t full_w = area->x2 - area->x1 + 1; + + ssd1963_data_mode(); + LV_DRV_DISP_PAR_CS(0); +#if LV_COLOR_DEPTH == 16 + uint16_t act_w = act_x2 - act_x1 + 1; + for(i = act_y1; i <= act_y2; i++) { + LV_DRV_DISP_PAR_WR_ARRAY((uint16_t *)color_p, act_w); + color_p += full_w; + } + LV_DRV_DISP_PAR_CS(1); +#else + int16_t j; + for(i = act_y1; i <= act_y2; i++) { + for(j = 0; j <= act_x2 - act_x1 + 1; j++) { + LV_DRV_DISP_PAR_WR_WORD(color_p[j]); + color_p += full_w; + } + } +#endif + + lv_disp_flush_ready(disp_drv); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void ssd1963_io_init(void) +{ + LV_DRV_DISP_CMD_DATA(SSD1963_CMD_MODE); + cmd_mode = true; +} + +static void ssd1963_reset(void) +{ + /*Hardware reset*/ + LV_DRV_DISP_RST(1); + LV_DRV_DELAY_MS(50); + LV_DRV_DISP_RST(0); + LV_DRV_DELAY_MS(50); + LV_DRV_DISP_RST(1); + LV_DRV_DELAY_MS(50); + + /*Chip enable*/ + LV_DRV_DISP_PAR_CS(0); + LV_DRV_DELAY_MS(10); + LV_DRV_DISP_PAR_CS(1); + LV_DRV_DELAY_MS(5); + + /*Software reset*/ + ssd1963_cmd(0x01); + LV_DRV_DELAY_MS(20); + + ssd1963_cmd(0x01); + LV_DRV_DELAY_MS(20); + + ssd1963_cmd(0x01); + LV_DRV_DELAY_MS(20); + +} + +/** + * Command mode + */ +static inline void ssd1963_cmd_mode(void) +{ + if(cmd_mode == false) { + LV_DRV_DISP_CMD_DATA(SSD1963_CMD_MODE); + cmd_mode = true; + } +} + +/** + * Data mode + */ +static inline void ssd1963_data_mode(void) +{ + if(cmd_mode != false) { + LV_DRV_DISP_CMD_DATA(SSD1963_DATA_MODE); + cmd_mode = false; + } +} + +/** + * Write command + * @param cmd the command + */ +static inline void ssd1963_cmd(uint8_t cmd) +{ + + LV_DRV_DISP_PAR_CS(0); + ssd1963_cmd_mode(); + LV_DRV_DISP_PAR_WR_WORD(cmd); + LV_DRV_DISP_PAR_CS(1); + +} + +/** + * Write data + * @param data the data + */ +static inline void ssd1963_data(uint8_t data) +{ + + LV_DRV_DISP_PAR_CS(0); + ssd1963_data_mode(); + LV_DRV_DISP_PAR_WR_WORD(data); + LV_DRV_DISP_PAR_CS(1); + +} + +#endif diff --git a/lv_drivers/display/SSD1963.h b/lv_drivers/display/SSD1963.h new file mode 100644 index 0000000..49639f5 --- /dev/null +++ b/lv_drivers/display/SSD1963.h @@ -0,0 +1,150 @@ +/** + * @file SSD1963.h + * + */ + +#ifndef SSD1963_H +#define SSD1963_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifndef LV_DRV_NO_CONF +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_drv_conf.h" +#else +#include "../../lv_drv_conf.h" +#endif +#endif + +#if USE_SSD1963 + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +/********************* + * DEFINES + *********************/ +// SSD1963 command table +#define CMD_NOP 0x00 //No operation +#define CMD_SOFT_RESET 0x01 //Software reset +#define CMD_GET_PWR_MODE 0x0A //Get the current power mode +#define CMD_GET_ADDR_MODE 0x0B //Get the frame memory to the display panel read order +#define CMD_GET_PIXEL_FORMAT 0x0C //Get the current pixel format +#define CMD_GET_DISPLAY_MODE 0x0D //Returns the display mode +#define CMD_GET_SIGNAL_MODE 0x0E // +#define CMD_GET_DIAGNOSTIC 0x0F +#define CMD_ENT_SLEEP 0x10 +#define CMD_EXIT_SLEEP 0x11 +#define CMD_ENT_PARTIAL_MODE 0x12 +#define CMD_ENT_NORMAL_MODE 0x13 +#define CMD_EXIT_INVERT_MODE 0x20 +#define CMD_ENT_INVERT_MODE 0x21 +#define CMD_SET_GAMMA 0x26 +#define CMD_BLANK_DISPLAY 0x28 +#define CMD_ON_DISPLAY 0x29 +#define CMD_SET_COLUMN 0x2A +#define CMD_SET_PAGE 0x2B +#define CMD_WR_MEMSTART 0x2C +#define CMD_RD_MEMSTART 0x2E +#define CMD_SET_PARTIAL_AREA 0x30 +#define CMD_SET_SCROLL_AREA 0x33 +#define CMD_SET_TEAR_OFF 0x34 //synchronization information is not sent from the display +#define CMD_SET_TEAR_ON 0x35 //sync. information is sent from the display +#define CMD_SET_ADDR_MODE 0x36 //set fram buffer read order to the display panel +#define CMD_SET_SCROLL_START 0x37 +#define CMD_EXIT_IDLE_MODE 0x38 +#define CMD_ENT_IDLE_MODE 0x39 +#define CMD_SET_PIXEL_FORMAT 0x3A //defines how many bits per pixel is used +#define CMD_WR_MEM_AUTO 0x3C +#define CMD_RD_MEM_AUTO 0x3E +#define CMD_SET_TEAR_SCANLINE 0x44 +#define CMD_GET_SCANLINE 0x45 +#define CMD_RD_DDB_START 0xA1 +#define CMD_RD_DDB_AUTO 0xA8 +#define CMD_SET_PANEL_MODE 0xB0 +#define CMD_GET_PANEL_MODE 0xB1 +#define CMD_SET_HOR_PERIOD 0xB4 +#define CMD_GET_HOR_PERIOD 0xB5 +#define CMD_SET_VER_PERIOD 0xB6 +#define CMD_GET_VER_PERIOD 0xB7 +#define CMD_SET_GPIO_CONF 0xB8 +#define CMD_GET_GPIO_CONF 0xB9 +#define CMD_SET_GPIO_VAL 0xBA +#define CMD_GET_GPIO_STATUS 0xBB +#define CMD_SET_POST_PROC 0xBC +#define CMD_GET_POST_PROC 0xBD +#define CMD_SET_PWM_CONF 0xBE +#define CMD_GET_PWM_CONF 0xBF +#define CMD_SET_LCD_GEN0 0xC0 +#define CMD_GET_LCD_GEN0 0xC1 +#define CMD_SET_LCD_GEN1 0xC2 +#define CMD_GET_LCD_GEN1 0xC3 +#define CMD_SET_LCD_GEN2 0xC4 +#define CMD_GET_LCD_GEN2 0xC5 +#define CMD_SET_LCD_GEN3 0xC6 +#define CMD_GET_LCD_GEN3 0xC7 +#define CMD_SET_GPIO0_ROP 0xC8 +#define CMD_GET_GPIO0_ROP 0xC9 +#define CMD_SET_GPIO1_ROP 0xCA +#define CMD_GET_GPIO1_ROP 0xCB +#define CMD_SET_GPIO2_ROP 0xCC +#define CMD_GET_GPIO2_ROP 0xCD +#define CMD_SET_GPIO3_ROP 0xCE +#define CMD_GET_GPIO3_ROP 0xCF +#define CMD_SET_ABC_DBC_CONF 0xD0 +#define CMD_GET_ABC_DBC_CONF 0xD1 +#define CMD_SET_DBC_HISTO_PTR 0xD2 +#define CMD_GET_DBC_HISTO_PTR 0xD3 +#define CMD_SET_DBC_THRES 0xD4 +#define CMD_GET_DBC_THRES 0xD5 +#define CMD_SET_ABM_TMR 0xD6 +#define CMD_GET_ABM_TMR 0xD7 +#define CMD_SET_AMB_LVL0 0xD8 +#define CMD_GET_AMB_LVL0 0xD9 +#define CMD_SET_AMB_LVL1 0xDA +#define CMD_GET_AMB_LVL1 0xDB +#define CMD_SET_AMB_LVL2 0xDC +#define CMD_GET_AMB_LVL2 0xDD +#define CMD_SET_AMB_LVL3 0xDE +#define CMD_GET_AMB_LVL3 0xDF +#define CMD_PLL_START 0xE0 //start the PLL +#define CMD_PLL_STOP 0xE1 //disable the PLL +#define CMD_SET_PLL_MN 0xE2 +#define CMD_GET_PLL_MN 0xE3 +#define CMD_GET_PLL_STATUS 0xE4 //get the current PLL status +#define CMD_ENT_DEEP_SLEEP 0xE5 +#define CMD_SET_PCLK 0xE6 //set pixel clock (LSHIFT signal) frequency +#define CMD_GET_PCLK 0xE7 //get pixel clock (LSHIFT signal) freq. settings +#define CMD_SET_DATA_INTERFACE 0xF0 +#define CMD_GET_DATA_INTERFACE 0xF1 + + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void ssd1963_init(void); +void ssd1963_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p); + +/********************** + * MACROS + **********************/ + +#endif /* USE_SSD1963 */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SSD1963_H */ diff --git a/lv_drivers/display/ST7565.c b/lv_drivers/display/ST7565.c new file mode 100644 index 0000000..e4eac4b --- /dev/null +++ b/lv_drivers/display/ST7565.c @@ -0,0 +1,289 @@ +/** + * @file ST7565.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "ST7565.h" +#if USE_ST7565 + +#include +#include +#include +#include "lvgl/lv_core/lv_vdb.h" +#include LV_DRV_DISP_INCLUDE +#include LV_DRV_DELAY_INCLUDE + +/********************* + * DEFINES + *********************/ +#define ST7565_BAUD 2000000 /*< 2,5 MHz (400 ns)*/ + +#define ST7565_CMD_MODE 0 +#define ST7565_DATA_MODE 1 + +#define ST7565_HOR_RES 128 +#define ST7565_VER_RES 64 + +#define CMD_DISPLAY_OFF 0xAE +#define CMD_DISPLAY_ON 0xAF + +#define CMD_SET_DISP_START_LINE 0x40 +#define CMD_SET_PAGE 0xB0 + +#define CMD_SET_COLUMN_UPPER 0x10 +#define CMD_SET_COLUMN_LOWER 0x00 + +#define CMD_SET_ADC_NORMAL 0xA0 +#define CMD_SET_ADC_REVERSE 0xA1 + +#define CMD_SET_DISP_NORMAL 0xA6 +#define CMD_SET_DISP_REVERSE 0xA7 + +#define CMD_SET_ALLPTS_NORMAL 0xA4 +#define CMD_SET_ALLPTS_ON 0xA5 +#define CMD_SET_BIAS_9 0xA2 +#define CMD_SET_BIAS_7 0xA3 + +#define CMD_RMW 0xE0 +#define CMD_RMW_CLEAR 0xEE +#define CMD_INTERNAL_RESET 0xE2 +#define CMD_SET_COM_NORMAL 0xC0 +#define CMD_SET_COM_REVERSE 0xC8 +#define CMD_SET_POWER_CONTROL 0x28 +#define CMD_SET_RESISTOR_RATIO 0x20 +#define CMD_SET_VOLUME_FIRST 0x81 +#define CMD_SET_VOLUME_SECOND 0x00 +#define CMD_SET_STATIC_OFF 0xAC +#define CMD_SET_STATIC_ON 0xAD +#define CMD_SET_STATIC_REG 0x00 +#define CMD_SET_BOOSTER_FIRST 0xF8 +#define CMD_SET_BOOSTER_234 0x00 +#define CMD_SET_BOOSTER_5 0x01 +#define CMD_SET_BOOSTER_6 0x03 +#define CMD_NOP 0xE3 +#define CMD_TEST 0xF0 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void st7565_sync(int32_t x1, int32_t y1, int32_t x2, int32_t y2); +static void st7565_command(uint8_t cmd); +static void st7565_data(uint8_t data); + +/********************** + * STATIC VARIABLES + **********************/ +static uint8_t lcd_fb[ST7565_HOR_RES * ST7565_VER_RES / 8] = {0xAA, 0xAA}; +static uint8_t pagemap[] = { 7, 6, 5, 4, 3, 2, 1, 0 }; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initialize the ST7565 + */ +void st7565_init(void) +{ + LV_DRV_DISP_RST(1); + LV_DRV_DELAY_MS(10); + LV_DRV_DISP_RST(0); + LV_DRV_DELAY_MS(10); + LV_DRV_DISP_RST(1); + LV_DRV_DELAY_MS(10); + + LV_DRV_DISP_SPI_CS(0); + + st7565_command(CMD_SET_BIAS_7); + st7565_command(CMD_SET_ADC_NORMAL); + st7565_command(CMD_SET_COM_NORMAL); + st7565_command(CMD_SET_DISP_START_LINE); + st7565_command(CMD_SET_POWER_CONTROL | 0x4); + LV_DRV_DELAY_MS(50); + + st7565_command(CMD_SET_POWER_CONTROL | 0x6); + LV_DRV_DELAY_MS(50); + + st7565_command(CMD_SET_POWER_CONTROL | 0x7); + LV_DRV_DELAY_MS(10); + + st7565_command(CMD_SET_RESISTOR_RATIO | 0x6); // Defaulted to 0x26 (but could also be between 0x20-0x27 based on display's specs) + + st7565_command(CMD_DISPLAY_ON); + st7565_command(CMD_SET_ALLPTS_NORMAL); + + /*Set brightness*/ + st7565_command(CMD_SET_VOLUME_FIRST); + st7565_command(CMD_SET_VOLUME_SECOND | (0x18 & 0x3f)); + + LV_DRV_DISP_SPI_CS(1); + + memset(lcd_fb, 0x00, sizeof(lcd_fb)); +} + + +void st7565_flush(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_color_t * color_p) +{ + /*Return if the area is out the screen*/ + if(x2 < 0) return; + if(y2 < 0) return; + if(x1 > ST7565_HOR_RES - 1) return; + if(y1 > ST7565_VER_RES - 1) return; + + /*Truncate the area to the screen*/ + int32_t act_x1 = x1 < 0 ? 0 : x1; + int32_t act_y1 = y1 < 0 ? 0 : y1; + int32_t act_x2 = x2 > ST7565_HOR_RES - 1 ? ST7565_HOR_RES - 1 : x2; + int32_t act_y2 = y2 > ST7565_VER_RES - 1 ? ST7565_VER_RES - 1 : y2; + + int32_t x, y; + + /*Set the first row in */ + + /*Refresh frame buffer*/ + for(y = act_y1; y <= act_y2; y++) { + for(x = act_x1; x <= act_x2; x++) { + if(lv_color_to1(*color_p) != 0) { + lcd_fb[x + (y / 8)*ST7565_HOR_RES] &= ~(1 << (7 - (y % 8))); + } else { + lcd_fb[x + (y / 8)*ST7565_HOR_RES] |= (1 << (7 - (y % 8))); + } + color_p ++; + } + + color_p += x2 - act_x2; /*Next row*/ + } + + st7565_sync(act_x1, act_y1, act_x2, act_y2); + lv_flush_ready(); +} + + + +void st7565_fill(int32_t x1, int32_t y1, int32_t x2, int32_t y2, lv_color_t color) +{ + /*Return if the area is out the screen*/ + if(x2 < 0) return; + if(y2 < 0) return; + if(x1 > ST7565_HOR_RES - 1) return; + if(y1 > ST7565_VER_RES - 1) return; + + /*Truncate the area to the screen*/ + int32_t act_x1 = x1 < 0 ? 0 : x1; + int32_t act_y1 = y1 < 0 ? 0 : y1; + int32_t act_x2 = x2 > ST7565_HOR_RES - 1 ? ST7565_HOR_RES - 1 : x2; + int32_t act_y2 = y2 > ST7565_VER_RES - 1 ? ST7565_VER_RES - 1 : y2; + + int32_t x, y; + uint8_t white = lv_color_to1(color); + + /*Refresh frame buffer*/ + for(y = act_y1; y <= act_y2; y++) { + for(x = act_x1; x <= act_x2; x++) { + if(white != 0) { + lcd_fb[x + (y / 8)*ST7565_HOR_RES] |= (1 << (7 - (y % 8))); + } else { + lcd_fb[x + (y / 8)*ST7565_HOR_RES] &= ~(1 << (7 - (y % 8))); + } + } + } + + st7565_sync(act_x1, act_y1, act_x2, act_y2); +} + +void st7565_map(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_color_t * color_p) +{ + /*Return if the area is out the screen*/ + if(x2 < 0) return; + if(y2 < 0) return; + if(x1 > ST7565_HOR_RES - 1) return; + if(y1 > ST7565_VER_RES - 1) return; + + /*Truncate the area to the screen*/ + int32_t act_x1 = x1 < 0 ? 0 : x1; + int32_t act_y1 = y1 < 0 ? 0 : y1; + int32_t act_x2 = x2 > ST7565_HOR_RES - 1 ? ST7565_HOR_RES - 1 : x2; + int32_t act_y2 = y2 > ST7565_VER_RES - 1 ? ST7565_VER_RES - 1 : y2; + + int32_t x, y; + + /*Set the first row in */ + + /*Refresh frame buffer*/ + for(y = act_y1; y <= act_y2; y++) { + for(x = act_x1; x <= act_x2; x++) { + if(lv_color_to1(*color_p) != 0) { + lcd_fb[x + (y / 8)*ST7565_HOR_RES] &= ~(1 << (7 - (y % 8))); + } else { + lcd_fb[x + (y / 8)*ST7565_HOR_RES] |= (1 << (7 - (y % 8))); + } + color_p ++; + } + + color_p += x2 - act_x2; /*Next row*/ + } + + st7565_sync(act_x1, act_y1, act_x2, act_y2); +} +/********************** + * STATIC FUNCTIONS + **********************/ +/** + * Flush a specific part of the buffer to the display + * @param x1 left coordinate of the area to flush + * @param y1 top coordinate of the area to flush + * @param x2 right coordinate of the area to flush + * @param y2 bottom coordinate of the area to flush + */ +static void st7565_sync(int32_t x1, int32_t y1, int32_t x2, int32_t y2) +{ + + LV_DRV_DISP_SPI_CS(0); + + uint8_t c, p; + for(p = y1 / 8; p <= y2 / 8; p++) { + st7565_command(CMD_SET_PAGE | pagemap[p]); + st7565_command(CMD_SET_COLUMN_LOWER | (x1 & 0xf)); + st7565_command(CMD_SET_COLUMN_UPPER | ((x1 >> 4) & 0xf)); + st7565_command(CMD_RMW); + + for(c = x1; c <= x2; c++) { + st7565_data(lcd_fb[(ST7565_HOR_RES * p) + c]); + } + } + + LV_DRV_DISP_SPI_CS(1); +} + +/** + * Write a command to the ST7565 + * @param cmd the command + */ +static void st7565_command(uint8_t cmd) +{ + LV_DRV_DISP_CMD_DATA(ST7565_CMD_MODE); + LV_DRV_DISP_SPI_WR_BYTE(cmd); +} + +/** + * Write data to the ST7565 + * @param data the data + */ +static void st7565_data(uint8_t data) +{ + LV_DRV_DISP_CMD_DATA(ST7565_DATA_MODE); + LV_DRV_DISP_SPI_WR_BYTE(data); +} + +#endif diff --git a/lv_drivers/display/ST7565.h b/lv_drivers/display/ST7565.h new file mode 100644 index 0000000..1a96df4 --- /dev/null +++ b/lv_drivers/display/ST7565.h @@ -0,0 +1,58 @@ +/** + * @file ST7565.h + * + */ + +#ifndef ST7565_H +#define ST7565_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifndef LV_DRV_NO_CONF +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_drv_conf.h" +#else +#include "../../lv_drv_conf.h" +#endif +#endif + +#if USE_ST7565 + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void st7565_init(void); +void st7565_flush(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_color_t * color_p); +void st7565_fill(int32_t x1, int32_t y1, int32_t x2, int32_t y2, lv_color_t color); +void st7565_map(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_color_t * color_p); + +/********************** + * MACROS + **********************/ + +#endif /* USE_ST7565 */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ST7565_H */ diff --git a/lv_drivers/display/UC1610.c b/lv_drivers/display/UC1610.c new file mode 100644 index 0000000..0f2b439 --- /dev/null +++ b/lv_drivers/display/UC1610.c @@ -0,0 +1,206 @@ +/** + * @file UC1610.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "UC1610.h" + +#if USE_UC1610 + +#include +#include LV_DRV_DISP_INCLUDE +#include LV_DRV_DELAY_INCLUDE + +/********************* + * DEFINES + *********************/ +#define UC1610_CMD_MODE 0 +#define UC1610_DATA_MODE 1 +#define UC1610_RESET_MODE 0 +#define UC1610_SET_MODE 1 + +/* hardware control commands */ +#define UC1610_SYSTEM_RESET 0xE2 /* software reset */ +#define UC1610_NOP 0xE3 +#define UC1610_SET_TEMP_COMP 0x24 /* set temperature compensation, default -0.05%/°C */ +#define UC1610_SET_PANEL_LOADING 0x29 /* set panel loading, default 16~21 nF */ +#define UC1610_SET_PUMP_CONTROL 0x2F /* default internal Vlcd (8x pump) */ +#define UC1610_SET_LCD_BIAS_RATIO 0xEB /* default 11 */ +#define UC1610_SET_VBIAS_POT 0x81 /* 1 byte (0~255) to follow setting the contrast, default 0x81 */ +#define UC1610_SET_LINE_RATE 0xA0 /* default 12,1 Klps */ +#define UC1610_SET_DISPLAY_ENABLE 0xAE /* + 1 / 0 : exit sleep mode / entering sleep mode */ +#define UC1610_SET_LCD_GRAY_SHADE 0xD0 /* default 24% between the two gray shade levels */ +#define UC1610_SET_COM_END 0xF1 /* set the number of used com electrodes (lines number -1) */ + +/* ram address control */ +#define UC1610_SET_AC 0x88 /* set ram address control */ +#define UC1610_AC_WA_FLAG 1 /* automatic column/page increment wrap around (1 : cycle increment) */ +#define UC1610_AC_AIO_FLAG (1 << 1) /* auto increment order (0/1 : column/page increment first) */ +#define UC1610_AC_PID_FLAG (1 << 2) /* page address auto increment order (0/1 : +1/-1) */ + +/* set cursor ram address */ +#define UC1610_SET_CA_LSB 0x00 /* + 4 LSB bits */ +#define UC1610_SET_CA_MSB 0x10 /* + 4 MSB bits // MSB + LSB values range : 0~159 */ +#define UC1610_SET_PA 0x60 /* + 5 bits // values range : 0~26 */ + +/* display control commands */ +#define UC1610_SET_FIXED_LINES 0x90 /* + 4 bits = 2xFL */ +#define UC1610_SET_SCROLL_LINES_LSB 0x40 /* + 4 LSB bits scroll up display by N (7 bits) lines */ +#define UC1610_SET_SCROLL_LINES_MSB 0x50 /* + 3 MSB bits */ +#define UC1610_SET_ALL_PIXEL_ON 0xA4 /* + 1 / 0 : set all pixel on, reverse */ +#define UC1610_SET_INVERSE_DISPLAY 0xA6 /* + 1 / 0 : inverse all data stored in ram, reverse */ +#define UC1610_SET_MAPPING_CONTROL 0xC0 /* control mirorring */ +#define UC1610_SET_MAPPING_CONTROL_LC_FLAG 1 +#define UC1610_SET_MAPPING_CONTROL_MX_FLAG (1 << 1) +#define UC1610_SET_MAPPING_CONTROL_MY_FLAG (1 << 2) + +/* window program mode */ +#define UC1610_SET_WINDOW_PROGRAM_ENABLE 0xF8 /* + 1 / 0 : enable / disable window programming mode, */ + /* reset before changing boundaries */ +#define UC1610_SET_WP_STARTING_CA 0xF4 /* 1 byte to follow for column address */ +#define UC1610_SET_WP_ENDING_CA 0xF6 /* 1 byte to follow for column address */ +#define UC1610_SET_WP_STARTING_PA 0xF5 /* 1 byte to follow for page address */ +#define UC1610_SET_WP_ENDING_PA 0xF7 /* 1 byte to follow for page address */ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ +static uint8_t cmd_buf[12]; + +/********************** + * MACROS + **********************/ + +/* Return the byte bitmask of a pixel color corresponding to draw_buf arrangement */ +#define PIXIDX(y, c) ((c) << (((y) & 3) << 1)) + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void uc1610_init(void) { + LV_DRV_DELAY_MS(12); + + /* initialization sequence */ +#if UC1610_INIT_HARD_RST + LV_DRV_DISP_RST(UC1610_RESET_MODE); /* hardware reset */ + LV_DRV_DELAY_MS(1); + LV_DRV_DISP_RST(UC1610_SET_MODE); +#else + cmd_buf[0] = UC1610_SYSTEM_RESET; /* software reset */ + LV_DRV_DISP_CMD_DATA(UC1610_CMD_MODE); + LV_DRV_DISP_SPI_CS(0); + LV_DRV_DISP_SPI_WR_ARRAY(cmd_buf, 1); + LV_DRV_DISP_SPI_CS(1); +#endif + + LV_DRV_DELAY_MS(2); + cmd_buf[0] = UC1610_SET_COM_END; /* set com end value */ + cmd_buf[1] = UC1610_VER_RES - 1; + cmd_buf[2] = UC1610_SET_PANEL_LOADING; + cmd_buf[3] = UC1610_SET_LCD_BIAS_RATIO; + cmd_buf[4] = UC1610_SET_VBIAS_POT; /* set contrast */ + cmd_buf[5] = (UC1610_INIT_CONTRAST * 255) / 100; +#if UC1610_TOP_VIEW + cmd_buf[6] = UC1610_SET_MAPPING_CONTROL | /* top view */ + UC1610_SET_MAPPING_CONTROL_MY_FLAG | + UC1610_SET_MAPPING_CONTROL_MX_FLAG; +#else + cmd_buf[6] = UC1610_SET_MAPPING_CONTROL; /* bottom view */ +#endif + cmd_buf[7] = UC1610_SET_SCROLL_LINES_LSB | 0; /* set scroll line on line 0 */ + cmd_buf[8] = UC1610_SET_SCROLL_LINES_MSB | 0; + cmd_buf[9] = UC1610_SET_AC | UC1610_AC_WA_FLAG; /* set auto increment wrap around */ + cmd_buf[10] = UC1610_SET_INVERSE_DISPLAY | 1; /* invert colors to complies lv color system */ + cmd_buf[11] = UC1610_SET_DISPLAY_ENABLE | 1; /* turn display on */ + + LV_DRV_DISP_CMD_DATA(UC1610_CMD_MODE); + LV_DRV_DISP_SPI_CS(0); + LV_DRV_DISP_SPI_WR_ARRAY(cmd_buf, 12); + LV_DRV_DISP_SPI_CS(1); +} + + +void uc1610_flush_cb(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) { + /*Return if the area is out the screen*/ + if(area->x2 < 0) return; + if(area->y2 < 0) return; + if(area->x1 > UC1610_HOR_RES - 1) return; + if(area->y1 > UC1610_VER_RES - 1) return; + + /*Truncate the area to the screen*/ + uint8_t act_x1 = area->x1 < 0 ? 0 : area->x1; + uint8_t act_y1 = area->y1 < 0 ? 0 : area->y1; + uint8_t act_x2 = area->x2 > UC1610_HOR_RES - 1 ? UC1610_HOR_RES - 1 : area->x2; + uint8_t act_y2 = area->y2 > UC1610_VER_RES - 1 ? UC1610_VER_RES - 1 : area->y2; + + uint8_t * buf = (uint8_t *) color_p; + uint16_t buf_size = (act_x2 - act_x1 + 1) * (((act_y2 - act_y1) >> 2) + 1); + + /*Set display window to fill*/ + cmd_buf[0] = UC1610_SET_WINDOW_PROGRAM_ENABLE | 0; /* before changing boundaries */ + cmd_buf[1] = UC1610_SET_WP_STARTING_CA; + cmd_buf[2] = act_x1; + cmd_buf[3] = UC1610_SET_WP_ENDING_CA; + cmd_buf[4] = act_x2; + cmd_buf[5] = UC1610_SET_WP_STARTING_PA; + cmd_buf[6] = act_y1 >> 2; + cmd_buf[7] = UC1610_SET_WP_ENDING_PA; + cmd_buf[8] = act_y2 >> 2; + cmd_buf[9] = UC1610_SET_WINDOW_PROGRAM_ENABLE | 1; /* entering window programming */ + + LV_DRV_DISP_CMD_DATA(UC1610_CMD_MODE); + LV_DRV_DISP_SPI_CS(0); + LV_DRV_DISP_SPI_WR_ARRAY(cmd_buf, 10); + LV_DRV_DISP_SPI_CS(1); + + /*Flush draw_buf on display memory*/ + LV_DRV_DISP_CMD_DATA(UC1610_DATA_MODE); + LV_DRV_DISP_SPI_CS(0); + LV_DRV_DISP_SPI_WR_ARRAY(buf, buf_size); + LV_DRV_DISP_SPI_CS(1); + + lv_disp_flush_ready(disp_drv); +} + +void uc1610_set_px_cb(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa) { + (void) disp_drv; + (void) opa; + + uint16_t idx = x + buf_w * (y >> 2); + + /* Convert color to depth 2 */ +#if LV_COLOR_DEPTH == 1 + uint8_t color2 = color.full * 3; +#else + uint8_t color2 = color.full >> (LV_COLOR_DEPTH - 2); +#endif + + buf[idx] &= ~PIXIDX(y, 3); /* reset pixel color */ + buf[idx] |= PIXIDX(y, color2); /* write new color */ +} + +void uc1610_rounder_cb(lv_disp_drv_t * disp_drv, lv_area_t * area) { + (void) disp_drv; + + /* Round y window to display memory page size */ + area->y1 = (area->y1 & (~3)); + area->y2 = (area->y2 & (~3)) + 3; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif diff --git a/lv_drivers/display/UC1610.h b/lv_drivers/display/UC1610.h new file mode 100644 index 0000000..38b1fa3 --- /dev/null +++ b/lv_drivers/display/UC1610.h @@ -0,0 +1,58 @@ +/** + * @file UC1610.h + * + */ + +#ifndef UC1610_H +#define UC1610_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifndef LV_DRV_NO_CONF +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_drv_conf.h" +#else +#include "../../lv_drv_conf.h" +#endif +#endif + +#if USE_UC1610 + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void uc1610_init(void); +void uc1610_flush_cb(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p); +void uc1610_rounder_cb(lv_disp_drv_t * disp_drv, lv_area_t * area); +void uc1610_set_px_cb(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa); + +/********************** + * MACROS + **********************/ + +#endif /* USE_UC1610 */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* UC1610_H */ diff --git a/lv_drivers/display/drm.c b/lv_drivers/display/drm.c new file mode 100644 index 0000000..285ce19 --- /dev/null +++ b/lv_drivers/display/drm.c @@ -0,0 +1,796 @@ +/** + * @file drm.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "drm.h" +#if USE_DRM + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define DBG_TAG "drm" + +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) + +#define print(msg, ...) fprintf(stderr, msg, ##__VA_ARGS__); +#define err(msg, ...) print("error: " msg "\n", ##__VA_ARGS__) +#define info(msg, ...) print(msg "\n", ##__VA_ARGS__) +#define dbg(msg, ...) {} //print(DBG_TAG ": " msg "\n", ##__VA_ARGS__) + +struct drm_buffer { + uint32_t handle; + uint32_t pitch; + uint32_t offset; + unsigned long int size; + void * map; + uint32_t fb_handle; +}; + +struct drm_dev { + int fd; + uint32_t conn_id, enc_id, crtc_id, plane_id, crtc_idx; + uint32_t width, height; + uint32_t mmWidth, mmHeight; + uint32_t fourcc; + drmModeModeInfo mode; + uint32_t blob_id; + drmModeCrtc *saved_crtc; + drmModeAtomicReq *req; + drmEventContext drm_event_ctx; + drmModePlane *plane; + drmModeCrtc *crtc; + drmModeConnector *conn; + uint32_t count_plane_props; + uint32_t count_crtc_props; + uint32_t count_conn_props; + drmModePropertyPtr plane_props[128]; + drmModePropertyPtr crtc_props[128]; + drmModePropertyPtr conn_props[128]; + struct drm_buffer drm_bufs[2]; /* DUMB buffers */ + struct drm_buffer *cur_bufs[2]; /* double buffering handling */ +} drm_dev; + +static uint32_t get_plane_property_id(const char *name) +{ + uint32_t i; + + dbg("Find plane property: %s", name); + + for (i = 0; i < drm_dev.count_plane_props; ++i) + if (!strcmp(drm_dev.plane_props[i]->name, name)) + return drm_dev.plane_props[i]->prop_id; + + dbg("Unknown plane property: %s", name); + + return 0; +} + +static uint32_t get_crtc_property_id(const char *name) +{ + uint32_t i; + + dbg("Find crtc property: %s", name); + + for (i = 0; i < drm_dev.count_crtc_props; ++i) + if (!strcmp(drm_dev.crtc_props[i]->name, name)) + return drm_dev.crtc_props[i]->prop_id; + + dbg("Unknown crtc property: %s", name); + + return 0; +} + +static uint32_t get_conn_property_id(const char *name) +{ + uint32_t i; + + dbg("Find conn property: %s", name); + + for (i = 0; i < drm_dev.count_conn_props; ++i) + if (!strcmp(drm_dev.conn_props[i]->name, name)) + return drm_dev.conn_props[i]->prop_id; + + dbg("Unknown conn property: %s", name); + + return 0; +} + +static void page_flip_handler(int fd, unsigned int sequence, unsigned int tv_sec, + unsigned int tv_usec, void *user_data) +{ + dbg("flip"); +} + +static int drm_get_plane_props(void) +{ + uint32_t i; + + drmModeObjectPropertiesPtr props = drmModeObjectGetProperties(drm_dev.fd, drm_dev.plane_id, + DRM_MODE_OBJECT_PLANE); + if (!props) { + err("drmModeObjectGetProperties failed"); + return -1; + } + dbg("Found %u plane props", props->count_props); + drm_dev.count_plane_props = props->count_props; + for (i = 0; i < props->count_props; i++) { + drm_dev.plane_props[i] = drmModeGetProperty(drm_dev.fd, props->props[i]); + dbg("Added plane prop %u:%s", drm_dev.plane_props[i]->prop_id, drm_dev.plane_props[i]->name); + } + drmModeFreeObjectProperties(props); + + return 0; +} + +static int drm_get_crtc_props(void) +{ + uint32_t i; + + drmModeObjectPropertiesPtr props = drmModeObjectGetProperties(drm_dev.fd, drm_dev.crtc_id, + DRM_MODE_OBJECT_CRTC); + if (!props) { + err("drmModeObjectGetProperties failed"); + return -1; + } + dbg("Found %u crtc props", props->count_props); + drm_dev.count_crtc_props = props->count_props; + for (i = 0; i < props->count_props; i++) { + drm_dev.crtc_props[i] = drmModeGetProperty(drm_dev.fd, props->props[i]); + dbg("Added crtc prop %u:%s", drm_dev.crtc_props[i]->prop_id, drm_dev.crtc_props[i]->name); + } + drmModeFreeObjectProperties(props); + + return 0; +} + +static int drm_get_conn_props(void) +{ + uint32_t i; + + drmModeObjectPropertiesPtr props = drmModeObjectGetProperties(drm_dev.fd, drm_dev.conn_id, + DRM_MODE_OBJECT_CONNECTOR); + if (!props) { + err("drmModeObjectGetProperties failed"); + return -1; + } + dbg("Found %u connector props", props->count_props); + drm_dev.count_conn_props = props->count_props; + for (i = 0; i < props->count_props; i++) { + drm_dev.conn_props[i] = drmModeGetProperty(drm_dev.fd, props->props[i]); + dbg("Added connector prop %u:%s", drm_dev.conn_props[i]->prop_id, drm_dev.conn_props[i]->name); + } + drmModeFreeObjectProperties(props); + + return 0; +} + +static int drm_add_plane_property(const char *name, uint64_t value) +{ + int ret; + uint32_t prop_id = get_plane_property_id(name); + + if (!prop_id) { + err("Couldn't find plane prop %s", name); + return -1; + } + + ret = drmModeAtomicAddProperty(drm_dev.req, drm_dev.plane_id, get_plane_property_id(name), value); + if (ret < 0) { + err("drmModeAtomicAddProperty (%s:%" PRIu64 ") failed: %d", name, value, ret); + return ret; + } + + return 0; +} + +static int drm_add_crtc_property(const char *name, uint64_t value) +{ + int ret; + uint32_t prop_id = get_crtc_property_id(name); + + if (!prop_id) { + err("Couldn't find crtc prop %s", name); + return -1; + } + + ret = drmModeAtomicAddProperty(drm_dev.req, drm_dev.crtc_id, get_crtc_property_id(name), value); + if (ret < 0) { + err("drmModeAtomicAddProperty (%s:%" PRIu64 ") failed: %d", name, value, ret); + return ret; + } + + return 0; +} + +static int drm_add_conn_property(const char *name, uint64_t value) +{ + int ret; + uint32_t prop_id = get_conn_property_id(name); + + if (!prop_id) { + err("Couldn't find conn prop %s", name); + return -1; + } + + ret = drmModeAtomicAddProperty(drm_dev.req, drm_dev.conn_id, get_conn_property_id(name), value); + if (ret < 0) { + err("drmModeAtomicAddProperty (%s:%" PRIu64 ") failed: %d", name, value, ret); + return ret; + } + + return 0; +} + +static int drm_dmabuf_set_plane(struct drm_buffer *buf) +{ + int ret; + static int first = 1; + uint32_t flags = DRM_MODE_PAGE_FLIP_EVENT; + + drm_dev.req = drmModeAtomicAlloc(); + + /* On first Atomic commit, do a modeset */ + if (first) { + drm_add_conn_property("CRTC_ID", drm_dev.crtc_id); + + drm_add_crtc_property("MODE_ID", drm_dev.blob_id); + drm_add_crtc_property("ACTIVE", 1); + + flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; + + first = 0; + } + + drm_add_plane_property("FB_ID", buf->fb_handle); + drm_add_plane_property("CRTC_ID", drm_dev.crtc_id); + drm_add_plane_property("SRC_X", 0); + drm_add_plane_property("SRC_Y", 0); + drm_add_plane_property("SRC_W", drm_dev.width << 16); + drm_add_plane_property("SRC_H", drm_dev.height << 16); + drm_add_plane_property("CRTC_X", 0); + drm_add_plane_property("CRTC_Y", 0); + drm_add_plane_property("CRTC_W", drm_dev.width); + drm_add_plane_property("CRTC_H", drm_dev.height); + + ret = drmModeAtomicCommit(drm_dev.fd, drm_dev.req, flags, NULL); + if (ret) { + err("drmModeAtomicCommit failed: %s", strerror(errno)); + drmModeAtomicFree(drm_dev.req); + return ret; + } + + return 0; +} + +static int find_plane(unsigned int fourcc, uint32_t *plane_id, uint32_t crtc_id, uint32_t crtc_idx) +{ + drmModePlaneResPtr planes; + drmModePlanePtr plane; + unsigned int i; + unsigned int j; + int ret = 0; + unsigned int format = fourcc; + + planes = drmModeGetPlaneResources(drm_dev.fd); + if (!planes) { + err("drmModeGetPlaneResources failed"); + return -1; + } + + dbg("drm: found planes %u", planes->count_planes); + + for (i = 0; i < planes->count_planes; ++i) { + plane = drmModeGetPlane(drm_dev.fd, planes->planes[i]); + if (!plane) { + err("drmModeGetPlane failed: %s", strerror(errno)); + break; + } + + if (!(plane->possible_crtcs & (1 << crtc_idx))) { + drmModeFreePlane(plane); + continue; + } + + for (j = 0; j < plane->count_formats; ++j) { + if (plane->formats[j] == format) + break; + } + + if (j == plane->count_formats) { + drmModeFreePlane(plane); + continue; + } + + *plane_id = plane->plane_id; + drmModeFreePlane(plane); + + dbg("found plane %d", *plane_id); + + break; + } + + if (i == planes->count_planes) + ret = -1; + + drmModeFreePlaneResources(planes); + + return ret; +} + +static int drm_find_connector(void) +{ + drmModeConnector *conn = NULL; + drmModeEncoder *enc = NULL; + drmModeRes *res; + int i; + + if ((res = drmModeGetResources(drm_dev.fd)) == NULL) { + err("drmModeGetResources() failed"); + return -1; + } + + if (res->count_crtcs <= 0) { + err("no Crtcs"); + goto free_res; + } + + /* find all available connectors */ + for (i = 0; i < res->count_connectors; i++) { + conn = drmModeGetConnector(drm_dev.fd, res->connectors[i]); + if (!conn) + continue; + +#if DRM_CONNECTOR_ID >= 0 + if (conn->connector_id != DRM_CONNECTOR_ID) { + drmModeFreeConnector(conn); + continue; + } +#endif + + if (conn->connection == DRM_MODE_CONNECTED) { + dbg("drm: connector %d: connected", conn->connector_id); + } else if (conn->connection == DRM_MODE_DISCONNECTED) { + dbg("drm: connector %d: disconnected", conn->connector_id); + } else if (conn->connection == DRM_MODE_UNKNOWNCONNECTION) { + dbg("drm: connector %d: unknownconnection", conn->connector_id); + } else { + dbg("drm: connector %d: unknown", conn->connector_id); + } + + if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes > 0) + break; + + drmModeFreeConnector(conn); + conn = NULL; + }; + + if (!conn) { + err("suitable connector not found"); + goto free_res; + } + + drm_dev.conn_id = conn->connector_id; + dbg("conn_id: %d", drm_dev.conn_id); + drm_dev.mmWidth = conn->mmWidth; + drm_dev.mmHeight = conn->mmHeight; + + memcpy(&drm_dev.mode, &conn->modes[0], sizeof(drmModeModeInfo)); + + if (drmModeCreatePropertyBlob(drm_dev.fd, &drm_dev.mode, sizeof(drm_dev.mode), + &drm_dev.blob_id)) { + err("error creating mode blob"); + goto free_res; + } + + drm_dev.width = conn->modes[0].hdisplay; + drm_dev.height = conn->modes[0].vdisplay; + + for (i = 0 ; i < res->count_encoders; i++) { + enc = drmModeGetEncoder(drm_dev.fd, res->encoders[i]); + if (!enc) + continue; + + dbg("enc%d enc_id %d conn enc_id %d", i, enc->encoder_id, conn->encoder_id); + + if (enc->encoder_id == conn->encoder_id) + break; + + drmModeFreeEncoder(enc); + enc = NULL; + } + + if (enc) { + drm_dev.enc_id = enc->encoder_id; + dbg("enc_id: %d", drm_dev.enc_id); + drm_dev.crtc_id = enc->crtc_id; + dbg("crtc_id: %d", drm_dev.crtc_id); + drmModeFreeEncoder(enc); + } else { + /* Encoder hasn't been associated yet, look it up */ + for (i = 0; i < conn->count_encoders; i++) { + int crtc, crtc_id = -1; + + enc = drmModeGetEncoder(drm_dev.fd, conn->encoders[i]); + if (!enc) + continue; + + for (crtc = 0 ; crtc < res->count_crtcs; crtc++) { + uint32_t crtc_mask = 1 << crtc; + + crtc_id = res->crtcs[crtc]; + + dbg("enc_id %d crtc%d id %d mask %x possible %x", enc->encoder_id, crtc, crtc_id, crtc_mask, enc->possible_crtcs); + + if (enc->possible_crtcs & crtc_mask) + break; + } + + if (crtc_id > 0) { + drm_dev.enc_id = enc->encoder_id; + dbg("enc_id: %d", drm_dev.enc_id); + drm_dev.crtc_id = crtc_id; + dbg("crtc_id: %d", drm_dev.crtc_id); + break; + } + + drmModeFreeEncoder(enc); + enc = NULL; + } + + if (!enc) { + err("suitable encoder not found"); + goto free_res; + } + + drmModeFreeEncoder(enc); + } + + drm_dev.crtc_idx = -1; + + for (i = 0; i < res->count_crtcs; ++i) { + if (drm_dev.crtc_id == res->crtcs[i]) { + drm_dev.crtc_idx = i; + break; + } + } + + if (drm_dev.crtc_idx == -1) { + err("drm: CRTC not found"); + goto free_res; + } + + dbg("crtc_idx: %d", drm_dev.crtc_idx); + + return 0; + +free_res: + drmModeFreeResources(res); + + return -1; +} + +static int drm_open(const char *path) +{ + int fd, flags; + uint64_t has_dumb; + int ret; + + fd = open(path, O_RDWR); + if (fd < 0) { + err("cannot open \"%s\"", path); + return -1; + } + + /* set FD_CLOEXEC flag */ + if ((flags = fcntl(fd, F_GETFD)) < 0 || + fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) { + err("fcntl FD_CLOEXEC failed"); + goto err; + } + + /* check capability */ + ret = drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &has_dumb); + if (ret < 0 || has_dumb == 0) { + err("drmGetCap DRM_CAP_DUMB_BUFFER failed or doesn't have dumb " + "buffer"); + goto err; + } + + return fd; +err: + close(fd); + return -1; +} + +static int drm_setup(unsigned int fourcc) +{ + int ret; + + drm_dev.fd = drm_open(DRM_CARD); + if (drm_dev.fd < 0) + return -1; + + ret = drmSetClientCap(drm_dev.fd, DRM_CLIENT_CAP_ATOMIC, 1); + if (ret) { + err("No atomic modesetting support: %s", strerror(errno)); + goto err; + } + + ret = drm_find_connector(); + if (ret) { + err("available drm devices not found"); + goto err; + } + + ret = find_plane(fourcc, &drm_dev.plane_id, drm_dev.crtc_id, drm_dev.crtc_idx); + if (ret) { + err("Cannot find plane"); + goto err; + } + + drm_dev.plane = drmModeGetPlane(drm_dev.fd, drm_dev.plane_id); + if (!drm_dev.plane) { + err("Cannot get plane"); + goto err; + } + + drm_dev.crtc = drmModeGetCrtc(drm_dev.fd, drm_dev.crtc_id); + if (!drm_dev.crtc) { + err("Cannot get crtc"); + goto err; + } + + drm_dev.conn = drmModeGetConnector(drm_dev.fd, drm_dev.conn_id); + if (!drm_dev.conn) { + err("Cannot get connector"); + goto err; + } + + ret = drm_get_plane_props(); + if (ret) { + err("Cannot get plane props"); + goto err; + } + + ret = drm_get_crtc_props(); + if (ret) { + err("Cannot get crtc props"); + goto err; + } + + ret = drm_get_conn_props(); + if (ret) { + err("Cannot get connector props"); + goto err; + } + + drm_dev.drm_event_ctx.version = DRM_EVENT_CONTEXT_VERSION; + drm_dev.drm_event_ctx.page_flip_handler = page_flip_handler; + drm_dev.fourcc = fourcc; + + info("drm: Found plane_id: %u connector_id: %d crtc_id: %d", + drm_dev.plane_id, drm_dev.conn_id, drm_dev.crtc_id); + + info("drm: %dx%d (%dmm X% dmm) pixel format %c%c%c%c", + drm_dev.width, drm_dev.height, drm_dev.mmWidth, drm_dev.mmHeight, + (fourcc>>0)&0xff, (fourcc>>8)&0xff, (fourcc>>16)&0xff, (fourcc>>24)&0xff); + + return 0; + +err: + close(drm_dev.fd); + return -1; +} + +static int drm_allocate_dumb(struct drm_buffer *buf) +{ + struct drm_mode_create_dumb creq; + struct drm_mode_map_dumb mreq; + uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0}; + int ret; + + /* create dumb buffer */ + memset(&creq, 0, sizeof(creq)); + creq.width = drm_dev.width; + creq.height = drm_dev.height; + creq.bpp = LV_COLOR_DEPTH; + ret = drmIoctl(drm_dev.fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq); + if (ret < 0) { + err("DRM_IOCTL_MODE_CREATE_DUMB fail"); + return -1; + } + + buf->handle = creq.handle; + buf->pitch = creq.pitch; + dbg("pitch %d", buf->pitch); + buf->size = creq.size; + dbg("size %d", buf->size); + + /* prepare buffer for memory mapping */ + memset(&mreq, 0, sizeof(mreq)); + mreq.handle = creq.handle; + ret = drmIoctl(drm_dev.fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq); + if (ret) { + err("DRM_IOCTL_MODE_MAP_DUMB fail"); + return -1; + } + + buf->offset = mreq.offset; + + /* perform actual memory mapping */ + buf->map = mmap(0, creq.size, PROT_READ | PROT_WRITE, MAP_SHARED, drm_dev.fd, mreq.offset); + if (buf->map == MAP_FAILED) { + err("mmap fail"); + return -1; + } + + /* clear the framebuffer to 0 (= full transparency in ARGB8888) */ + memset(buf->map, 0, creq.size); + + /* create framebuffer object for the dumb-buffer */ + handles[0] = creq.handle; + pitches[0] = creq.pitch; + offsets[0] = 0; + ret = drmModeAddFB2(drm_dev.fd, drm_dev.width, drm_dev.height, drm_dev.fourcc, + handles, pitches, offsets, &buf->fb_handle, 0); + if (ret) { + err("drmModeAddFB fail"); + return -1; + } + + return 0; +} + +static int drm_setup_buffers(void) +{ + int ret; + + /* Allocate DUMB buffers */ + ret = drm_allocate_dumb(&drm_dev.drm_bufs[0]); + if (ret) + return ret; + + ret = drm_allocate_dumb(&drm_dev.drm_bufs[1]); + if (ret) + return ret; + + /* Set buffering handling */ + drm_dev.cur_bufs[0] = NULL; + drm_dev.cur_bufs[1] = &drm_dev.drm_bufs[0]; + + return 0; +} + +void drm_wait_vsync(lv_disp_drv_t *disp_drv) +{ + int ret; + fd_set fds; + FD_ZERO(&fds); + FD_SET(drm_dev.fd, &fds); + + do { + ret = select(drm_dev.fd + 1, &fds, NULL, NULL, NULL); + } while (ret == -1 && errno == EINTR); + + if (ret < 0) { + err("select failed: %s", strerror(errno)); + drmModeAtomicFree(drm_dev.req); + drm_dev.req = NULL; + return; + } + + if (FD_ISSET(drm_dev.fd, &fds)) + drmHandleEvent(drm_dev.fd, &drm_dev.drm_event_ctx); + + drmModeAtomicFree(drm_dev.req); + drm_dev.req = NULL; +} + +void drm_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) +{ + struct drm_buffer *fbuf = drm_dev.cur_bufs[1]; + lv_coord_t w = (area->x2 - area->x1 + 1); + lv_coord_t h = (area->y2 - area->y1 + 1); + int i, y; + + dbg("x %d:%d y %d:%d w %d h %d", area->x1, area->x2, area->y1, area->y2, w, h); + + /* Partial update */ + if ((w != drm_dev.width || h != drm_dev.height) && drm_dev.cur_bufs[0]) + memcpy(fbuf->map, drm_dev.cur_bufs[0]->map, fbuf->size); + + for (y = 0, i = area->y1 ; i <= area->y2 ; ++i, ++y) { + memcpy((uint8_t *)fbuf->map + (area->x1 * (LV_COLOR_SIZE/8)) + (fbuf->pitch * i), + (uint8_t *)color_p + (w * (LV_COLOR_SIZE/8) * y), + w * (LV_COLOR_SIZE/8)); + } + + if (drm_dev.req) + drm_wait_vsync(disp_drv); + + /* show fbuf plane */ + if (drm_dmabuf_set_plane(fbuf)) { + err("Flush fail"); + return; + } + else + dbg("Flush done"); + + if (!drm_dev.cur_bufs[0]) + drm_dev.cur_bufs[1] = &drm_dev.drm_bufs[1]; + else + drm_dev.cur_bufs[1] = drm_dev.cur_bufs[0]; + + drm_dev.cur_bufs[0] = fbuf; + + lv_disp_flush_ready(disp_drv); +} + +#if LV_COLOR_DEPTH == 32 +#define DRM_FOURCC DRM_FORMAT_ARGB8888 +#elif LV_COLOR_DEPTH == 16 +#define DRM_FOURCC DRM_FORMAT_RGB565 +#else +#error LV_COLOR_DEPTH not supported +#endif + +void drm_get_sizes(lv_coord_t *width, lv_coord_t *height, uint32_t *dpi) +{ + if (width) + *width = drm_dev.width; + + if (height) + *height = drm_dev.height; + + if (dpi && drm_dev.mmWidth) + *dpi = DIV_ROUND_UP(drm_dev.width * 25400, drm_dev.mmWidth * 1000); +} + +void drm_init(void) +{ + int ret; + + ret = drm_setup(DRM_FOURCC); + if (ret) { + close(drm_dev.fd); + drm_dev.fd = -1; + return; + } + + ret = drm_setup_buffers(); + if (ret) { + err("DRM buffer allocation failed"); + close(drm_dev.fd); + drm_dev.fd = -1; + return; + } + + info("DRM subsystem and buffer mapped successfully"); +} + +void drm_exit(void) +{ + close(drm_dev.fd); + drm_dev.fd = -1; +} + +#endif diff --git a/lv_drivers/display/drm.h b/lv_drivers/display/drm.h new file mode 100644 index 0000000..ebf2e28 --- /dev/null +++ b/lv_drivers/display/drm.h @@ -0,0 +1,60 @@ +/** + * @file drm.h + * + */ + +#ifndef DRM_H +#define DRM_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifndef LV_DRV_NO_CONF +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_drv_conf.h" +#else +#include "../../lv_drv_conf.h" +#endif +#endif + +#if USE_DRM + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void drm_init(void); +void drm_get_sizes(lv_coord_t *width, lv_coord_t *height, uint32_t *dpi); +void drm_exit(void); +void drm_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p); +void drm_wait_vsync(lv_disp_drv_t * drv); + + +/********************** + * MACROS + **********************/ + +#endif /*USE_DRM*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*DRM_H*/ diff --git a/lv_drivers/display/fbdev.c b/lv_drivers/display/fbdev.c new file mode 100644 index 0000000..d391ce8 --- /dev/null +++ b/lv_drivers/display/fbdev.c @@ -0,0 +1,251 @@ +/** + * @file fbdev.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "fbdev.h" +#if USE_FBDEV || USE_BSD_FBDEV + +#include +#include +#include +#include +#include +#include +#include + +#if USE_BSD_FBDEV +#include +#include +#include +#include +#else /* USE_BSD_FBDEV */ +#include +#endif /* USE_BSD_FBDEV */ + +/********************* + * DEFINES + *********************/ +#ifndef FBDEV_PATH +#define FBDEV_PATH "/dev/fb0" +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STRUCTURES + **********************/ + +struct bsd_fb_var_info{ + uint32_t xoffset; + uint32_t yoffset; + uint32_t xres; + uint32_t yres; + int bits_per_pixel; + }; + +struct bsd_fb_fix_info{ + long int line_length; + long int smem_len; +}; + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ +#if USE_BSD_FBDEV +static struct bsd_fb_var_info vinfo; +static struct bsd_fb_fix_info finfo; +#else +static struct fb_var_screeninfo vinfo; +static struct fb_fix_screeninfo finfo; +#endif /* USE_BSD_FBDEV */ +static char *fbp = 0; +static long int screensize = 0; +static int fbfd = 0; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void fbdev_init(void) +{ + // Open the file for reading and writing + fbfd = open(FBDEV_PATH, O_RDWR); + if(fbfd == -1) { + perror("Error: cannot open framebuffer device"); + return; + } + printf("The framebuffer device was opened successfully.\n"); + +#if USE_BSD_FBDEV + struct fbtype fb; + unsigned line_length; + + //Get fb type + if (ioctl(fbfd, FBIOGTYPE, &fb) != 0) { + perror("ioctl(FBIOGTYPE)"); + return; + } + + //Get screen width + if (ioctl(fbfd, FBIO_GETLINEWIDTH, &line_length) != 0) { + perror("ioctl(FBIO_GETLINEWIDTH)"); + return; + } + + vinfo.xres = (unsigned) fb.fb_width; + vinfo.yres = (unsigned) fb.fb_height; + vinfo.bits_per_pixel = fb.fb_depth; + vinfo.xoffset = 0; + vinfo.yoffset = 0; + finfo.line_length = line_length; + finfo.smem_len = finfo.line_length * vinfo.yres; +#else /* USE_BSD_FBDEV */ + + // Get fixed screen information + if(ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo) == -1) { + perror("Error reading fixed information"); + return; + } + + // Get variable screen information + if(ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo) == -1) { + perror("Error reading variable information"); + return; + } +#endif /* USE_BSD_FBDEV */ + + printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel); + + // Figure out the size of the screen in bytes + screensize = finfo.smem_len; //finfo.line_length * vinfo.yres; + + // Map the device to memory + fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0); + if((intptr_t)fbp == -1) { + perror("Error: failed to map framebuffer device to memory"); + return; + } + memset(fbp, 0, screensize); + + printf("The framebuffer device was mapped to memory successfully.\n"); + +} + +void fbdev_exit(void) +{ + close(fbfd); +} + +/** + * Flush a buffer to the marked area + * @param drv pointer to driver where this function belongs + * @param area an area where to copy `color_p` + * @param color_p an array of pixel to copy to the `area` part of the screen + */ +void fbdev_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p) +{ + if(fbp == NULL || + area->x2 < 0 || + area->y2 < 0 || + area->x1 > (int32_t)vinfo.xres - 1 || + area->y1 > (int32_t)vinfo.yres - 1) { + lv_disp_flush_ready(drv); + return; + } + + /*Truncate the area to the screen*/ + int32_t act_x1 = area->x1 < 0 ? 0 : area->x1; + int32_t act_y1 = area->y1 < 0 ? 0 : area->y1; + int32_t act_x2 = area->x2 > (int32_t)vinfo.xres - 1 ? (int32_t)vinfo.xres - 1 : area->x2; + int32_t act_y2 = area->y2 > (int32_t)vinfo.yres - 1 ? (int32_t)vinfo.yres - 1 : area->y2; + + + lv_coord_t w = (act_x2 - act_x1 + 1); + long int location = 0; + long int byte_location = 0; + unsigned char bit_location = 0; + + /*32 or 24 bit per pixel*/ + if(vinfo.bits_per_pixel == 32 || vinfo.bits_per_pixel == 24) { + uint32_t * fbp32 = (uint32_t *)fbp; + int32_t y; + for(y = act_y1; y <= act_y2; y++) { + location = (act_x1 + vinfo.xoffset) + (y + vinfo.yoffset) * finfo.line_length / 4; + memcpy(&fbp32[location], (uint32_t *)color_p, (act_x2 - act_x1 + 1) * 4); + color_p += w; + } + } + /*16 bit per pixel*/ + else if(vinfo.bits_per_pixel == 16) { + uint16_t * fbp16 = (uint16_t *)fbp; + int32_t y; + for(y = act_y1; y <= act_y2; y++) { + location = (act_x1 + vinfo.xoffset) + (y + vinfo.yoffset) * finfo.line_length / 2; + memcpy(&fbp16[location], (uint32_t *)color_p, (act_x2 - act_x1 + 1) * 2); + color_p += w; + } + } + /*8 bit per pixel*/ + else if(vinfo.bits_per_pixel == 8) { + uint8_t * fbp8 = (uint8_t *)fbp; + int32_t y; + for(y = act_y1; y <= act_y2; y++) { + location = (act_x1 + vinfo.xoffset) + (y + vinfo.yoffset) * finfo.line_length; + memcpy(&fbp8[location], (uint32_t *)color_p, (act_x2 - act_x1 + 1)); + color_p += w; + } + } + /*1 bit per pixel*/ + else if(vinfo.bits_per_pixel == 1) { + uint8_t * fbp8 = (uint8_t *)fbp; + int32_t x; + int32_t y; + for(y = act_y1; y <= act_y2; y++) { + for(x = act_x1; x <= act_x2; x++) { + location = (x + vinfo.xoffset) + (y + vinfo.yoffset) * vinfo.xres; + byte_location = location / 8; /* find the byte we need to change */ + bit_location = location % 8; /* inside the byte found, find the bit we need to change */ + fbp8[byte_location] &= ~(((uint8_t)(1)) << bit_location); + fbp8[byte_location] |= ((uint8_t)(color_p->full)) << bit_location; + color_p++; + } + + color_p += area->x2 - act_x2; + } + } else { + /*Not supported bit per pixel*/ + } + + //May be some direct update command is required + //ret = ioctl(state->fd, FBIO_UPDATE, (unsigned long)((uintptr_t)rect)); + + lv_disp_flush_ready(drv); +} + +void fbdev_get_sizes(uint32_t *width, uint32_t *height) { + if (width) + *width = vinfo.xres; + + if (height) + *height = vinfo.yres; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif diff --git a/lv_drivers/display/fbdev.h b/lv_drivers/display/fbdev.h new file mode 100644 index 0000000..6d265ad --- /dev/null +++ b/lv_drivers/display/fbdev.h @@ -0,0 +1,59 @@ +/** + * @file fbdev.h + * + */ + +#ifndef FBDEV_H +#define FBDEV_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifndef LV_DRV_NO_CONF +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_drv_conf.h" +#else +#include "../../lv_drv_conf.h" +#endif +#endif + +#if USE_FBDEV || USE_BSD_FBDEV + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void fbdev_init(void); +void fbdev_exit(void); +void fbdev_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p); +void fbdev_get_sizes(uint32_t *width, uint32_t *height); + + +/********************** + * MACROS + **********************/ + +#endif /*USE_FBDEV*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*FBDEV_H*/ diff --git a/lv_drivers/display/monitor.h b/lv_drivers/display/monitor.h new file mode 100644 index 0000000..6ef4d6d --- /dev/null +++ b/lv_drivers/display/monitor.h @@ -0,0 +1,57 @@ +/** + * @file monitor.h + * + */ + +#ifndef MONITOR_H +#define MONITOR_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifndef LV_DRV_NO_CONF +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_drv_conf.h" +#else +#include "../../lv_drv_conf.h" +#endif +#endif + +#if USE_MONITOR + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void monitor_init(void); +void monitor_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p); +void monitor_flush2(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p); + +/********************** + * MACROS + **********************/ + +#endif /* USE_MONITOR */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* MONITOR_H */ diff --git a/lv_drivers/docs/astyle_c b/lv_drivers/docs/astyle_c new file mode 100644 index 0000000..9b9d7f3 --- /dev/null +++ b/lv_drivers/docs/astyle_c @@ -0,0 +1 @@ +--style=kr --convert-tabs --indent=spaces=4 --indent-switches --pad-oper --unpad-paren --align-pointer=middle --suffix=.bak --lineend=linux --min-conditional-indent= diff --git a/lv_drivers/docs/astyle_h b/lv_drivers/docs/astyle_h new file mode 100644 index 0000000..d9c7633 --- /dev/null +++ b/lv_drivers/docs/astyle_h @@ -0,0 +1 @@ +--convert-tabs --indent=spaces=4 diff --git a/lv_drivers/gtkdrv/README.md b/lv_drivers/gtkdrv/README.md new file mode 100644 index 0000000..b7a24f1 --- /dev/null +++ b/lv_drivers/gtkdrv/README.md @@ -0,0 +1,97 @@ +# Add GTK under Linux in Eclipse + +## Install GDK + +``` +sudo apt-get install libgtk-3-dev +sudo apt-get install libglib2.0-dev +``` + +## Add GTK include paths and libraries + +In "Project properties > C/C++ Build > Settings" set the followings: + +- "Cross GCC Compiler > Command line pattern" + - Add ` ${gtk+-cflags}` to the end (add a space between the last command and this) + +- "Cross GCC Compiler > Includes" + - /usr/include/glib-2.0 + - /usr/include/gtk-3.0 + - /usr/include/pango-1.0 + - /usr/include/cairo + - /usr/include/gdk-pixbuf-2.0 + - /usr/include/atk-1.0 + +- "Cross GCC Linker > Command line pattern" + - Add ` ${gtk+-libs}` to the end (add a space between the last command and this) + + +- "Cross GCC Linker > Libraries" + - Add `pthread` + + +- In "C/C++ Build > Build variables" + - Configuration: [All Configuration] + + - Add + - Variable name: `gtk+-cflags` + - Type: `String` + - Value: `pkg-config --cflags gtk+-3.0` + - Variable name: `gtk+-libs` + - Type: `String` + - Value: `pkg-config --libs gtk+-3.0` + + +## Init GDK in LVGL + +1. In `main.c` `#incude "lv_drivers/gdkdrv/gdkdrv.h"` +2. Enable the GTK driver in `lv_drv_conf.h` with `USE_GTK 1` +3. After `lv_init()` call `gdkdrv_init()`; +4. Add a display: +```c + static lv_disp_buf_t disp_buf1; + static lv_color_t buf1_1[LV_HOR_RES_MAX * LV_VER_RES_MAX]; + lv_disp_buf_init(&disp_buf1, buf1_1, NULL, LV_HOR_RES_MAX * LV_VER_RES_MAX); + + /*Create a display*/ + lv_disp_drv_t disp_drv; + lv_disp_drv_init(&disp_drv); + disp_drv.buffer = &disp_buf1; + disp_drv.flush_cb = gtkdrv_flush_cb; +``` +5. Add mouse: +```c + lv_indev_drv_t indev_drv_mouse; + lv_indev_drv_init(&indev_drv_mouse); + indev_drv_mouse.type = LV_INDEV_TYPE_POINTER; +``` +6. Akk keyboard: +```c + lv_indev_drv_t indev_drv_kb; + lv_indev_drv_init(&indev_drv_kb); + indev_drv_kb.type = LV_INDEV_TYPE_KEYPAD; + indev_drv_kb.read_cb = lv_keyboard_read_cb; + lv_indev_drv_register(&indev_drv_kb); +``` +7. Configure tick in `lv_conf.h` +```c +#define LV_TICK_CUSTOM 1 +#if LV_TICK_CUSTOM == 1 +#define LV_TICK_CUSTOM_INCLUDE "lv_drivers/gtkdrv/gtkdrv.h" /*Header for the sys time function*/ +#define LV_TICK_CUSTOM_SYS_TIME_EXPR (gtkdrv_tick_get()) /*Expression evaluating to current systime in ms*/ +#endif /*LV_TICK_CUSTOM*/ +``` +8. Be sure `LV_COLOR_DEPTH` is `32` in `lv_conf.h` + +## Run in a window +Build and Run to "normally" run the UI in a window + +## Run in browser +With the help of `Broadway` the UI can be easily shown via a browser. + +1. Open Terminal and start *Broadway* with `broadwayd :5`. Leave the terminal running. +2. Navigate to where eclipse created the binary executable (my_project/Debug) and open a terminal in that folder. +In this terminal run `GDK_BACKEND=broadway BROADWAY_DISPLAY=:5 ./my_executable` (replace *my_executable* wih name of your executable) +3. Open a web browser and go to `http://localhost:8085/` + +![LVGL with GTK/GDK Broadway backend](https://github.com/lvgl/lv_drivers/blob/master/gtkdrv/broadway.png?raw=true) diff --git a/lv_drivers/gtkdrv/broadway.png b/lv_drivers/gtkdrv/broadway.png new file mode 100644 index 0000000..e0448ff Binary files /dev/null and b/lv_drivers/gtkdrv/broadway.png differ diff --git a/lv_drivers/gtkdrv/gtkdrv.c b/lv_drivers/gtkdrv/gtkdrv.c new file mode 100644 index 0000000..1c200ac --- /dev/null +++ b/lv_drivers/gtkdrv/gtkdrv.c @@ -0,0 +1,327 @@ +/** + * @file gtk.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "gtkdrv.h" + +#if USE_GTK +#define _DEFAULT_SOURCE /* needed for usleep() */ +#include +#include +#include +#include +#include +#include +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void gtkdrv_handler(void * p); +static gboolean mouse_pressed(GtkWidget *widget, GdkEventButton *event, + gpointer user_data); +static gboolean mouse_released(GtkWidget *widget, GdkEventButton *event, + gpointer user_data); +static gboolean mouse_motion(GtkWidget *widget, GdkEventMotion *event, + gpointer user_data); +static gboolean keyboard_press(GtkWidget *widget, GdkEventKey *event, + gpointer user_data); +static gboolean keyboard_release(GtkWidget *widget, GdkEventKey *event, + gpointer user_data); + +static void quit_handler(void); + +/********************** + * STATIC VARIABLES + **********************/ +static GtkWidget *window; +static GtkWidget *event_box; + +static GtkWidget *output_image; +static GdkPixbuf *pixbuf; + +static unsigned char run_gtk; + +static lv_coord_t mouse_x; +static lv_coord_t mouse_y; +static lv_indev_state_t mouse_btn = LV_INDEV_STATE_REL; +static lv_key_t last_key; +static lv_indev_state_t last_key_state; + +static uint8_t fb[LV_HOR_RES_MAX * LV_VER_RES_MAX * 3]; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void gtkdrv_init(void) +{ + // Init GTK + gtk_init(NULL, NULL); + + /* Or just set up the widgets in code */ + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_default_size(GTK_WINDOW(window), LV_HOR_RES_MAX, LV_VER_RES_MAX); + gtk_window_set_resizable (GTK_WINDOW(window), FALSE); + output_image = gtk_image_new(); + event_box = gtk_event_box_new (); // Use event_box around image, otherwise mouse position output in broadway is offset + gtk_container_add(GTK_CONTAINER (event_box), output_image); + gtk_container_add(GTK_CONTAINER (window), event_box); + + gtk_widget_add_events(event_box, GDK_BUTTON_PRESS_MASK); + gtk_widget_add_events(event_box, GDK_SCROLL_MASK); + gtk_widget_add_events(event_box, GDK_POINTER_MOTION_MASK); + gtk_widget_add_events(window, GDK_KEY_PRESS_MASK); + + g_signal_connect(window, "destroy", G_CALLBACK(quit_handler), NULL); + g_signal_connect(event_box, "button-press-event", G_CALLBACK(mouse_pressed), NULL); + g_signal_connect(event_box, "button-release-event", G_CALLBACK(mouse_released), NULL); + g_signal_connect(event_box, "motion-notify-event", G_CALLBACK(mouse_motion), NULL); + g_signal_connect(window, "key_press_event", G_CALLBACK(keyboard_press), NULL); + g_signal_connect(window, "key_release_event", G_CALLBACK(keyboard_release), NULL); + + + gtk_widget_show_all(window); + + pixbuf = gdk_pixbuf_new_from_data((guchar*)fb, GDK_COLORSPACE_RGB, false, 8, LV_HOR_RES_MAX, LV_VER_RES_MAX, LV_HOR_RES_MAX * 3, NULL, NULL); + if (pixbuf == NULL) + { + fprintf(stderr, "Creating pixbuf failed\n"); + return; + } + + pthread_t thread; + pthread_create(&thread, NULL, gtkdrv_handler, NULL); +} + + +/*Set in lv_conf.h as `LV_TICK_CUSTOM_SYS_TIME_EXPR`*/ +uint32_t gtkdrv_tick_get(void) +{ + static uint64_t start_ms = 0; + if(start_ms == 0) { + struct timeval tv_start; + gettimeofday(&tv_start, NULL); + start_ms = (tv_start.tv_sec * 1000000 + tv_start.tv_usec) / 1000; + } + + struct timeval tv_now; + gettimeofday(&tv_now, NULL); + uint64_t now_ms; + now_ms = (tv_now.tv_sec * 1000000 + tv_now.tv_usec) / 1000; + + uint32_t time_ms = now_ms - start_ms; + + return time_ms; +} + + +/** + * Flush a buffer to the marked area + * @param drv pointer to driver where this function belongs + * @param area an area where to copy `color_p` + * @param color_p an array of pixel to copy to the `area` part of the screen + */ +void gtkdrv_flush_cb(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) +{ + lv_coord_t hres = disp_drv->rotated == 0 ? disp_drv->hor_res : disp_drv->ver_res; + lv_coord_t vres = disp_drv->rotated == 0 ? disp_drv->ver_res : disp_drv->hor_res; + + + /*Return if the area is out the screen*/ + if(area->x2 < 0 || area->y2 < 0 || area->x1 > hres - 1 || area->y1 > vres - 1) { + + lv_disp_flush_ready(disp_drv); + return; + } + + int32_t y; + int32_t x; + int32_t p; + for(y = area->y1; y <= area->y2 && y < disp_drv->ver_res; y++) { + p = (y * disp_drv->hor_res + area->x1) * 3; + for(x = area->x1; x <= area->x2 && x < disp_drv->hor_res; x++) { + fb[p] = color_p->ch.red; + fb[p + 1] = color_p->ch.green; + fb[p + 2] = color_p->ch.blue; + + p += 3; + color_p ++; + } + } + + /*IMPORTANT! It must be called to tell the system the flush is ready*/ + lv_disp_flush_ready(disp_drv); +} + + +bool gtkdrv_mouse_read_cb(lv_indev_drv_t * drv, lv_indev_data_t * data) +{ + data->point.x = mouse_x; + data->point.y = mouse_y; + data->state = mouse_btn; + + return false; +} + + +bool gtkdrv_keyboard_read_cb(lv_indev_drv_t * drv, lv_indev_data_t * data) +{ + data->key = last_key; + data->state = last_key_state; + + return false; +} + + + + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void gtkdrv_handler(void * p) +{ + while(1) { + gtk_image_set_from_pixbuf(GTK_IMAGE(output_image), pixbuf); // Test code + + /* Real code should: call gdk_pixbuf_new_from_data () with pointer to frame buffer + generated by LVGL. See + https://developer.gnome.org/gdk-pixbuf/2.36/gdk-pixbuf-Image-Data-in-Memory.html + */ + + gtk_main_iteration_do(FALSE); + /* Explicitly calling each iteration of the GTK main loop allows LVGL to sync frame + buffer updates with GTK. It is perhaps also possible to just call gtk_main(), but not + sure how sync will work then + */ + usleep(1*1000); + } +} + +static gboolean mouse_pressed(GtkWidget *widget, GdkEventButton *event, + gpointer user_data) +{ + mouse_btn = LV_INDEV_STATE_PR; + // Important, if this function returns TRUE the window cannot be moved around inside the browser + // when using broadway + return FALSE; +} + + +static gboolean mouse_released(GtkWidget *widget, GdkEventButton *event, + gpointer user_data) +{ + mouse_btn = LV_INDEV_STATE_REL; + // Important, if this function returns TRUE the window cannot be moved around inside the browser + // when using broadway + return FALSE; +} + +/*****************************************************************************/ + +static gboolean mouse_motion(GtkWidget *widget, GdkEventMotion *event, + gpointer user_data) +{ + mouse_x = event->x; + mouse_y = event->y; + // Important, if this function returns TRUE the window cannot be moved around inside the browser + // when using broadway + return FALSE; +} + + +static gboolean keyboard_press(GtkWidget *widget, GdkEventKey *event, + gpointer user_data) +{ + + uint32_t ascii_key = event->keyval; + /*Remap some key to LV_KEY_... to manage groups*/ + switch(event->keyval) { + case GDK_KEY_rightarrow: + case GDK_KEY_Right: + ascii_key = LV_KEY_RIGHT; + break; + + case GDK_KEY_leftarrow: + case GDK_KEY_Left: + ascii_key = LV_KEY_LEFT; + break; + + case GDK_KEY_uparrow: + case GDK_KEY_Up: + ascii_key = LV_KEY_UP; + break; + + case GDK_KEY_downarrow: + case GDK_KEY_Down: + ascii_key = LV_KEY_DOWN; + break; + + case GDK_KEY_Escape: + ascii_key = LV_KEY_ESC; + break; + + case GDK_KEY_BackSpace: + ascii_key = LV_KEY_BACKSPACE; + break; + + case GDK_KEY_Delete: + ascii_key = LV_KEY_DEL; + break; + + case GDK_KEY_Tab: + ascii_key = LV_KEY_NEXT; + break; + + case GDK_KEY_KP_Enter: + case GDK_KEY_Return: + case '\r': + ascii_key = LV_KEY_ENTER; + break; + + default: + break; + + } + + last_key = ascii_key; + last_key_state = LV_INDEV_STATE_PR; + // For other codes refer to https://developer.gnome.org/gdk3/stable/gdk3-Event-Structures.html#GdkEventKey + + return TRUE; +} + +static gboolean keyboard_release(GtkWidget *widget, GdkEventKey *event, + gpointer user_data) +{ + last_key = 0; + last_key_state = LV_INDEV_STATE_REL; + // For other codes refer to https://developer.gnome.org/gdk3/stable/gdk3-Event-Structures.html#GdkEventKey + + return TRUE; +} + +static void quit_handler(void) +{ + exit(0); + run_gtk = FALSE; +} +#endif /*USE_GTK*/ + diff --git a/lv_drivers/gtkdrv/gtkdrv.h b/lv_drivers/gtkdrv/gtkdrv.h new file mode 100644 index 0000000..33c67eb --- /dev/null +++ b/lv_drivers/gtkdrv/gtkdrv.h @@ -0,0 +1,59 @@ +/** + * @file gtkdrv + * + */ + +#ifndef GTKDRV_H +#define GTKDRV_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifndef LV_DRV_NO_CONF +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_drv_conf.h" +#else +#include "../../lv_drv_conf.h" +#endif +#endif + +#if USE_GTK + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void gtkdrv_init(void); +uint32_t gtkdrv_tick_get(void); +void gtkdrv_flush_cb(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p); +bool gtkdrv_mouse_read_cb(lv_indev_drv_t * drv, lv_indev_data_t * data); +bool gtkdrv_keyboard_read_cb(lv_indev_drv_t * drv, lv_indev_data_t * data); +/********************** + * MACROS + **********************/ + +#endif /*USE_GTK*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* GTKDRV_H */ diff --git a/lv_drivers/indev/AD_touch.c b/lv_drivers/indev/AD_touch.c new file mode 100644 index 0000000..c09c359 --- /dev/null +++ b/lv_drivers/indev/AD_touch.c @@ -0,0 +1,383 @@ +/** + * @file AD_touch.c + * + */ + +#include "AD_touch.h" + +#if USE_AD_TOUCH + +#include LV_DRV_INDEV_INCLUDE +#include LV_DRV_DELAY_INCLUDE + +#define SAMPLE_POINTS 4 + +#define CALIBRATIONINSET 20 // range 0 <= CALIBRATIONINSET <= 40 + +#define RESISTIVETOUCH_AUTO_SAMPLE_MODE +#define TOUCHSCREEN_RESISTIVE_PRESS_THRESHOLD 350 // between 0-0x03ff the lesser this value + + +// Current ADC values for X and Y channels +int16_t adcX = 0; +int16_t adcY = 0; +volatile unsigned int adcTC = 0; + +// coefficient values +volatile long _trA; +volatile long _trB; +volatile long _trC; +volatile long _trD; + +volatile int16_t xRawTouch[SAMPLE_POINTS] = {TOUCHCAL_ULX, TOUCHCAL_URX, TOUCHCAL_LRX, TOUCHCAL_LLX}; +volatile int16_t yRawTouch[SAMPLE_POINTS] = {TOUCHCAL_ULY, TOUCHCAL_URY, TOUCHCAL_LRY, TOUCHCAL_LLY}; + +#define TOUCHSCREEN_RESISTIVE_CALIBRATION_SCALE_FACTOR 8 + +// use this scale factor to avoid working in floating point numbers +#define SCALE_FACTOR (1 << TOUCHSCREEN_RESISTIVE_CALIBRATION_SCALE_FACTOR) + +typedef enum { + IDLE, //0 + SET_X, //1 + RUN_X, //2 + GET_X, //3 + RUN_CHECK_X, //4 + CHECK_X, //5 + SET_Y, //6 + RUN_Y, //7 + GET_Y, //8 + CHECK_Y, //9 + SET_VALUES, //10 + GET_POT, //11 + RUN_POT //12 +} TOUCH_STATES; + +volatile TOUCH_STATES state = IDLE; + +#define CAL_X_INSET (((GetMaxX() + 1) * (CALIBRATIONINSET >> 1)) / 100) +#define CAL_Y_INSET (((GetMaxY() + 1) * (CALIBRATIONINSET >> 1)) / 100) + +int stat; +int16_t temp_x, temp_y; + + +static int16_t TouchGetX(void); +static int16_t TouchGetRawX(void); +static int16_t TouchGetY(void); +static int16_t TouchGetRawY(void); +static int16_t TouchDetectPosition(void); +static void TouchCalculateCalPoints(void); + + +/********************************************************************/ +void ad_touch_init(void) +{ + // Initialize ADC for auto sampling mode + AD1CON1 = 0; // reset + AD1CON2 = 0; // AVdd, AVss, int every conversion, MUXA only + AD1CON3 = 0x1FFF; // 31 Tad auto-sample, Tad = 256*Tcy + AD1CON1 = 0x80E0; // Turn on A/D module, use auto-convert + + + ADPCFG_XPOS = RESISTIVETOUCH_ANALOG; + ADPCFG_YPOS = RESISTIVETOUCH_ANALOG; + + AD1CSSL = 0; // No scanned inputs + + state = SET_X; // set the state of the state machine to start the sampling + + /*Load calibration data*/ + xRawTouch[0] = TOUCHCAL_ULX; + yRawTouch[0] = TOUCHCAL_ULY; + xRawTouch[1] = TOUCHCAL_URX; + yRawTouch[1] = TOUCHCAL_URY; + xRawTouch[3] = TOUCHCAL_LLX; + yRawTouch[3] = TOUCHCAL_LLY; + xRawTouch[2] = TOUCHCAL_LRX; + yRawTouch[2] = TOUCHCAL_LRY; + + TouchCalculateCalPoints(); +} + +/*Use this in lv_indev_drv*/ +bool ad_touch_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data) +{ + static int16_t last_x = 0; + static int16_t last_y = 0; + + int16_t x, y; + + x = TouchGetX(); + y = TouchGetY(); + + if((x > 0) && (y > 0)) { + data->point.x = x; + data->point.y = y; + last_x = data->point.x; + last_y = data->point.y; + data->state = LV_INDEV_STATE_PR; + } else { + data->point.x = last_x; + data->point.y = last_y; + data->state = LV_INDEV_STATE_REL; + } + + return false; +} + +/* Call periodically (e.g. in every 1 ms) to handle reading with ADC*/ +int16_t ad_touch_handler(void) +{ + static int16_t tempX, tempY; + int16_t temp; + + switch(state) { + case IDLE: + adcX = 0; + adcY = 0; + break; + + case SET_VALUES: + if(!TOUCH_ADC_DONE) + break; + if((WORD)TOUCHSCREEN_RESISTIVE_PRESS_THRESHOLD < (WORD)ADC1BUF0) { + adcX = 0; + adcY = 0; + } else { + adcX = tempX; + adcY = tempY; + } + state = SET_X; + return 1; // touch screen acquisition is done + + case SET_X: + TOUCH_ADC_INPUT_SEL = ADC_XPOS; + + ResistiveTouchScreen_XPlus_Config_As_Input(); + ResistiveTouchScreen_YPlus_Config_As_Input(); + ResistiveTouchScreen_XMinus_Config_As_Input(); + ResistiveTouchScreen_YMinus_Drive_Low(); + ResistiveTouchScreen_YMinus_Config_As_Output(); + + ADPCFG_YPOS = RESISTIVETOUCH_DIGITAL; // set to digital pin + ADPCFG_XPOS = RESISTIVETOUCH_ANALOG; // set to analog pin + + TOUCH_ADC_START = 1; // run conversion + state = CHECK_X; + break; + + case CHECK_X: + case CHECK_Y: + + if(TOUCH_ADC_DONE == 0) { + break; + } + + if((WORD)TOUCHSCREEN_RESISTIVE_PRESS_THRESHOLD > (WORD)ADC1BUF0) { + if(state == CHECK_X) { + ResistiveTouchScreen_YPlus_Drive_High(); + ResistiveTouchScreen_YPlus_Config_As_Output(); + tempX = 0; + state = RUN_X; + } else { + ResistiveTouchScreen_XPlus_Drive_High(); + ResistiveTouchScreen_XPlus_Config_As_Output(); + tempY = 0; + state = RUN_Y; + } + } else { + adcX = 0; + adcY = 0; + state = SET_X; + return 1; // touch screen acquisition is done + break; + } + + case RUN_X: + case RUN_Y: + TOUCH_ADC_START = 1; + state = (state == RUN_X) ? GET_X : GET_Y; + // no break needed here since the next state is either GET_X or GET_Y + break; + + case GET_X: + case GET_Y: + if(!TOUCH_ADC_DONE) + break; + + temp = ADC1BUF0; + if(state == GET_X) { + if(temp != tempX) { + tempX = temp; + state = RUN_X; + break; + } + } else { + if(temp != tempY) { + tempY = temp; + state = RUN_Y; + break; + } + } + + if(state == GET_X) + ResistiveTouchScreen_YPlus_Config_As_Input(); + else + ResistiveTouchScreen_XPlus_Config_As_Input(); + TOUCH_ADC_START = 1; + state = (state == GET_X) ? SET_Y : SET_VALUES; + break; + + case SET_Y: + if(!TOUCH_ADC_DONE) + break; + + if((WORD)TOUCHSCREEN_RESISTIVE_PRESS_THRESHOLD < (WORD)ADC1BUF0) { + adcX = 0; + adcY = 0; + state = SET_X; + return 1; // touch screen acquisition is done + break; + } + + TOUCH_ADC_INPUT_SEL = ADC_YPOS; + + ResistiveTouchScreen_XPlus_Config_As_Input(); + ResistiveTouchScreen_YPlus_Config_As_Input(); + ResistiveTouchScreen_XMinus_Drive_Low(); + ResistiveTouchScreen_XMinus_Config_As_Output(); + ResistiveTouchScreen_YMinus_Config_As_Input(); + + ADPCFG_YPOS = RESISTIVETOUCH_ANALOG; // set to analog pin + ADPCFG_XPOS = RESISTIVETOUCH_DIGITAL; // set to digital pin + TOUCH_ADC_START = 1; // run conversion + state = CHECK_Y; + break; + + default: + state = SET_X; + return 1; // touch screen acquisition is done + } + stat = state; + temp_x = adcX; + temp_y = adcY; + + return 0; // touch screen acquisition is not done +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/********************************************************************/ +static int16_t TouchGetX(void) +{ + long result; + + result = TouchGetRawX(); + + if(result > 0) { + result = (long)((((long)_trC * result) + _trD) >> TOUCHSCREEN_RESISTIVE_CALIBRATION_SCALE_FACTOR); + + } + return ((int16_t)result); +} +/********************************************************************/ +static int16_t TouchGetRawX(void) +{ +#ifdef TOUCHSCREEN_RESISTIVE_SWAP_XY + return adcY; +#else + return adcX; +#endif +} + +/********************************************************************/ +static int16_t TouchGetY(void) +{ + + long result; + + result = TouchGetRawY(); + + if(result > 0) { + result = (long)((((long)_trA * result) + (long)_trB) >> TOUCHSCREEN_RESISTIVE_CALIBRATION_SCALE_FACTOR); + + } + return ((int16_t)result); +} + +/********************************************************************/ +static int16_t TouchGetRawY(void) +{ +#ifdef TOUCHSCREEN_RESISTIVE_SWAP_XY + return adcX; +#else + return adcY; +#endif +} + + +static void TouchCalculateCalPoints(void) +{ + long trA, trB, trC, trD; // variables for the coefficients + long trAhold, trBhold, trChold, trDhold; + long test1, test2; // temp variables (must be signed type) + + int16_t xPoint[SAMPLE_POINTS], yPoint[SAMPLE_POINTS]; + + yPoint[0] = yPoint[1] = CAL_Y_INSET; + yPoint[2] = yPoint[3] = (GetMaxY() - CAL_Y_INSET); + xPoint[0] = xPoint[3] = CAL_X_INSET; + xPoint[1] = xPoint[2] = (GetMaxX() - CAL_X_INSET); + + // calculate points transfer functiona + // based on two simultaneous equations solve for the + // constants + + // use sample points 1 and 4 + // Dy1 = aTy1 + b; Dy4 = aTy4 + b + // Dx1 = cTx1 + d; Dy4 = aTy4 + b + + test1 = (long)yPoint[0] - (long)yPoint[3]; + test2 = (long)yRawTouch[0] - (long)yRawTouch[3]; + + trA = ((long)((long)test1 * SCALE_FACTOR) / test2); + trB = ((long)((long)yPoint[0] * SCALE_FACTOR) - (trA * (long)yRawTouch[0])); + + test1 = (long)xPoint[0] - (long)xPoint[2]; + test2 = (long)xRawTouch[0] - (long)xRawTouch[2]; + + trC = ((long)((long)test1 * SCALE_FACTOR) / test2); + trD = ((long)((long)xPoint[0] * SCALE_FACTOR) - (trC * (long)xRawTouch[0])); + + trAhold = trA; + trBhold = trB; + trChold = trC; + trDhold = trD; + + // use sample points 2 and 3 + // Dy2 = aTy2 + b; Dy3 = aTy3 + b + // Dx2 = cTx2 + d; Dy3 = aTy3 + b + + test1 = (long)yPoint[1] - (long)yPoint[2]; + test2 = (long)yRawTouch[1] - (long)yRawTouch[2]; + + trA = ((long)(test1 * SCALE_FACTOR) / test2); + trB = ((long)((long)yPoint[1] * SCALE_FACTOR) - (trA * (long)yRawTouch[1])); + + test1 = (long)xPoint[1] - (long)xPoint[3]; + test2 = (long)xRawTouch[1] - (long)xRawTouch[3]; + + trC = ((long)((long)test1 * SCALE_FACTOR) / test2); + trD = ((long)((long)xPoint[1] * SCALE_FACTOR) - (trC * (long)xRawTouch[1])); + + // get the average and use the average + _trA = (trA + trAhold) >> 1; + _trB = (trB + trBhold) >> 1; + _trC = (trC + trChold) >> 1; + _trD = (trD + trDhold) >> 1; +} + +#endif diff --git a/lv_drivers/indev/AD_touch.h b/lv_drivers/indev/AD_touch.h new file mode 100644 index 0000000..7c07ab3 --- /dev/null +++ b/lv_drivers/indev/AD_touch.h @@ -0,0 +1,120 @@ +/** + * @file AD_touch.h + * + */ + +#ifndef AD_TOUCH_H +#define AD_TOUCH_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifndef LV_DRV_NO_CONF +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_drv_conf.h" +#else +#include "../../lv_drv_conf.h" +#endif +#endif + +#if USE_AD_TOUCH + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +#define _SUPPRESS_PLIB_WARNING +#include + +#include "GenericTypeDefs.h" + +#define DISP_ORIENTATION 0 +#define DISP_HOR_RESOLUTION 320 +#define DISP_VER_RESOLUTION 240 + +/*GetMaxX Macro*/ +#if (DISP_ORIENTATION == 90) || (DISP_ORIENTATION == 270) +#define GetMaxX() (DISP_VER_RESOLUTION - 1) +#elif (DISP_ORIENTATION == 0) || (DISP_ORIENTATION == 180) +#define GetMaxX() (DISP_HOR_RESOLUTION - 1) +#endif + +/*GetMaxY Macro*/ +#if (DISP_ORIENTATION == 90) || (DISP_ORIENTATION == 270) +#define GetMaxY() (DISP_HOR_RESOLUTION - 1) +#elif (DISP_ORIENTATION == 0) || (DISP_ORIENTATION == 180) +#define GetMaxY() (DISP_VER_RESOLUTION - 1) +#endif + +/********************************************************************* + * HARDWARE PROFILE FOR THE RESISTIVE TOUCHSCREEN + *********************************************************************/ + +#define TOUCH_ADC_INPUT_SEL AD1CHS + +// ADC Sample Start +#define TOUCH_ADC_START AD1CON1bits.SAMP + +// ADC Status +#define TOUCH_ADC_DONE AD1CON1bits.DONE + +#define RESISTIVETOUCH_ANALOG 1 +#define RESISTIVETOUCH_DIGITAL 0 + +// ADC channel constants +#define ADC_XPOS ADC_CH0_POS_SAMPLEA_AN12 +#define ADC_YPOS ADC_CH0_POS_SAMPLEA_AN13 + +// ADC Port Control Bits +#define ADPCFG_XPOS AD1PCFGbits.PCFG12 //XR +#define ADPCFG_YPOS AD1PCFGbits.PCFG13 //YD + +// X port definitions +#define ResistiveTouchScreen_XPlus_Drive_High() LATBbits.LATB12 = 1 +#define ResistiveTouchScreen_XPlus_Drive_Low() LATBbits.LATB12 = 0 //LAT_XPOS +#define ResistiveTouchScreen_XPlus_Config_As_Input() TRISBbits.TRISB12 = 1 //TRIS_XPOS +#define ResistiveTouchScreen_XPlus_Config_As_Output() TRISBbits.TRISB12 = 0 + +#define ResistiveTouchScreen_XMinus_Drive_High() LATFbits.LATF0 = 1 +#define ResistiveTouchScreen_XMinus_Drive_Low() LATFbits.LATF0 = 0 //LAT_XNEG +#define ResistiveTouchScreen_XMinus_Config_As_Input() TRISFbits.TRISF0 = 1 //TRIS_XNEG +#define ResistiveTouchScreen_XMinus_Config_As_Output() TRISFbits.TRISF0 = 0 + +// Y port definitions +#define ResistiveTouchScreen_YPlus_Drive_High() LATBbits.LATB13 = 1 +#define ResistiveTouchScreen_YPlus_Drive_Low() LATBbits.LATB13 = 0 //LAT_YPOS +#define ResistiveTouchScreen_YPlus_Config_As_Input() TRISBbits.TRISB13 = 1 //TRIS_YPOS +#define ResistiveTouchScreen_YPlus_Config_As_Output() TRISBbits.TRISB13 = 0 + +#define ResistiveTouchScreen_YMinus_Drive_High() LATFbits.LATF1 = 1 +#define ResistiveTouchScreen_YMinus_Drive_Low() LATFbits.LATF1 = 0 //LAT_YNEG +#define ResistiveTouchScreen_YMinus_Config_As_Input() TRISFbits.TRISF1 = 1 //TRIS_YNEG +#define ResistiveTouchScreen_YMinus_Config_As_Output() TRISFbits.TRISF1 = 0 + +// Default calibration points +#define TOUCHCAL_ULX 0x0348 +#define TOUCHCAL_ULY 0x00CC +#define TOUCHCAL_URX 0x00D2 +#define TOUCHCAL_URY 0x00CE +#define TOUCHCAL_LLX 0x034D +#define TOUCHCAL_LLY 0x0335 +#define TOUCHCAL_LRX 0x00D6 +#define TOUCHCAL_LRY 0x032D + +void ad_touch_init(void); +bool ad_touch_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data); +int16_t ad_touch_handler(void); + +#endif /* USE_AD_TOUCH */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* AD_TOUCH_H */ diff --git a/lv_drivers/indev/FT5406EE8.c b/lv_drivers/indev/FT5406EE8.c new file mode 100644 index 0000000..e753a18 --- /dev/null +++ b/lv_drivers/indev/FT5406EE8.c @@ -0,0 +1,179 @@ +/** + * @file FT5406EE8.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "FT5406EE8.h" +#if USE_FT5406EE8 + +#include +#include +#include LV_DRV_INDEV_INCLUDE +#include LV_DRV_DELAY_INCLUDE + +/********************* + * DEFINES + *********************/ + +#define I2C_WR_BIT 0x00 +#define I2C_RD_BIT 0x01 + +/*DEVICE MODES*/ +#define OPERAT_MD 0x00 +#define TEST_MD 0x04 +#define SYS_INF_MD 0x01 + +/*OPERATING MODE*/ +#define DEVICE_MODE 0x00 +#define GEST_ID 0x01 +#define TD_STATUS 0x02 + +#define FT5406EE8_FINGER_MAX 10 + +/*Register adresses*/ +#define FT5406EE8_REG_DEVICE_MODE 0x00 +#define FT5406EE8_REG_GEST_ID 0x01 +#define FT5406EE8_REG_TD_STATUS 0x02 +#define FT5406EE8_REG_YH 0x03 +#define FT5406EE8_REG_YL 0x04 +#define FT5406EE8_REG_XH 0x05 +#define FT5406EE8_REG_XL 0x06 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static bool ft5406ee8_get_touch_num(void); +static bool ft5406ee8_read_finger1(int16_t * x, int16_t * y); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * + */ +void ft5406ee8_init(void) +{ + +} + +/** + * Get the current position and state of the touchpad + * @param data store the read data here + * @return false: because no ore data to be read + */ +bool ft5406ee8_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data) +{ + static int16_t x_last; + static int16_t y_last; + int16_t x; + int16_t y; + bool valid = true; + + valid = ft5406ee8_get_touch_num(); + if(valid == true) { + valid = ft5406ee8_read_finger1(&x, &y); + } + + if(valid == true) { + x = (uint32_t)((uint32_t)x * 320) / 2048; + y = (uint32_t)((uint32_t)y * 240) / 2048; + + + x_last = x; + y_last = y; + } else { + x = x_last; + y = y_last; + } + + data->point.x = x; + data->point.y = y; + data->state = valid == false ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR; + return false; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static bool ft5406ee8_get_touch_num(void) +{ + bool ok = true; + uint8_t t_num = 0; + + LV_DRV_INDEV_I2C_START; + LV_DRV_INDEV_I2C_WR((FT5406EE8_I2C_ADR << 1) | I2C_WR_BIT); + LV_DRV_INDEV_I2C_WR(FT5406EE8_REG_TD_STATUS) + LV_DRV_INDEV_I2C_RESTART; + LV_DRV_INDEV_I2C_WR((FT5406EE8_I2C_ADR << 1) | I2C_RD_BIT); + t_num = LV_DRV_INDEV_I2C_READ(0); + + /* Error if not touched or too much finger */ + if(t_num > FT5406EE8_FINGER_MAX || t_num == 0) { + ok = false; + } + + return ok; +} + +/** + * Read the x and y coordinated + * @param x store the x coordinate here + * @param y store the y coordinate here + * @return false: not valid point; true: valid point + */ +static bool ft5406ee8_read_finger1(int16_t * x, int16_t * y) +{ + uint8_t temp_xH = 0; + uint8_t temp_xL = 0; + uint8_t temp_yH = 0; + uint8_t temp_yL = 0; + + /*Read Y High and low byte*/ + LV_DRV_INDEV_I2C_START; + LV_DRV_INDEV_I2C_WR((FT5406EE8_I2C_ADR << 1) | I2C_WR_BIT); + LV_DRV_INDEV_I2C_WR(FT5406EE8_REG_YH) + LV_DRV_INDEV_I2C_RESTART; + LV_DRV_INDEV_I2C_WR((FT5406EE8_I2C_ADR << 1) | I2C_RD_BIT); + temp_yH = LV_DRV_INDEV_I2C_READ(1); + temp_yL = LV_DRV_INDEV_I2C_READ(1); + + /*The upper two bit must be 2 on valid press*/ + if(((temp_yH >> 6) & 0xFF) != 2) { + (void) LV_DRV_INDEV_I2C_READ(0); /*Dummy read to close read sequence*/ + *x = 0; + *y = 0; + return false; + } + + /*Read X High and low byte*/ + temp_xH = LV_DRV_INDEV_I2C_READ(1); + temp_xL = LV_DRV_INDEV_I2C_READ(0); + + /*Save the result*/ + *x = (temp_xH & 0x0F) << 8; + *x += temp_xL; + *y = (temp_yH & 0x0F) << 8; + *y += temp_yL; + + return true; +} + +#endif diff --git a/lv_drivers/indev/FT5406EE8.h b/lv_drivers/indev/FT5406EE8.h new file mode 100644 index 0000000..2d1eda7 --- /dev/null +++ b/lv_drivers/indev/FT5406EE8.h @@ -0,0 +1,56 @@ +/** + * @file FT5406EE8.h + * + */ + +#ifndef FT5406EE8_H +#define FT5406EE8_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifndef LV_DRV_NO_CONF +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_drv_conf.h" +#else +#include "../../lv_drv_conf.h" +#endif +#endif + +#if USE_FT5406EE8 + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void ft5406ee8_init(void); +bool ft5406ee8_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data); + +/********************** + * MACROS + **********************/ + +#endif /* USE_FT5406EE8 */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* FT5406EE8_H */ diff --git a/lv_drivers/indev/XPT2046.c b/lv_drivers/indev/XPT2046.c new file mode 100644 index 0000000..f27fa76 --- /dev/null +++ b/lv_drivers/indev/XPT2046.c @@ -0,0 +1,174 @@ +/** + * @file XPT2046.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "XPT2046.h" +#if USE_XPT2046 + +#include +#include LV_DRV_INDEV_INCLUDE +#include LV_DRV_DELAY_INCLUDE + +/********************* + * DEFINES + *********************/ +#define CMD_X_READ 0b10010000 +#define CMD_Y_READ 0b11010000 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void xpt2046_corr(int16_t * x, int16_t * y); +static void xpt2046_avg(int16_t * x, int16_t * y); + +/********************** + * STATIC VARIABLES + **********************/ +int16_t avg_buf_x[XPT2046_AVG]; +int16_t avg_buf_y[XPT2046_AVG]; +uint8_t avg_last; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initialize the XPT2046 + */ +void xpt2046_init(void) +{ + +} + +/** + * Get the current position and state of the touchpad + * @param data store the read data here + * @return false: because no ore data to be read + */ +bool xpt2046_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data) +{ + static int16_t last_x = 0; + static int16_t last_y = 0; + uint8_t buf; + + int16_t x = 0; + int16_t y = 0; + + uint8_t irq = LV_DRV_INDEV_IRQ_READ; + + if(irq == 0) { + LV_DRV_INDEV_SPI_CS(0); + + LV_DRV_INDEV_SPI_XCHG_BYTE(CMD_X_READ); /*Start x read*/ + + buf = LV_DRV_INDEV_SPI_XCHG_BYTE(0); /*Read x MSB*/ + x = buf << 8; + buf = LV_DRV_INDEV_SPI_XCHG_BYTE(CMD_Y_READ); /*Until x LSB converted y command can be sent*/ + x += buf; + + buf = LV_DRV_INDEV_SPI_XCHG_BYTE(0); /*Read y MSB*/ + y = buf << 8; + + buf = LV_DRV_INDEV_SPI_XCHG_BYTE(0); /*Read y LSB*/ + y += buf; + + /*Normalize Data*/ + x = x >> 3; + y = y >> 3; + xpt2046_corr(&x, &y); + xpt2046_avg(&x, &y); + + last_x = x; + last_y = y; + data->state = LV_INDEV_STATE_PR; + + LV_DRV_INDEV_SPI_CS(1); + } else { + x = last_x; + y = last_y; + avg_last = 0; + data->state = LV_INDEV_STATE_REL; + } + + data->point.x = x; + data->point.y = y; + + return false; +} + +/********************** + * STATIC FUNCTIONS + **********************/ +static void xpt2046_corr(int16_t * x, int16_t * y) +{ +#if XPT2046_XY_SWAP != 0 + int16_t swap_tmp; + swap_tmp = *x; + *x = *y; + *y = swap_tmp; +#endif + + if((*x) > XPT2046_X_MIN)(*x) -= XPT2046_X_MIN; + else(*x) = 0; + + if((*y) > XPT2046_Y_MIN)(*y) -= XPT2046_Y_MIN; + else(*y) = 0; + + (*x) = (uint32_t)((uint32_t)(*x) * XPT2046_HOR_RES) / + (XPT2046_X_MAX - XPT2046_X_MIN); + + (*y) = (uint32_t)((uint32_t)(*y) * XPT2046_VER_RES) / + (XPT2046_Y_MAX - XPT2046_Y_MIN); + +#if XPT2046_X_INV != 0 + (*x) = XPT2046_HOR_RES - (*x); +#endif + +#if XPT2046_Y_INV != 0 + (*y) = XPT2046_VER_RES - (*y); +#endif + + +} + + +static void xpt2046_avg(int16_t * x, int16_t * y) +{ + /*Shift out the oldest data*/ + uint8_t i; + for(i = XPT2046_AVG - 1; i > 0 ; i--) { + avg_buf_x[i] = avg_buf_x[i - 1]; + avg_buf_y[i] = avg_buf_y[i - 1]; + } + + /*Insert the new point*/ + avg_buf_x[0] = *x; + avg_buf_y[0] = *y; + if(avg_last < XPT2046_AVG) avg_last++; + + /*Sum the x and y coordinates*/ + int32_t x_sum = 0; + int32_t y_sum = 0; + for(i = 0; i < avg_last ; i++) { + x_sum += avg_buf_x[i]; + y_sum += avg_buf_y[i]; + } + + /*Normalize the sums*/ + (*x) = (int32_t)x_sum / avg_last; + (*y) = (int32_t)y_sum / avg_last; +} + +#endif diff --git a/lv_drivers/indev/XPT2046.h b/lv_drivers/indev/XPT2046.h new file mode 100644 index 0000000..7eee8c0 --- /dev/null +++ b/lv_drivers/indev/XPT2046.h @@ -0,0 +1,56 @@ +/** + * @file XPT2046.h + * + */ + +#ifndef XPT2046_H +#define XPT2046_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifndef LV_DRV_NO_CONF +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_drv_conf.h" +#else +#include "../../lv_drv_conf.h" +#endif +#endif + +#if USE_XPT2046 + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void xpt2046_init(void); +bool xpt2046_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data); + +/********************** + * MACROS + **********************/ + +#endif /* USE_XPT2046 */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* XPT2046_H */ diff --git a/lv_drivers/indev/evdev.c b/lv_drivers/indev/evdev.c new file mode 100644 index 0000000..975a479 --- /dev/null +++ b/lv_drivers/indev/evdev.c @@ -0,0 +1,244 @@ +/** + * @file evdev.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "evdev.h" +#if USE_EVDEV != 0 || USE_BSD_EVDEV + +#include +#include +#include +#if USE_BSD_EVDEV +#include +#else +#include +#endif + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +int map(int x, int in_min, int in_max, int out_min, int out_max); + +/********************** + * STATIC VARIABLES + **********************/ +int evdev_fd; +int evdev_root_x; +int evdev_root_y; +int evdev_button; + +int evdev_key_val; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initialize the evdev interface + */ +void evdev_init(void) +{ +#if USE_BSD_EVDEV + evdev_fd = open(EVDEV_NAME, O_RDWR | O_NOCTTY); +#else + evdev_fd = open(EVDEV_NAME, O_RDWR | O_NOCTTY | O_NDELAY); +#endif + if(evdev_fd == -1) { + perror("unable open evdev interface:"); + return; + } + +#if USE_BSD_EVDEV + fcntl(evdev_fd, F_SETFL, O_NONBLOCK); +#else + fcntl(evdev_fd, F_SETFL, O_ASYNC | O_NONBLOCK); +#endif + + evdev_root_x = 0; + evdev_root_y = 0; + evdev_key_val = 0; + evdev_button = LV_INDEV_STATE_REL; +} +/** + * reconfigure the device file for evdev + * @param dev_name set the evdev device filename + * @return true: the device file set complete + * false: the device file doesn't exist current system + */ +bool evdev_set_file(char* dev_name) +{ + if(evdev_fd != -1) { + close(evdev_fd); + } +#if USE_BSD_EVDEV + evdev_fd = open(dev_name, O_RDWR | O_NOCTTY); +#else + evdev_fd = open(dev_name, O_RDWR | O_NOCTTY | O_NDELAY); +#endif + + if(evdev_fd == -1) { + perror("unable open evdev interface:"); + return false; + } + +#if USE_BSD_EVDEV + fcntl(evdev_fd, F_SETFL, O_NONBLOCK); +#else + fcntl(evdev_fd, F_SETFL, O_ASYNC | O_NONBLOCK); +#endif + + evdev_root_x = 0; + evdev_root_y = 0; + evdev_key_val = 0; + evdev_button = LV_INDEV_STATE_REL; + + return true; +} +/** + * Get the current position and state of the evdev + * @param data store the evdev data here + * @return false: because the points are not buffered, so no more data to be read + */ +bool evdev_read(lv_indev_drv_t * drv, lv_indev_data_t * data) +{ + struct input_event in; + + while(read(evdev_fd, &in, sizeof(struct input_event)) > 0) { + if(in.type == EV_REL) { + if(in.code == REL_X) + #if EVDEV_SWAP_AXES + evdev_root_y += in.value; + #else + evdev_root_x += in.value; + #endif + else if(in.code == REL_Y) + #if EVDEV_SWAP_AXES + evdev_root_x += in.value; + #else + evdev_root_y += in.value; + #endif + } else if(in.type == EV_ABS) { + if(in.code == ABS_X) + #if EVDEV_SWAP_AXES + evdev_root_y = in.value; + #else + evdev_root_x = in.value; + #endif + else if(in.code == ABS_Y) + #if EVDEV_SWAP_AXES + evdev_root_x = in.value; + #else + evdev_root_y = in.value; + #endif + else if(in.code == ABS_MT_POSITION_X) + #if EVDEV_SWAP_AXES + evdev_root_y = in.value; + #else + evdev_root_x = in.value; + #endif + else if(in.code == ABS_MT_POSITION_Y) + #if EVDEV_SWAP_AXES + evdev_root_x = in.value; + #else + evdev_root_y = in.value; + #endif + else if(in.code == ABS_MT_TRACKING_ID) + if(in.value == -1) + evdev_button = LV_INDEV_STATE_REL; + else if(in.value == 0) + evdev_button = LV_INDEV_STATE_PR; + } else if(in.type == EV_KEY) { + if(in.code == BTN_MOUSE || in.code == BTN_TOUCH) { + if(in.value == 0) + evdev_button = LV_INDEV_STATE_REL; + else if(in.value == 1) + evdev_button = LV_INDEV_STATE_PR; + } else if(drv->type == LV_INDEV_TYPE_KEYPAD) { + data->state = (in.value) ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL; + switch(in.code) { + case KEY_BACKSPACE: + data->key = LV_KEY_BACKSPACE; + break; + case KEY_ENTER: + data->key = LV_KEY_ENTER; + break; + case KEY_UP: + data->key = LV_KEY_UP; + break; + case KEY_LEFT: + data->key = LV_KEY_PREV; + break; + case KEY_RIGHT: + data->key = LV_KEY_NEXT; + break; + case KEY_DOWN: + data->key = LV_KEY_DOWN; + break; + default: + data->key = 0; + break; + } + evdev_key_val = data->key; + evdev_button = data->state; + return false; + } + } + } + + if(drv->type == LV_INDEV_TYPE_KEYPAD) { + /* No data retrieved */ + data->key = evdev_key_val; + data->state = evdev_button; + return false; + } + if(drv->type != LV_INDEV_TYPE_POINTER) + return false; + /*Store the collected data*/ + +#if EVDEV_CALIBRATE + data->point.x = map(evdev_root_x, EVDEV_HOR_MIN, EVDEV_HOR_MAX, 0, drv->disp->driver.hor_res); + data->point.y = map(evdev_root_y, EVDEV_VER_MIN, EVDEV_VER_MAX, 0, drv->disp->driver.ver_res); +#else + data->point.x = evdev_root_x; + data->point.y = evdev_root_y; +#endif + + data->state = evdev_button; + + if(data->point.x < 0) + data->point.x = 0; + if(data->point.y < 0) + data->point.y = 0; + if(data->point.x >= drv->disp->driver.hor_res) + data->point.x = drv->disp->driver.hor_res - 1; + if(data->point.y >= drv->disp->driver.ver_res) + data->point.y = drv->disp->driver.ver_res - 1; + + return false; +} + +/********************** + * STATIC FUNCTIONS + **********************/ +int map(int x, int in_min, int in_max, int out_min, int out_max) +{ + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} + +#endif diff --git a/lv_drivers/indev/evdev.h b/lv_drivers/indev/evdev.h new file mode 100644 index 0000000..dcb9114 --- /dev/null +++ b/lv_drivers/indev/evdev.h @@ -0,0 +1,73 @@ +/** + * @file evdev.h + * + */ + +#ifndef EVDEV_H +#define EVDEV_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifndef LV_DRV_NO_CONF +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_drv_conf.h" +#else +#include "../../lv_drv_conf.h" +#endif +#endif + +#if USE_EVDEV || USE_BSD_EVDEV + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Initialize the evdev + */ +void evdev_init(void); +/** + * reconfigure the device file for evdev + * @param dev_name set the evdev device filename + * @return true: the device file set complete + * false: the device file doesn't exist current system + */ +bool evdev_set_file(char* dev_name); +/** + * Get the current position and state of the evdev + * @param data store the evdev data here + * @return false: because the points are not buffered, so no more data to be read + */ +bool evdev_read(lv_indev_drv_t * drv, lv_indev_data_t * data); + + +/********************** + * MACROS + **********************/ + +#endif /* USE_EVDEV */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* EVDEV_H */ diff --git a/lv_drivers/indev/keyboard.c b/lv_drivers/indev/keyboard.c new file mode 100644 index 0000000..0c08685 --- /dev/null +++ b/lv_drivers/indev/keyboard.c @@ -0,0 +1,134 @@ +/** + * @file sdl_kb.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "keyboard.h" +#if USE_KEYBOARD + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static uint32_t keycode_to_ascii(uint32_t sdl_key); + +/********************** + * STATIC VARIABLES + **********************/ +static uint32_t last_key; +static lv_indev_state_t state; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initialize the keyboard + */ +void keyboard_init(void) +{ + /*Nothing to init*/ +} + +/** + * Get the last pressed or released character from the PC's keyboard + * @param indev_drv pointer to the related input device driver + * @param data store the read data here + * @return false: because the points are not buffered, so no more data to be read + */ +void keyboard_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data) +{ + (void) indev_drv; /*Unused*/ + data->state = state; + data->key = keycode_to_ascii(last_key); +} + +/** + * It is called periodically from the SDL thread to check a key is pressed/released + * @param event describes the event + */ +void keyboard_handler(SDL_Event * event) +{ + /* We only care about SDL_KEYDOWN and SDL_KEYUP events */ + switch(event->type) { + case SDL_KEYDOWN: /*Button press*/ + last_key = event->key.keysym.sym; /*Save the pressed key*/ + state = LV_INDEV_STATE_PRESSED; /*Save the key is pressed now*/ + break; + case SDL_KEYUP: /*Button release*/ + state = LV_INDEV_STATE_RELEASED; /*Save the key is released but keep the last key*/ + break; + default: + break; + + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Convert the key code LV_KEY_... "codes" or leave them if they are not control characters + * @param sdl_key the key code + * @return + */ +static uint32_t keycode_to_ascii(uint32_t sdl_key) +{ + /*Remap some key to LV_KEY_... to manage groups*/ + switch(sdl_key) { + case SDLK_RIGHT: + case SDLK_KP_PLUS: + return LV_KEY_RIGHT; + + case SDLK_LEFT: + case SDLK_KP_MINUS: + return LV_KEY_LEFT; + + case SDLK_UP: + return LV_KEY_UP; + + case SDLK_DOWN: + return LV_KEY_DOWN; + + case SDLK_ESCAPE: + return LV_KEY_ESC; + +#ifdef LV_KEY_BACKSPACE /*For backward compatibility*/ + case SDLK_BACKSPACE: + return LV_KEY_BACKSPACE; +#endif + +#ifdef LV_KEY_DEL /*For backward compatibility*/ + case SDLK_DELETE: + return LV_KEY_DEL; +#endif + case SDLK_KP_ENTER: + case '\r': + return LV_KEY_ENTER; + + case SDLK_PAGEDOWN: + return LV_KEY_NEXT; + + case SDLK_PAGEUP: + return LV_KEY_PREV; + + default: + return sdl_key; + } +} +#endif diff --git a/lv_drivers/indev/keyboard.h b/lv_drivers/indev/keyboard.h new file mode 100644 index 0000000..50afd1d --- /dev/null +++ b/lv_drivers/indev/keyboard.h @@ -0,0 +1,77 @@ +/** + * @file keyboard.h + * + */ + +#ifndef KEYBOARD_H +#define KEYBOARD_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifndef LV_DRV_NO_CONF +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_drv_conf.h" +#else +#include "../../lv_drv_conf.h" +#endif +#endif + +#if USE_KEYBOARD + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +#ifndef MONITOR_SDL_INCLUDE_PATH +#define MONITOR_SDL_INCLUDE_PATH +#endif + +#include MONITOR_SDL_INCLUDE_PATH + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +/** + * Initialize the keyboard + */ +void keyboard_init(void); + +/** + * Get the last pressed or released character from the PC's keyboard + * @param indev_drv pointer to the related input device driver + * @param data store the read data here + */ +void keyboard_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data); + +/** + * It is called periodically from the SDL thread to check a key is pressed/released + * @param event describes the event + */ +void keyboard_handler(SDL_Event *event); + +/********************** + * MACROS + **********************/ + +#endif /*USE_KEYBOARD*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*KEYBOARD_H*/ diff --git a/lv_drivers/indev/libinput.c b/lv_drivers/indev/libinput.c new file mode 100644 index 0000000..98dfd7d --- /dev/null +++ b/lv_drivers/indev/libinput.c @@ -0,0 +1,175 @@ +/** + * @file libinput.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "libinput_drv.h" +#if USE_LIBINPUT != 0 + +#include +#include +#include +#include +#include +#include +#include +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static int open_restricted(const char *path, int flags, void *user_data); +static void close_restricted(int fd, void *user_data); + +/********************** + * STATIC VARIABLES + **********************/ +static int libinput_fd; +static int libinput_button; +static const int timeout = 0; // do not block +static const nfds_t nfds = 1; +static struct pollfd fds[1]; +static lv_point_t most_recent_touch_point = { .x = 0, .y = 0}; + +static struct libinput *libinput_context; +static struct libinput_device *libinput_device; +const static struct libinput_interface interface = { + .open_restricted = open_restricted, + .close_restricted = close_restricted, +}; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * reconfigure the device file for libinput + * @param dev_name set the libinput device filename + * @return true: the device file set complete + * false: the device file doesn't exist current system + */ +bool libinput_set_file(char* dev_name) +{ + // This check *should* not be necessary, yet applications crashes even on NULL handles. + // citing libinput.h:libinput_path_remove_device: + // > If no matching device exists, this function does nothing. + if (libinput_device) { + libinput_device = libinput_device_unref(libinput_device); + libinput_path_remove_device(libinput_device); + } + + libinput_device = libinput_path_add_device(libinput_context, dev_name); + if(!libinput_device) { + perror("unable to add device to libinput context:"); + return false; + } + libinput_device = libinput_device_ref(libinput_device); + if(!libinput_device) { + perror("unable to reference device within libinput context:"); + return false; + } + + libinput_button = LV_INDEV_STATE_REL; + + return true; +} + +/** + * Initialize the libinput interface + */ +void libinput_init(void) +{ + libinput_device = NULL; + libinput_context = libinput_path_create_context(&interface, NULL); + if(!libinput_set_file(LIBINPUT_NAME)) { + perror("unable to add device \"" LIBINPUT_NAME "\" to libinput context:"); + return; + } + libinput_fd = libinput_get_fd(libinput_context); + + /* prepare poll */ + fds[0].fd = libinput_fd; + fds[0].events = POLLIN; + fds[0].revents = 0; +} + +/** + * Get the current position and state of the libinput + * @param indev_drv driver object itself + * @param data store the libinput data here + * @return false: because the points are not buffered, so no more data to be read + */ +bool libinput_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data) +{ + struct libinput_event *event; + struct libinput_event_touch *touch_event = NULL; + int rc = 0; + + rc = poll(fds, nfds, timeout); + switch (rc){ + case -1: + perror(NULL); + case 0: + goto report_most_recent_state; + default: + break; + } + libinput_dispatch(libinput_context); + while((event = libinput_get_event(libinput_context)) != NULL) { + enum libinput_event_type type = libinput_event_get_type(event); + switch (type) { + case LIBINPUT_EVENT_TOUCH_MOTION: + case LIBINPUT_EVENT_TOUCH_DOWN: + touch_event = libinput_event_get_touch_event(event); + most_recent_touch_point.x = libinput_event_touch_get_x_transformed(touch_event, LV_HOR_RES); + most_recent_touch_point.y = libinput_event_touch_get_y_transformed(touch_event, LV_VER_RES); + libinput_button = LV_INDEV_STATE_PR; + break; + case LIBINPUT_EVENT_TOUCH_UP: + libinput_button = LV_INDEV_STATE_REL; + break; + default: + break; + } + libinput_event_destroy(event); + } +report_most_recent_state: + data->point.x = most_recent_touch_point.x; + data->point.y = most_recent_touch_point.y; + data->state = libinput_button; + + return false; +} + + +/********************** + * STATIC FUNCTIONS + **********************/ + +static int open_restricted(const char *path, int flags, void *user_data) +{ + int fd = open(path, flags); + return fd < 0 ? -errno : fd; +} + +static void close_restricted(int fd, void *user_data) +{ + close(fd); +} + +#endif diff --git a/lv_drivers/indev/libinput_drv.h b/lv_drivers/indev/libinput_drv.h new file mode 100644 index 0000000..ae1ee42 --- /dev/null +++ b/lv_drivers/indev/libinput_drv.h @@ -0,0 +1,74 @@ +/** + * @file libinput.h + * + */ + +#ifndef LVGL_LIBINPUT_H +#define LVGL_LIBINPUT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifndef LV_DRV_NO_CONF +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_drv_conf.h" +#else +#include "../../lv_drv_conf.h" +#endif +#endif + +#if USE_LIBINPUT + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Initialize the libinput + */ +void libinput_init(void); +/** + * reconfigure the device file for libinput + * @param dev_name set the libinput device filename + * @return true: the device file set complete + * false: the device file doesn't exist current system + */ +bool libinput_set_file(char* dev_name); +/** + * Get the current position and state of the libinput + * @param indev_drv driver object itself + * @param data store the libinput data here + * @return false: because the points are not buffered, so no more data to be read + */ +bool libinput_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data); + + +/********************** + * MACROS + **********************/ + +#endif /* USE_LIBINPUT */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LVGL_LIBINPUT_H */ diff --git a/lv_drivers/indev/mouse.c b/lv_drivers/indev/mouse.c new file mode 100644 index 0000000..e7bcdbc --- /dev/null +++ b/lv_drivers/indev/mouse.c @@ -0,0 +1,109 @@ +/** + * @file mouse.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "mouse.h" +#if USE_MOUSE != 0 + +/********************* + * DEFINES + *********************/ +#ifndef MONITOR_ZOOM +#define MONITOR_ZOOM 1 +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ +static bool left_button_down = false; +static int16_t last_x = 0; +static int16_t last_y = 0; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initialize the mouse + */ +void mouse_init(void) +{ + +} + +/** + * Get the current position and state of the mouse + * @param indev_drv pointer to the related input device driver + * @param data store the mouse data here + */ +void mouse_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data) +{ + (void) indev_drv; /*Unused*/ + + /*Store the collected data*/ + data->point.x = last_x; + data->point.y = last_y; + data->state = left_button_down ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; +} + +/** + * It will be called from the main SDL thread + */ +void mouse_handler(SDL_Event * event) +{ + switch(event->type) { + case SDL_MOUSEBUTTONUP: + if(event->button.button == SDL_BUTTON_LEFT) + left_button_down = false; + break; + case SDL_MOUSEBUTTONDOWN: + if(event->button.button == SDL_BUTTON_LEFT) { + left_button_down = true; + last_x = event->motion.x / MONITOR_ZOOM; + last_y = event->motion.y / MONITOR_ZOOM; + } + break; + case SDL_MOUSEMOTION: + last_x = event->motion.x / MONITOR_ZOOM; + last_y = event->motion.y / MONITOR_ZOOM; + break; + + case SDL_FINGERUP: + left_button_down = false; + last_x = LV_HOR_RES * event->tfinger.x / MONITOR_ZOOM; + last_y = LV_VER_RES * event->tfinger.y / MONITOR_ZOOM; + break; + case SDL_FINGERDOWN: + left_button_down = true; + last_x = LV_HOR_RES * event->tfinger.x / MONITOR_ZOOM; + last_y = LV_VER_RES * event->tfinger.y / MONITOR_ZOOM; + break; + case SDL_FINGERMOTION: + last_x = LV_HOR_RES * event->tfinger.x / MONITOR_ZOOM; + last_y = LV_VER_RES * event->tfinger.y / MONITOR_ZOOM; + break; + } + +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif diff --git a/lv_drivers/indev/mouse.h b/lv_drivers/indev/mouse.h new file mode 100644 index 0000000..5e56f49 --- /dev/null +++ b/lv_drivers/indev/mouse.h @@ -0,0 +1,77 @@ +/** + * @file mouse.h + * + */ + +#ifndef MOUSE_H +#define MOUSE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifndef LV_DRV_NO_CONF +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_drv_conf.h" +#else +#include "../../lv_drv_conf.h" +#endif +#endif + +#if USE_MOUSE + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +#ifndef MONITOR_SDL_INCLUDE_PATH +#define MONITOR_SDL_INCLUDE_PATH +#endif + +#include MONITOR_SDL_INCLUDE_PATH + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Initialize the mouse + */ +void mouse_init(void); + +/** + * Get the current position and state of the mouse + * @param indev_drv pointer to the related input device driver + * @param data store the mouse data here + */ +void mouse_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data); + +/** + * It will be called from the main SDL thread + */ +void mouse_handler(SDL_Event *event); + +/********************** + * MACROS + **********************/ + +#endif /* USE_MOUSE */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* MOUSE_H */ diff --git a/lv_drivers/indev/mousewheel.c b/lv_drivers/indev/mousewheel.c new file mode 100644 index 0000000..41a248b --- /dev/null +++ b/lv_drivers/indev/mousewheel.c @@ -0,0 +1,97 @@ +/** + * @file mousewheel.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "mousewheel.h" +#if USE_MOUSEWHEEL + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ +static int16_t enc_diff = 0; +static lv_indev_state_t state = LV_INDEV_STATE_RELEASED; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initialize the mousewheel + */ +void mousewheel_init(void) +{ + /*Nothing to init*/ +} + +/** + * Get encoder (i.e. mouse wheel) ticks difference and pressed state + * @param indev_drv pointer to the related input device driver + * @param data store the read data here + */ +void mousewheel_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data) +{ + (void) indev_drv; /*Unused*/ + + data->state = state; + data->enc_diff = enc_diff; + enc_diff = 0; +} + +/** + * It is called periodically from the SDL thread to check mouse wheel state + * @param event describes the event + */ +void mousewheel_handler(SDL_Event * event) +{ + switch(event->type) { + case SDL_MOUSEWHEEL: + // Scroll down (y = -1) means positive encoder turn, + // so invert it +#ifdef __EMSCRIPTEN__ + /*Escripten scales it wrong*/ + if(event->wheel.y < 0) enc_diff++; + if(event->wheel.y > 0) enc_diff--; +#else + enc_diff = -event->wheel.y; +#endif + break; + case SDL_MOUSEBUTTONDOWN: + if(event->button.button == SDL_BUTTON_MIDDLE) { + state = LV_INDEV_STATE_PRESSED; + } + break; + case SDL_MOUSEBUTTONUP: + if(event->button.button == SDL_BUTTON_MIDDLE) { + state = LV_INDEV_STATE_RELEASED; + } + break; + default: + break; + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif diff --git a/lv_drivers/indev/mousewheel.h b/lv_drivers/indev/mousewheel.h new file mode 100644 index 0000000..3103c36 --- /dev/null +++ b/lv_drivers/indev/mousewheel.h @@ -0,0 +1,78 @@ +/** + * @file mousewheel.h + * + */ + +#ifndef MOUSEWHEEL_H +#define MOUSEWHEEL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifndef LV_DRV_NO_CONF +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_drv_conf.h" +#else +#include "../../lv_drv_conf.h" +#endif +#endif + +#if USE_MOUSEWHEEL + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +#ifndef MONITOR_SDL_INCLUDE_PATH +#define MONITOR_SDL_INCLUDE_PATH +#endif + +#include MONITOR_SDL_INCLUDE_PATH + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Initialize the encoder + */ +void mousewheel_init(void); + +/** + * Get encoder (i.e. mouse wheel) ticks difference and pressed state + * @param indev_drv pointer to the related input device driver + * @param data store the read data here + */ +void mousewheel_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data); + +/** + * It is called periodically from the SDL thread to check a key is pressed/released + * @param event describes the event + */ +void mousewheel_handler(SDL_Event *event); + +/********************** + * MACROS + **********************/ + +#endif /*USE_MOUSEWHEEL*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*MOUSEWHEEL_H*/ diff --git a/lv_drivers/library.json b/lv_drivers/library.json new file mode 100644 index 0000000..f9d5fce --- /dev/null +++ b/lv_drivers/library.json @@ -0,0 +1,13 @@ +{ + "name": "lv_drivers", + "version": "7.11.0", + "keywords": "littlevgl, lvgl, driver, display, touchpad", + "description": "Drivers for LittlevGL graphics library.", + "repository": { + "type": "git", + "url": "https://github.com/littlevgl/lv_drivers.git" + }, + "build": { + "includeDir": "." + } +} diff --git a/lv_drivers/lv_drivers.mk b/lv_drivers/lv_drivers.mk new file mode 100644 index 0000000..a49452e --- /dev/null +++ b/lv_drivers/lv_drivers.mk @@ -0,0 +1,8 @@ +LV_DRIVERS_DIR_NAME ?= lv_drivers + +CSRCS += $(wildcard $(LVGL_DIR)/$(LV_DRIVERS_DIR_NAME)/*.c) +CSRCS += $(wildcard $(LVGL_DIR)/$(LV_DRIVERS_DIR_NAME)/wayland/*.c) +CSRCS += $(wildcard $(LVGL_DIR)/$(LV_DRIVERS_DIR_NAME)/indev/*.c) +CSRCS += $(wildcard $(LVGL_DIR)/$(LV_DRIVERS_DIR_NAME)/gtkdrv/*.c) +CSRCS += $(wildcard $(LVGL_DIR)/$(LV_DRIVERS_DIR_NAME)/display/*.c) + diff --git a/lv_drivers/lv_drv_conf_template.h b/lv_drivers/lv_drv_conf_template.h new file mode 100644 index 0000000..c715919 --- /dev/null +++ b/lv_drivers/lv_drv_conf_template.h @@ -0,0 +1,423 @@ +/** + * @file lv_drv_conf.h + * Configuration file for v8.0.0 + */ + +/* + * COPY THIS FILE AS lv_drv_conf.h + */ + +#if 0 /*Set it to "1" to enable the content*/ + +#ifndef LV_DRV_CONF_H +#define LV_DRV_CONF_H + +#include "lv_conf.h" + +/********************* + * DELAY INTERFACE + *********************/ +#define LV_DRV_DELAY_INCLUDE /*Dummy include by default*/ +#define LV_DRV_DELAY_US(us) /*delay_us(us)*/ /*Delay the given number of microseconds*/ +#define LV_DRV_DELAY_MS(ms) /*delay_ms(ms)*/ /*Delay the given number of milliseconds*/ + +/********************* + * DISPLAY INTERFACE + *********************/ + +/*------------ + * Common + *------------*/ +#define LV_DRV_DISP_INCLUDE /*Dummy include by default*/ +#define LV_DRV_DISP_CMD_DATA(val) /*pin_x_set(val)*/ /*Set the command/data pin to 'val'*/ +#define LV_DRV_DISP_RST(val) /*pin_x_set(val)*/ /*Set the reset pin to 'val'*/ + +/*--------- + * SPI + *---------*/ +#define LV_DRV_DISP_SPI_CS(val) /*spi_cs_set(val)*/ /*Set the SPI's Chip select to 'val'*/ +#define LV_DRV_DISP_SPI_WR_BYTE(data) /*spi_wr(data)*/ /*Write a byte the SPI bus*/ +#define LV_DRV_DISP_SPI_WR_ARRAY(adr, n) /*spi_wr_mem(adr, n)*/ /*Write 'n' bytes to SPI bus from 'adr'*/ + +/*------------------ + * Parallel port + *-----------------*/ +#define LV_DRV_DISP_PAR_CS(val) /*par_cs_set(val)*/ /*Set the Parallel port's Chip select to 'val'*/ +#define LV_DRV_DISP_PAR_SLOW /*par_slow()*/ /*Set low speed on the parallel port*/ +#define LV_DRV_DISP_PAR_FAST /*par_fast()*/ /*Set high speed on the parallel port*/ +#define LV_DRV_DISP_PAR_WR_WORD(data) /*par_wr(data)*/ /*Write a word to the parallel port*/ +#define LV_DRV_DISP_PAR_WR_ARRAY(adr, n) /*par_wr_mem(adr,n)*/ /*Write 'n' bytes to Parallel ports from 'adr'*/ + +/*************************** + * INPUT DEVICE INTERFACE + ***************************/ + +/*---------- + * Common + *----------*/ +#define LV_DRV_INDEV_INCLUDE /*Dummy include by default*/ +#define LV_DRV_INDEV_RST(val) /*pin_x_set(val)*/ /*Set the reset pin to 'val'*/ +#define LV_DRV_INDEV_IRQ_READ 0 /*pn_x_read()*/ /*Read the IRQ pin*/ + +/*--------- + * SPI + *---------*/ +#define LV_DRV_INDEV_SPI_CS(val) /*spi_cs_set(val)*/ /*Set the SPI's Chip select to 'val'*/ +#define LV_DRV_INDEV_SPI_XCHG_BYTE(data) 0 /*spi_xchg(val)*/ /*Write 'val' to SPI and give the read value*/ + +/*--------- + * I2C + *---------*/ +#define LV_DRV_INDEV_I2C_START /*i2c_start()*/ /*Make an I2C start*/ +#define LV_DRV_INDEV_I2C_STOP /*i2c_stop()*/ /*Make an I2C stop*/ +#define LV_DRV_INDEV_I2C_RESTART /*i2c_restart()*/ /*Make an I2C restart*/ +#define LV_DRV_INDEV_I2C_WR(data) /*i2c_wr(data)*/ /*Write a byte to the I1C bus*/ +#define LV_DRV_INDEV_I2C_READ(last_read) 0 /*i2c_rd()*/ /*Read a byte from the I2C bud*/ + + +/********************* + * DISPLAY DRIVERS + *********************/ + +/*------------------- + * Monitor of PC + *-------------------*/ +#ifndef USE_MONITOR +# define USE_MONITOR 0 +#endif + +#if USE_MONITOR +# define MONITOR_HOR_RES 480 +# define MONITOR_VER_RES 320 + +/* Scale window by this factor (useful when simulating small screens) */ +# define MONITOR_ZOOM 1 + +/* Used to test true double buffering with only address changing. + * Use 2 draw buffers, bith with MONITOR_HOR_RES x MONITOR_VER_RES size*/ +# define MONITOR_DOUBLE_BUFFERED 0 + +/*Eclipse: Visual Studio: */ +# define MONITOR_SDL_INCLUDE_PATH + +/*Open two windows to test multi display support*/ +# define MONITOR_DUAL 0 +#endif + +/*----------------------------------- + * Native Windows (including mouse) + *----------------------------------*/ +#ifndef USE_WINDOWS +# define USE_WINDOWS 0 +#endif + +#if USE_WINDOWS +# define WINDOW_HOR_RES 480 +# define WINDOW_VER_RES 320 +#endif + +/*---------------------------- + * Native Windows (win32drv) + *---------------------------*/ +#ifndef USE_WIN32DRV +# define USE_WIN32DRV 0 +#endif + +#if USE_WIN32DRV +/* Scale window by this factor (useful when simulating small screens) */ +# define WIN32DRV_MONITOR_ZOOM 1 +#endif + +/*---------------------------------------- + * GTK drivers (monitor, mouse, keyboard + *---------------------------------------*/ +#ifndef USE_GTK +# define USE_GTK 0 +#endif + +/*---------------------------------------- + * Wayland drivers (monitor, mouse, keyboard, touchscreen) + *---------------------------------------*/ +#ifndef USE_WAYLAND +# define USE_WAYLAND 0 +#endif + +#if USE_WAYLAND +# define WAYLAND_HOR_RES 480 +# define WAYLAND_VER_RES 320 +# define WAYLAND_SURF_TITLE "LVGL" +#endif + +/*---------------- + * SSD1963 + *--------------*/ +#ifndef USE_SSD1963 +# define USE_SSD1963 0 +#endif + +#if USE_SSD1963 +# define SSD1963_HOR_RES LV_HOR_RES +# define SSD1963_VER_RES LV_VER_RES +# define SSD1963_HT 531 +# define SSD1963_HPS 43 +# define SSD1963_LPS 8 +# define SSD1963_HPW 10 +# define SSD1963_VT 288 +# define SSD1963_VPS 12 +# define SSD1963_FPS 4 +# define SSD1963_VPW 10 +# define SSD1963_HS_NEG 0 /*Negative hsync*/ +# define SSD1963_VS_NEG 0 /*Negative vsync*/ +# define SSD1963_ORI 0 /*0, 90, 180, 270*/ +# define SSD1963_COLOR_DEPTH 16 +#endif + +/*---------------- + * R61581 + *--------------*/ +#ifndef USE_R61581 +# define USE_R61581 0 +#endif + +#if USE_R61581 +# define R61581_HOR_RES LV_HOR_RES +# define R61581_VER_RES LV_VER_RES +# define R61581_HSPL 0 /*HSYNC signal polarity*/ +# define R61581_HSL 10 /*HSYNC length (Not Implemented)*/ +# define R61581_HFP 10 /*Horitontal Front poarch (Not Implemented)*/ +# define R61581_HBP 10 /*Horitontal Back poarch (Not Implemented */ +# define R61581_VSPL 0 /*VSYNC signal polarity*/ +# define R61581_VSL 10 /*VSYNC length (Not Implemented)*/ +# define R61581_VFP 8 /*Vertical Front poarch*/ +# define R61581_VBP 8 /*Vertical Back poarch */ +# define R61581_DPL 0 /*DCLK signal polarity*/ +# define R61581_EPL 1 /*ENABLE signal polarity*/ +# define R61581_ORI 0 /*0, 180*/ +# define R61581_LV_COLOR_DEPTH 16 /*Fix 16 bit*/ +#endif + +/*------------------------------ + * ST7565 (Monochrome, low res.) + *-----------------------------*/ +#ifndef USE_ST7565 +# define USE_ST7565 0 +#endif + +#if USE_ST7565 +/*No settings*/ +#endif /*USE_ST7565*/ + +/*------------------------------ + * GC9A01 (color, low res.) + *-----------------------------*/ +#ifndef USE_GC9A01 +# define USE_GC9A01 0 +#endif + +#if USE_GC9A01 +/*No settings*/ +#endif /*USE_GC9A01*/ + +/*------------------------------------------ + * UC1610 (4 gray 160*[104|128]) + * (EA DOGXL160 160x104 tested) + *-----------------------------------------*/ +#ifndef USE_UC1610 +# define USE_UC1610 0 +#endif + +#if USE_UC1610 +# define UC1610_HOR_RES LV_HOR_RES +# define UC1610_VER_RES LV_VER_RES +# define UC1610_INIT_CONTRAST 33 /* init contrast, values in [%] */ +# define UC1610_INIT_HARD_RST 0 /* 1 : hardware reset at init, 0 : software reset */ +# define UC1610_TOP_VIEW 0 /* 0 : Bottom View, 1 : Top View */ +#endif /*USE_UC1610*/ + +/*------------------------------------------------- + * SHARP memory in pixel monochrome display series + * LS012B7DD01 (184x38 pixels.) + * LS013B7DH03 (128x128 pixels.) + * LS013B7DH05 (144x168 pixels.) + * LS027B7DH01 (400x240 pixels.) (tested) + * LS032B7DD02 (336x536 pixels.) + * LS044Q7DH01 (320x240 pixels.) + *------------------------------------------------*/ +#ifndef USE_SHARP_MIP +# define USE_SHARP_MIP 0 +#endif + +#if USE_SHARP_MIP +# define SHARP_MIP_HOR_RES LV_HOR_RES +# define SHARP_MIP_VER_RES LV_VER_RES +# define SHARP_MIP_SOFT_COM_INVERSION 0 +# define SHARP_MIP_REV_BYTE(b) /*((uint8_t) __REV(__RBIT(b)))*/ /*Architecture / compiler dependent byte bits order reverse*/ +#endif /*USE_SHARP_MIP*/ + +/*------------------------------------------------- + * ILI9341 240X320 TFT LCD + *------------------------------------------------*/ +#ifndef USE_ILI9341 +# define USE_ILI9341 0 +#endif + +#if USE_ILI9341 +# define ILI9341_HOR_RES LV_HOR_RES +# define ILI9341_VER_RES LV_VER_RES +# define ILI9341_GAMMA 1 +# define ILI9341_TEARING 0 +#endif /*USE_ILI9341*/ + +/*----------------------------------------- + * Linux frame buffer device (/dev/fbx) + *-----------------------------------------*/ +#ifndef USE_FBDEV +# define USE_FBDEV 0 +#endif + +#if USE_FBDEV +# define FBDEV_PATH "/dev/fb0" +#endif + +/*----------------------------------------- + * FreeBSD frame buffer device (/dev/fbx) + *.........................................*/ +#ifndef USE_BSD_FBDEV +# define USE_BSD_FBDEV 0 +#endif + +#if USE_BSD_FBDEV +# define FBDEV_PATH "/dev/fb0" +#endif + +/*----------------------------------------- + * DRM/KMS device (/dev/dri/cardX) + *-----------------------------------------*/ +#ifndef USE_DRM +# define USE_DRM 0 +#endif + +#if USE_DRM +# define DRM_CARD "/dev/dri/card0" +# define DRM_CONNECTOR_ID -1 /* -1 for the first connected one */ +#endif + +/********************* + * INPUT DEVICES + *********************/ + +/*-------------- + * XPT2046 + *--------------*/ +#ifndef USE_XPT2046 +# define USE_XPT2046 0 +#endif + +#if USE_XPT2046 +# define XPT2046_HOR_RES 480 +# define XPT2046_VER_RES 320 +# define XPT2046_X_MIN 200 +# define XPT2046_Y_MIN 200 +# define XPT2046_X_MAX 3800 +# define XPT2046_Y_MAX 3800 +# define XPT2046_AVG 4 +# define XPT2046_X_INV 0 +# define XPT2046_Y_INV 0 +# define XPT2046_XY_SWAP 0 +#endif + +/*----------------- + * FT5406EE8 + *-----------------*/ +#ifndef USE_FT5406EE8 +# define USE_FT5406EE8 0 +#endif + +#if USE_FT5406EE8 +# define FT5406EE8_I2C_ADR 0x38 /*7 bit address*/ +#endif + +/*--------------- + * AD TOUCH + *--------------*/ +#ifndef USE_AD_TOUCH +# define USE_AD_TOUCH 0 +#endif + +#if USE_AD_TOUCH +/*No settings*/ +#endif + + +/*--------------------------------------- + * Mouse or touchpad on PC (using SDL) + *-------------------------------------*/ +#ifndef USE_MOUSE +# define USE_MOUSE 0 +#endif + +#if USE_MOUSE +/*No settings*/ +#endif + +/*------------------------------------------- + * Mousewheel as encoder on PC (using SDL) + *------------------------------------------*/ +#ifndef USE_MOUSEWHEEL +# define USE_MOUSEWHEEL 0 +#endif + +#if USE_MOUSEWHEEL +/*No settings*/ +#endif + +/*------------------------------------------------- + * Touchscreen as libinput interface (for Linux based systems) + *------------------------------------------------*/ +#ifndef USE_LIBINPUT +# define USE_LIBINPUT 0 +#endif + +#if USE_LIBINPUT +# define LIBINPUT_NAME "/dev/input/event0" /*You can use the "evtest" Linux tool to get the list of devices and test them*/ +#endif /*USE_LIBINPUT*/ + +/*------------------------------------------------- + * Mouse or touchpad as evdev interface (for Linux based systems) + *------------------------------------------------*/ +#ifndef USE_EVDEV +# define USE_EVDEV 0 +#endif + +#ifndef USE_BSD_EVDEV +# define USE_BSD_EVDEV 0 +#endif + +#if USE_EVDEV || USE_BSD_EVDEV +# define EVDEV_NAME "/dev/input/event0" /*You can use the "evtest" Linux tool to get the list of devices and test them*/ +# define EVDEV_SWAP_AXES 0 /*Swap the x and y axes of the touchscreen*/ + +# define EVDEV_CALIBRATE 0 /*Scale and offset the touchscreen coordinates by using maximum and minimum values for each axis*/ + +# if EVDEV_CALIBRATE +# define EVDEV_HOR_MIN 0 /*to invert axis swap EVDEV_XXX_MIN by EVDEV_XXX_MAX*/ +# define EVDEV_HOR_MAX 4096 /*"evtest" Linux tool can help to get the correct calibraion values>*/ +# define EVDEV_VER_MIN 0 +# define EVDEV_VER_MAX 4096 +# endif /*EVDEV_CALIBRATE*/ +#endif /*USE_EVDEV*/ + +/*------------------------------- + * Keyboard of a PC (using SDL) + *------------------------------*/ +#ifndef USE_KEYBOARD +# define USE_KEYBOARD 0 +#endif + +#if USE_KEYBOARD +/*No settings*/ +#endif + +#endif /*LV_DRV_CONF_H*/ + +#endif /*End of "Content enable"*/ diff --git a/lv_drivers/wayland/README.md b/lv_drivers/wayland/README.md new file mode 100644 index 0000000..dc79df6 --- /dev/null +++ b/lv_drivers/wayland/README.md @@ -0,0 +1,109 @@ +# Wayland display and input driver + +Wayland display and input driver, with support for keyboard, mouse and touchscreen. +Keyboard support is based on libxkbcommon. + +> NOTE: current implementation only supports `wl_shell` shell with no decorations. + + +## Install headers and libraries + +### Ubuntu + +``` +sudo apt-get install libwayland-dev libxkbcommon-dev +``` + +### Fedora + +``` +sudo dnf install wayland-devel libxkbcommon-devel +``` + + +## Build configuration under Eclipse + +In "Project properties > C/C++ Build > Settings" set the followings: + +- "Cross GCC Compiler > Command line pattern" + - Add ` ${wayland-cflags}` and ` ${xkbcommon-cflags}` to the end (add a space between the last command and this) + + +- "Cross GCC Linker > Command line pattern" + - Add ` ${wayland-libs}` and ` ${xkbcommon-libs}` to the end (add a space between the last command and this) + + +- "Cross GCC Linker > Libraries" + - Add `pthread` + + +- In "C/C++ Build > Build variables" + - Configuration: [All Configuration] + + - Add + - Variable name: `wayland-cflags` + - Type: `String` + - Value: `pkg-config --cflags wayland-client` + - Variable name: `wayland-libs` + - Type: `String` + - Value: `pkg-config --libs wayland-client` + - Variable name: `xkbcommon-cflags` + - Type: `String` + - Value: `pkg-config --cflags xkbcommon` + - Variable name: `xkbcommon-libs` + - Type: `String` + - Value: `pkg-config --libs xkbcommon` + + +## Init Wayland in LVGL + +1. In `main.c` `#incude "lv_drivers/wayland/wayland.h"` +2. Enable the Wayland driver in `lv_drv_conf.h` with `USE_WAYLAND 1` +3. `LV_COLOR_DEPTH` should be set either to `32` or `16` in `lv_conf.h`; + support for `8` and `1` depends on target platform. +4. After `lv_init()` call `wayland_init()` +5. Before `lv_deinit()` call `wayland_deinit()` +6. Add a display: +```c + static lv_disp_buf_t disp_buf1; + static lv_color_t buf1[LV_HOR_RES_MAX * LV_VER_RES_MAX]; + lv_disp_buf_init(&disp_buf1, buf1, NULL, LV_HOR_RES_MAX * LV_VER_RES_MAX); + + /* Create a display */ + lv_disp_drv_t disp_drv; + lv_disp_drv_init(&disp_drv); + disp_drv.buffer = &disp_buf1; + disp_drv.flush_cb = wayland_flush; +``` +7. Add keyboard: +```c + lv_indev_drv_t indev_drv_kb; + lv_indev_drv_init(&indev_drv_kb); + indev_drv_kb.type = LV_INDEV_TYPE_KEYPAD; + indev_drv_kb.read_cb = wayland_keyboard_read; + lv_indev_drv_register(&indev_drv_kb); +``` +8. Add touchscreen: +```c + lv_indev_drv_t indev_drv_touch; + lv_indev_drv_init(&indev_drv_touch); + indev_drv_touch.type = LV_INDEV_TYPE_POINTER; + indev_drv_touch.read_cb = wayland_touch_read; + lv_indev_drv_register(&indev_drv_touch); +``` +9. Add mouse: +```c + lv_indev_drv_t indev_drv_mouse; + lv_indev_drv_init(&indev_drv_mouse); + indev_drv_mouse.type = LV_INDEV_TYPE_POINTER; + indev_drv_mouse.read_cb = wayland_pointer_read; + lv_indev_drv_register(&indev_drv_mouse); +``` +10. Add mouse wheel as encoder: +```c + lv_indev_drv_t indev_drv_mousewheel; + lv_indev_drv_init(&indev_drv_mousewheel); + indev_drv_mousewheel.type = LV_INDEV_TYPE_ENCODER; + indev_drv_mousewheel.read_cb = wayland_pointeraxis_read; + lv_indev_drv_register(&indev_drv_mousewheel); +``` diff --git a/lv_drivers/wayland/wayland.c b/lv_drivers/wayland/wayland.c new file mode 100644 index 0000000..135d4a9 --- /dev/null +++ b/lv_drivers/wayland/wayland.c @@ -0,0 +1,892 @@ +/** + * @file wayland.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "wayland.h" + +#if USE_WAYLAND + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +struct input { + struct { + lv_coord_t x; + lv_coord_t y; + lv_indev_state_t left_button; + lv_indev_state_t right_button; + lv_indev_state_t wheel_button; + int16_t wheel_diff; + } mouse; + + struct { + lv_key_t key; + lv_indev_state_t state; + } keyboard; + + struct { + lv_coord_t x; + lv_coord_t y; + lv_indev_state_t state; + } touch; +}; + +struct seat { + struct application *application; + struct wl_seat *wl_seat; + struct wl_touch *wl_touch; + struct wl_pointer *wl_pointer; + struct wl_keyboard *wl_keyboard; + + struct { + struct xkb_keymap *keymap; + struct xkb_state *state; + } xkb; +}; + +struct application { + struct wl_display *display; + struct wl_registry *registry; + struct wl_compositor *compositor; + struct wl_shm *shm; + struct wl_buffer *buffer; + struct wl_surface *surface; + + struct wl_shell *shell; + struct wl_shell_surface *shell_surface; + + int width; + int height; + uint32_t format; + void *data; + + struct xkb_context *xkb_context; + struct seat seat; + struct input input; + + pthread_t thread; + pthread_mutex_t mutex; +}; + +/********************** + * STATIC PROTOTYPES + **********************/ +static void * wayland_dispatch_handler(void *data); +static void handle_global(void *data, struct wl_registry *registry, uint32_t name, + const char *interface, uint32_t version); +static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name); + +static void shm_format(void *data, struct wl_shm *wl_shm, uint32_t format); + +static void shell_handle_ping(void *data, struct wl_shell_surface *shell_surface, uint32_t serial); +static void shell_handle_configure(void *data, struct wl_shell_surface *shell_surface, + uint32_t edges, int32_t width, int32_t height); +static void shell_handle_popup_done(void *data, struct wl_shell_surface *shell_surface); +static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability caps); + +static void pointer_handle_enter(void *data, struct wl_pointer *pointer, + uint32_t serial, struct wl_surface *surface, + wl_fixed_t sx, wl_fixed_t sy); +static void pointer_handle_leave(void *data, struct wl_pointer *pointer, + uint32_t serial, struct wl_surface *surface); +static void pointer_handle_motion(void *data, struct wl_pointer *pointer, + uint32_t time, wl_fixed_t sx, wl_fixed_t sy); +static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, uint32_t time, uint32_t button, + uint32_t state); +static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis, wl_fixed_t value); + +static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, + uint32_t format, int fd, uint32_t size); +static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, + uint32_t serial, struct wl_surface *surface, + struct wl_array *keys); +static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard, + uint32_t serial, struct wl_surface *surface); +static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard, + uint32_t serial, uint32_t time, uint32_t key, + uint32_t state); +static void keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard, + uint32_t serial, uint32_t mods_depressed, + uint32_t mods_latched, uint32_t mods_locked, + uint32_t group); + +static void touch_handle_down(void *data, struct wl_touch *wl_touch, + uint32_t serial, uint32_t time, struct wl_surface *surface, + int32_t id, wl_fixed_t x_w, wl_fixed_t y_w); +static void touch_handle_up(void *data, struct wl_touch *wl_touch, + uint32_t serial, uint32_t time, int32_t id); +static void touch_handle_motion(void *data, struct wl_touch *wl_touch, + uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w); +static void touch_handle_frame(void *data, struct wl_touch *wl_touch); +static void touch_handle_cancel(void *data, struct wl_touch *wl_touch); + +static lv_key_t keycode_xkb_to_lv(uint32_t xkb_key); + +/********************** + * STATIC VARIABLES + **********************/ +static const struct wl_registry_listener registry_listener = { + handle_global, + handle_global_remove +}; + +struct wl_shm_listener shm_listener = { + shm_format +}; + +static const struct wl_shell_surface_listener shell_surface_listener = { + shell_handle_ping, + shell_handle_configure, + shell_handle_popup_done +}; + +static const struct wl_seat_listener seat_listener = { + seat_handle_capabilities, +}; + +static const struct wl_pointer_listener pointer_listener = { + pointer_handle_enter, + pointer_handle_leave, + pointer_handle_motion, + pointer_handle_button, + pointer_handle_axis, +}; + +static const struct wl_keyboard_listener keyboard_listener = { + keyboard_handle_keymap, + keyboard_handle_enter, + keyboard_handle_leave, + keyboard_handle_key, + keyboard_handle_modifiers, +}; + +static const struct wl_touch_listener touch_listener = { + touch_handle_down, + touch_handle_up, + touch_handle_motion, + touch_handle_frame, + touch_handle_cancel, +}; + +static struct application application; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ +/** + * Initialize Wayland driver + */ +void wayland_init(void) +{ + struct wl_shm_pool *pool; + + int stride; + int size; + + static const char template[] = "/lvgl-wayland-XXXXXX"; + const char *path; + char *name; + int fd; + int ret; + + // Create XKB context + application.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + assert(application.xkb_context); + if (application.xkb_context == NULL) { + LV_LOG_ERROR("failed to create XKB context\n"); + return; + } + + // Connect to Wayland display + application.display = wl_display_connect(NULL); + assert(application.display); + + // Create compositor surface + application.width = WAYLAND_HOR_RES; + application.height = WAYLAND_VER_RES; + + /* Add registry listener and wait for registry reception */ + application.format = 0xFFFFFFFF; + application.registry = wl_display_get_registry(application.display); + wl_registry_add_listener(application.registry, ®istry_listener, &application); + wl_display_dispatch(application.display); + wl_display_roundtrip(application.display); + + assert(application.format != 0xFFFFFFFF); + if (application.format == 0xFFFFFFFF) { + LV_LOG_ERROR("WL_SHM_FORMAT not available\n"); + return; + } + + // Create buffer + stride = application.width * ((LV_COLOR_DEPTH + 7) / 8); + size = stride * application.height; + + path = getenv("XDG_RUNTIME_DIR"); + if (!path) { + LV_LOG_ERROR("cannot get XDG_RUNTIME_DIR: %s\n", strerror(errno)); + return; + } + + name = malloc(strlen(path) + sizeof(template)); + if (!name) { + LV_LOG_ERROR("cannot malloc name: %s\n", strerror(errno)); + return; + } + + strcpy(name, path); + strcat(name, template); + + fd = mkstemp(name); + if (fd >= 0) { + long flags = fcntl(fd, F_GETFD); + if ((flags == -1) || (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)) { + LV_LOG_ERROR("cannot set FD_CLOEXEC\n"); + close(fd); + fd = -1; + } + unlink(name); + } + + free(name); + + if (fd < 0) { + LV_LOG_ERROR("cannot create tmpfile: %s\n", strerror(errno)); + return; + } + + do { + ret = ftruncate(fd, size); + } while ((ret < 0) && (errno == EINTR)); + if (ret < 0) { + LV_LOG_ERROR("ftruncate failed: %s\n", strerror(errno)); + close(fd); + return; + } + + application.data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (application.data == MAP_FAILED) { + LV_LOG_ERROR("mmap failed: %s\n", strerror(errno)); + close(fd); + return; + } + + pool = wl_shm_create_pool(application.shm, fd, size); + application.buffer = wl_shm_pool_create_buffer(pool, 0, + application.width, application.height, + stride, + application.format); + wl_shm_pool_destroy(pool); + + close(fd); + + // Create compositor surface + application.surface = wl_compositor_create_surface(application.compositor); + wl_surface_set_user_data(application.surface, &application); + + // Create shell surface + application.shell_surface = wl_shell_get_shell_surface(application.shell, application.surface); + assert(application.shell_surface); + + wl_shell_surface_add_listener(application.shell_surface, &shell_surface_listener, &application); + wl_shell_surface_set_toplevel(application.shell_surface); + wl_shell_surface_set_title(application.shell_surface, WAYLAND_SURF_TITLE); + + pthread_mutex_init(&application.mutex, NULL); + pthread_create(&application.thread, NULL, wayland_dispatch_handler, &application); +} + +/** + * De-initialize Wayland driver + */ +void wayland_deinit(void) +{ + pthread_cancel(application.thread); + + pthread_join(application.thread, NULL); + + pthread_mutex_destroy(&application.mutex); + + if (application.shm) { + wl_shm_destroy(application.shm); + } + + if (application.shell) { + wl_shell_destroy(application.shell); + } + + if (application.seat.wl_seat) { + wl_seat_destroy(application.seat.wl_seat); + } + + if (application.compositor) { + wl_compositor_destroy(application.compositor); + } + + wl_registry_destroy(application.registry); + wl_display_flush(application.display); + wl_display_disconnect(application.display); +} + +/** + * Flush a buffer to the marked area + * @param drv pointer to driver where this function belongs + * @param area an area where to copy `color_p` + * @param color_p an array of pixel to copy to the `area` part of the screen + */ +void wayland_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) +{ + lv_coord_t hres = (disp_drv->rotated == 0) ? (disp_drv->hor_res) : (disp_drv->ver_res); + lv_coord_t vres = (disp_drv->rotated == 0) ? (disp_drv->ver_res) : (disp_drv->hor_res); + + /* Return if the area is out the screen */ + if ((area->x2 < 0) || (area->y2 < 0) || (area->x1 > hres - 1) || (area->y1 > vres - 1)) { + lv_disp_flush_ready(disp_drv); + return; + } + + int32_t x; + int32_t y; + for (y = area->y1; y <= area->y2 && y < disp_drv->ver_res; y++) { + for (x = area->x1; x <= area->x2 && x < disp_drv->hor_res; x++) { + int offset = (y * disp_drv->hor_res) + x; +#if (LV_COLOR_DEPTH == 32) + uint32_t * const buf = (uint32_t *)application.data + offset; + *buf = color_p->full; +#elif (LV_COLOR_DEPTH == 16) + uint16_t * const buf = (uint16_t *)application.data + offset; + *buf = color_p->full; +#elif (LV_COLOR_DEPTH == 8) + uint8_t * const buf = (uint8_t *)application.data + offset; + *buf = color_p->full; +#elif (LV_COLOR_DEPTH == 1) + uint8_t * const buf = (uint8_t *)application.data + offset; + *buf = ((0x07 * color_p->ch.red) << 5) | + ((0x07 * color_p->ch.green) << 2) | + ((0x03 * color_p->ch.blue) << 0); +#endif + color_p++; + } + } + + wl_surface_attach(application.surface, application.buffer, 0, 0); + wl_surface_damage(application.surface, area->x1, area->y1, + (area->x2 - area->x1 + 1), (area->y2 - area->y1 + 1)); + wl_surface_commit(application.surface); + + wl_display_flush(application.display); + + lv_disp_flush_ready(disp_drv); +} + +/** + * Read pointer input + * @param drv pointer to driver where this function belongs + * @param data where to store input data + */ +bool wayland_pointer_read(lv_indev_drv_t * drv, lv_indev_data_t * data) +{ + (void) drv; /* Unused */ + + pthread_mutex_lock(&application.mutex); + + data->point.x = application.input.mouse.x; + data->point.y = application.input.mouse.y; + data->state = application.input.mouse.left_button; + + pthread_mutex_unlock(&application.mutex); + + return false; +} + +/** + * Read axis input + * @param drv pointer to driver where this function belongs + * @param data where to store input data + */ +bool wayland_pointeraxis_read(lv_indev_drv_t * drv, lv_indev_data_t * data) +{ + (void) drv; /* Unused */ + + pthread_mutex_lock(&application.mutex); + + data->state = application.input.mouse.wheel_button; + data->enc_diff = application.input.mouse.wheel_diff; + + application.input.mouse.wheel_diff = 0; + + pthread_mutex_unlock(&application.mutex); + + return false; +} + +/** + * Read keyboard input + * @param drv pointer to driver where this function belongs + * @param data where to store input data + */ +bool wayland_keyboard_read(lv_indev_drv_t * drv, lv_indev_data_t * data) +{ + (void) drv; /* Unused */ + + pthread_mutex_lock(&application.mutex); + + data->key = application.input.keyboard.key; + data->state = application.input.keyboard.state; + + pthread_mutex_unlock(&application.mutex); + + return false; +} + +/** + * Read touch input + * @param drv pointer to driver where this function belongs + * @param data where to store input data + */ +bool wayland_touch_read(lv_indev_drv_t * drv, lv_indev_data_t * data) +{ + (void) drv; /* Unused */ + + pthread_mutex_lock(&application.mutex); + + data->point.x = application.input.touch.x; + data->point.y = application.input.touch.y; + data->state = application.input.touch.state; + + pthread_mutex_unlock(&application.mutex); + + return false; +} + +/********************** + * STATIC FUNCTIONS + **********************/ +static void * wayland_dispatch_handler(void *data) +{ + struct application *app = data; + + while (wl_display_dispatch(app->display) >= 0) { + // Do nothing + } + + return (void *)0; +} + +static void handle_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) +{ + struct application *app = data; + + if (strcmp(interface, "wl_compositor") == 0) { + app->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1); + } else if (strcmp(interface, "wl_shell") == 0) { + app->shell = wl_registry_bind(registry, name, &wl_shell_interface, 1); + } else if (strcmp(interface, "wl_shm") == 0) { + app->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); + wl_shm_add_listener(app->shm, &shm_listener, app); + } else if (strcmp(interface, "wl_seat") == 0) { + app->seat.application = app; + app->seat.wl_seat = wl_registry_bind(app->registry, name, &wl_seat_interface, 1); + wl_seat_add_listener(app->seat.wl_seat, &seat_listener, &app->seat); + } +} + +static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) +{ +} + + +static void shm_format(void *data, struct wl_shm *wl_shm, uint32_t format) +{ + struct application *app = data; + + switch (format) { +#if (LV_COLOR_DEPTH == 32) + case WL_SHM_FORMAT_ARGB8888: + app->format = format; + break; + case WL_SHM_FORMAT_XRGB8888: + if (app->format != WL_SHM_FORMAT_ARGB8888) { + app->format = format; + } + break; +#elif (LV_COLOR_DEPTH == 16) + case WL_SHM_FORMAT_RGB565: + app->format = format; + break; +#elif (LV_COLOR_DEPTH == 8) + case WL_SHM_FORMAT_RGB332: + app->format = format; + break; +#elif (LV_COLOR_DEPTH == 1) + case WL_SHM_FORMAT_RGB332: + app->format = format; + break; +#endif + default: + break; + } +} + +static void shell_handle_ping(void *data, struct wl_shell_surface *shell_surface, uint32_t serial) +{ + wl_shell_surface_pong(shell_surface, serial); +} + +static void shell_handle_configure(void *data, struct wl_shell_surface *shell_surface, + uint32_t edges, int32_t width, int32_t height) +{ +} + +static void shell_handle_popup_done(void *data, struct wl_shell_surface *shell_surface) +{ +} + +static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability caps) +{ + struct seat *seat = data; + + if ((caps & WL_SEAT_CAPABILITY_POINTER) && !seat->wl_pointer) { + seat->wl_pointer = wl_seat_get_pointer(wl_seat); + wl_pointer_add_listener(seat->wl_pointer, &pointer_listener, seat->application); + } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && seat->wl_pointer) { + wl_pointer_destroy(seat->wl_pointer); + seat->wl_pointer = NULL; + } + + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !seat->wl_keyboard) { + seat->wl_keyboard = wl_seat_get_keyboard(wl_seat); + wl_keyboard_add_listener(seat->wl_keyboard, &keyboard_listener, seat->application); + } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && seat->wl_keyboard) { + wl_keyboard_destroy(seat->wl_keyboard); + seat->wl_keyboard = NULL; + } + + if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !seat->wl_touch) { + seat->wl_touch = wl_seat_get_touch(wl_seat); + wl_touch_add_listener(seat->wl_touch, &touch_listener, seat->application); + } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && seat->wl_touch) { + wl_touch_destroy(seat->wl_touch); + seat->wl_touch = NULL; + } +} + +static void pointer_handle_enter(void *data, struct wl_pointer *pointer, + uint32_t serial, struct wl_surface *surface, + wl_fixed_t sx, wl_fixed_t sy) +{ +} + +static void pointer_handle_leave(void *data, struct wl_pointer *pointer, + uint32_t serial, struct wl_surface *surface) +{ +} + +static void pointer_handle_motion(void *data, struct wl_pointer *pointer, + uint32_t time, wl_fixed_t sx, wl_fixed_t sy) +{ + struct application *app = data; + + pthread_mutex_lock(&app->mutex); + + app->input.mouse.x = wl_fixed_to_int(sx); + app->input.mouse.y = wl_fixed_to_int(sy); + + pthread_mutex_unlock(&app->mutex); +} + +static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, uint32_t time, uint32_t button, + uint32_t state) +{ + struct application *app = data; + const lv_indev_state_t lv_state = + (state == WL_POINTER_BUTTON_STATE_PRESSED) ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL; + + pthread_mutex_lock(&app->mutex); + + switch (button & 0xF) { + case 0: + app->input.mouse.left_button = lv_state; + break; + case 1: + app->input.mouse.right_button = lv_state; + break; + case 2: + app->input.mouse.wheel_button = lv_state; + break; + default: + break; + } + + pthread_mutex_unlock(&app->mutex); +} + +static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis, wl_fixed_t value) +{ + struct application *app = data; + const int diff = wl_fixed_to_int(value); + + if (axis == 0) { + pthread_mutex_lock(&app->mutex); + if (diff > 0) { + app->input.mouse.wheel_diff++; + } + else if (diff < 0) { + app->input.mouse.wheel_diff--; + } + pthread_mutex_unlock(&app->mutex); + } +} + +static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, + uint32_t format, int fd, uint32_t size) +{ + struct application *app = data; + + struct xkb_keymap *keymap; + struct xkb_state *state; + char *map_str; + + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { + close(fd); + return; + } + + map_str = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + if (map_str == MAP_FAILED) { + close(fd); + return; + } + + /* Set up XKB keymap */ + keymap = xkb_keymap_new_from_string(app->xkb_context, map_str, + XKB_KEYMAP_FORMAT_TEXT_V1, 0); + munmap(map_str, size); + close(fd); + + if (!keymap) { + LV_LOG_ERROR("failed to compile keymap\n"); + return; + } + + /* Set up XKB state */ + state = xkb_state_new(keymap); + if (!state) { + LV_LOG_ERROR("failed to create XKB state\n"); + xkb_keymap_unref(keymap); + return; + } + + xkb_keymap_unref(app->seat.xkb.keymap); + xkb_state_unref(app->seat.xkb.state); + app->seat.xkb.keymap = keymap; + app->seat.xkb.state = state; +} + +static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, + uint32_t serial, struct wl_surface *surface, + struct wl_array *keys) +{ +} + +static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard, + uint32_t serial, struct wl_surface *surface) +{ +} + +static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard, + uint32_t serial, uint32_t time, uint32_t key, + uint32_t state) +{ + struct application *app = data; + + const uint32_t code = (key + 8); + const xkb_keysym_t *syms; + xkb_keysym_t sym = XKB_KEY_NoSymbol; + + if (!app->seat.xkb.state) + return; + + if (xkb_state_key_get_syms(app->seat.xkb.state, code, &syms) == 1) { + sym = syms[0]; + } + + const lv_key_t lv_key = keycode_xkb_to_lv(sym); + const lv_indev_state_t lv_state = + (state == WL_KEYBOARD_KEY_STATE_PRESSED) ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL; + + if (lv_key != 0) { + pthread_mutex_lock(&app->mutex); + app->input.keyboard.key = lv_key; + app->input.keyboard.state = lv_state; + pthread_mutex_unlock(&app->mutex); + } +} + +static void keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard, + uint32_t serial, uint32_t mods_depressed, + uint32_t mods_latched, uint32_t mods_locked, + uint32_t group) +{ + struct application *app = data; + + /* If we're not using a keymap, then we don't handle PC-style modifiers */ + if (!app->seat.xkb.keymap) + return; + + xkb_state_update_mask(app->seat.xkb.state, + mods_depressed, mods_latched, mods_locked, 0, 0, group); +} + +static void touch_handle_down(void *data, struct wl_touch *wl_touch, + uint32_t serial, uint32_t time, struct wl_surface *surface, + int32_t id, wl_fixed_t x_w, wl_fixed_t y_w) +{ + struct application *app = data; + + pthread_mutex_lock(&app->mutex); + + app->input.touch.x = wl_fixed_to_int(x_w); + app->input.touch.y = wl_fixed_to_int(y_w); + app->input.touch.state = LV_INDEV_STATE_PR; + + pthread_mutex_unlock(&app->mutex); +} + +static void touch_handle_up(void *data, struct wl_touch *wl_touch, + uint32_t serial, uint32_t time, int32_t id) +{ + struct application *app = data; + + pthread_mutex_lock(&app->mutex); + + app->input.touch.state = LV_INDEV_STATE_REL; + + pthread_mutex_unlock(&app->mutex); +} + +static void touch_handle_motion(void *data, struct wl_touch *wl_touch, + uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w) +{ + struct application *app = data; + + pthread_mutex_lock(&app->mutex); + + app->input.touch.x = wl_fixed_to_int(x_w); + app->input.touch.y = wl_fixed_to_int(y_w); + + pthread_mutex_unlock(&app->mutex); +} + +static void touch_handle_frame(void *data, struct wl_touch *wl_touch) +{ +} + +static void touch_handle_cancel(void *data, struct wl_touch *wl_touch) +{ +} + +static lv_key_t keycode_xkb_to_lv(xkb_keysym_t xkb_key) +{ + lv_key_t key = 0; + + if (((xkb_key >= XKB_KEY_space) && (xkb_key <= XKB_KEY_asciitilde))) { + key = xkb_key; + } else if (((xkb_key >= XKB_KEY_KP_0) && (xkb_key <= XKB_KEY_KP_9))) { + key = (xkb_key & 0x003f); + } else { + switch (xkb_key) + { + case XKB_KEY_BackSpace: + key = LV_KEY_BACKSPACE; + break; + case XKB_KEY_Return: + case XKB_KEY_KP_Enter: + key = LV_KEY_ENTER; + break; + case XKB_KEY_Escape: + key = LV_KEY_ESC; + break; + case XKB_KEY_Delete: + case XKB_KEY_KP_Delete: + key = LV_KEY_DEL; + break; + case XKB_KEY_Home: + case XKB_KEY_KP_Home: + key = LV_KEY_HOME; + break; + case XKB_KEY_Left: + case XKB_KEY_KP_Left: + key = LV_KEY_LEFT; + break; + case XKB_KEY_Up: + case XKB_KEY_KP_Up: + key = LV_KEY_UP; + break; + case XKB_KEY_Right: + case XKB_KEY_KP_Right: + key = LV_KEY_RIGHT; + break; + case XKB_KEY_Down: + case XKB_KEY_KP_Down: + key = LV_KEY_DOWN; + break; + case XKB_KEY_Prior: + case XKB_KEY_KP_Prior: + key = LV_KEY_PREV; + break; + case XKB_KEY_Next: + case XKB_KEY_KP_Next: + case XKB_KEY_Tab: + case XKB_KEY_KP_Tab: + key = LV_KEY_NEXT; + break; + case XKB_KEY_End: + case XKB_KEY_KP_End: + key = LV_KEY_END; + break; + default: + break; + } + } + + return key; +} + +#endif /* USE_WAYLAND */ diff --git a/lv_drivers/wayland/wayland.h b/lv_drivers/wayland/wayland.h new file mode 100644 index 0000000..5eab27d --- /dev/null +++ b/lv_drivers/wayland/wayland.h @@ -0,0 +1,62 @@ +/** + * @file wayland + * + */ + +#ifndef WAYLAND_H +#define WAYLAND_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifndef LV_DRV_NO_CONF +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_drv_conf.h" +#else +#include "../../lv_drv_conf.h" +#endif +#endif + +#if USE_WAYLAND + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void wayland_init(void); +void wayland_deinit(void); +void wayland_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p); +bool wayland_pointer_read(lv_indev_drv_t * drv, lv_indev_data_t * data); +bool wayland_pointeraxis_read(lv_indev_drv_t * drv, lv_indev_data_t * data); +bool wayland_keyboard_read(lv_indev_drv_t * drv, lv_indev_data_t * data); +bool wayland_touch_read(lv_indev_drv_t * drv, lv_indev_data_t * data); + +/********************** + * MACROS + **********************/ + +#endif /* USE_WAYLAND */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* WAYLAND_H */ diff --git a/lv_drivers/win32drv/win32drv.c b/lv_drivers/win32drv/win32drv.c new file mode 100644 index 0000000..1ce29c4 --- /dev/null +++ b/lv_drivers/win32drv/win32drv.c @@ -0,0 +1,643 @@ +/** + * @file win32drv.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "win32drv.h" + +#if USE_WIN32DRV + +#include +#include + +#include +#include + +/********************* + * DEFINES + *********************/ + +#define WINDOW_EX_STYLE \ + WS_EX_CLIENTEDGE + +#define WINDOW_STYLE \ + (WS_OVERLAPPEDWINDOW & ~(WS_SIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME)) + +#ifndef WIN32DRV_MONITOR_ZOOM +#define WIN32DRV_MONITOR_ZOOM 1 +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/** + * @brief Creates a B8G8R8A8 frame buffer. + * @param WindowHandle A handle to the window for the creation of the frame + * buffer. If this value is NULL, the entire screen will be + * referenced. + * @param Width The width of the frame buffer. + * @param Height The height of the frame buffer. + * @param PixelBuffer The raw pixel buffer of the frame buffer you created. + * @param PixelBufferSize The size of the frame buffer you created. + * @return If the function succeeds, the return value is a handle to the device + * context (DC) for the frame buffer. If the function fails, the return + * value is NULL, and PixelBuffer parameter is NULL. +*/ +static HDC lv_win32_create_frame_buffer( + _In_opt_ HWND WindowHandle, + _In_ LONG Width, + _In_ LONG Height, + _Out_ UINT32** PixelBuffer, + _Out_ SIZE_T* PixelBufferSize); + +/** + * @brief Enables WM_DPICHANGED message for child window for the associated + * window. + * @param WindowHandle The window you want to enable WM_DPICHANGED message for + * child window. + * @return If the function succeeds, the return value is non-zero. If the + * function fails, the return value is zero. + * @remarks You need to use this function in Windows 10 Threshold 1 or Windows + * 10 Threshold 2. +*/ +static BOOL lv_win32_enable_child_window_dpi_message( + _In_ HWND WindowHandle); + +static void lv_win32_display_driver_flush_callback( + lv_disp_drv_t* disp_drv, + const lv_area_t* area, + lv_color_t* color_p); + +static void lv_win32_display_driver_rounder_callback( + lv_disp_drv_t* disp_drv, + lv_area_t* area); + +static void lv_win32_mouse_driver_read_callback( + lv_indev_drv_t* indev_drv, + lv_indev_data_t* data); + +static void lv_win32_keyboard_driver_read_callback( + lv_indev_drv_t* indev_drv, + lv_indev_data_t* data); + +static void lv_win32_mousewheel_driver_read_callback( + lv_indev_drv_t* indev_drv, + lv_indev_data_t* data); + +static LRESULT CALLBACK lv_win32_window_message_callback( + HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam); + +#if LV_VERSION_CHECK(8, 0, 0) +static void lv_win32_message_handler( + lv_timer_t* param); +#else +static void lv_win32_message_handler( + lv_task_t* param); +#endif + +/********************** + * GLOBAL VARIABLES + **********************/ + +EXTERN_C bool lv_win32_quit_signal = false; + +/********************** + * STATIC VARIABLES + **********************/ + +static HINSTANCE g_instance_handle = NULL; +static HWND g_window_handle = NULL; + +static HDC g_buffer_dc_handle = NULL; +static UINT32* g_pixel_buffer = NULL; +static SIZE_T g_pixel_buffer_size = 0; + +static lv_disp_t* g_display = NULL; + +static bool volatile g_mouse_pressed = false; +static LPARAM volatile g_mouse_value = 0; + +static bool volatile g_mousewheel_pressed = false; +static int16_t volatile g_mousewheel_value = 0; + +static bool volatile g_keyboard_pressed = false; +static WPARAM volatile g_keyboard_value = 0; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +EXTERN_C bool lv_win32_init( + HINSTANCE instance_handle, + int show_window_mode, + lv_coord_t hor_res, + lv_coord_t ver_res, + HICON icon_handle) +{ + WNDCLASSEXW WindowClass; + + WindowClass.cbSize = sizeof(WNDCLASSEX); + + WindowClass.style = 0; + WindowClass.lpfnWndProc = lv_win32_window_message_callback; + WindowClass.cbClsExtra = 0; + WindowClass.cbWndExtra = 0; + WindowClass.hInstance = instance_handle; + WindowClass.hIcon = icon_handle; + WindowClass.hCursor = LoadCursorW(NULL, IDC_ARROW); + WindowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + WindowClass.lpszMenuName = NULL; + WindowClass.lpszClassName = L"lv_sim_visual_studio"; + WindowClass.hIconSm = icon_handle; + + if (!RegisterClassExW(&WindowClass)) + { + return false; + } + + g_instance_handle = instance_handle; + + RECT NewWindowSize; + + NewWindowSize.left = 0; + NewWindowSize.right = hor_res * WIN32DRV_MONITOR_ZOOM; + NewWindowSize.top = 0; + NewWindowSize.bottom = ver_res * WIN32DRV_MONITOR_ZOOM; + + AdjustWindowRectEx( + &NewWindowSize, + WINDOW_STYLE, + FALSE, + WINDOW_EX_STYLE); + OffsetRect( + &NewWindowSize, + -NewWindowSize.left, + -NewWindowSize.top); + + g_window_handle = CreateWindowExW( + WINDOW_EX_STYLE, + WindowClass.lpszClassName, + L"LVGL Simulator for Windows Desktop", + WINDOW_STYLE, + CW_USEDEFAULT, + 0, + NewWindowSize.right, + NewWindowSize.bottom, + NULL, + NULL, + instance_handle, + NULL); + + if (!g_window_handle) + { + return false; + } + +#if LV_VERSION_CHECK(8, 0, 0) + lv_timer_create(lv_win32_message_handler, 0, NULL); +#else + lv_task_create(lv_win32_message_handler, 0, LV_TASK_PRIO_HIGHEST, NULL); +#endif + + lv_win32_enable_child_window_dpi_message(g_window_handle); + + HDC hNewBufferDC = lv_win32_create_frame_buffer( + g_window_handle, + hor_res, + ver_res, + &g_pixel_buffer, + &g_pixel_buffer_size); + + DeleteDC(g_buffer_dc_handle); + g_buffer_dc_handle = hNewBufferDC; + +#if LV_VERSION_CHECK(8, 0, 0) + static lv_disp_draw_buf_t disp_buf; + lv_disp_draw_buf_init( + &disp_buf, + (lv_color_t*)malloc(hor_res * ver_res * sizeof(lv_color_t)), + NULL, + hor_res * ver_res); + + static lv_disp_drv_t disp_drv; + lv_disp_drv_init(&disp_drv); + disp_drv.hor_res = hor_res; + disp_drv.ver_res = ver_res; + disp_drv.flush_cb = lv_win32_display_driver_flush_callback; + disp_drv.draw_buf = &disp_buf; + disp_drv.rounder_cb = lv_win32_display_driver_rounder_callback; + g_display = lv_disp_drv_register(&disp_drv); + + static lv_indev_drv_t indev_drv; + lv_indev_drv_init(&indev_drv); + indev_drv.type = LV_INDEV_TYPE_POINTER; + indev_drv.read_cb = lv_win32_mouse_driver_read_callback; + lv_indev_drv_register(&indev_drv); + + static lv_indev_drv_t kb_drv; + lv_indev_drv_init(&kb_drv); + kb_drv.type = LV_INDEV_TYPE_KEYPAD; + kb_drv.read_cb = lv_win32_keyboard_driver_read_callback; + lv_indev_drv_register(&kb_drv); + + static lv_indev_drv_t enc_drv; + lv_indev_drv_init(&enc_drv); + enc_drv.type = LV_INDEV_TYPE_ENCODER; + enc_drv.read_cb = lv_win32_mousewheel_driver_read_callback; + lv_indev_drv_register(&enc_drv); +#else + static lv_disp_buf_t disp_buf; + lv_disp_buf_init( + &disp_buf, + (lv_color_t*)malloc(hor_res * ver_res * sizeof(lv_color_t)), + NULL, + hor_res * ver_res); + + lv_disp_drv_t disp_drv; + lv_disp_drv_init(&disp_drv); + disp_drv.hor_res = hor_res; + disp_drv.ver_res = ver_res; + disp_drv.flush_cb = lv_win32_display_driver_flush_callback; + disp_drv.buffer = &disp_buf; + disp_drv.rounder_cb = lv_win32_display_driver_rounder_callback; + g_display = lv_disp_drv_register(&disp_drv); + + lv_indev_drv_t indev_drv; + lv_indev_drv_init(&indev_drv); + indev_drv.type = LV_INDEV_TYPE_POINTER; + indev_drv.read_cb = lv_win32_mouse_driver_read_callback; + lv_indev_drv_register(&indev_drv); + + lv_indev_drv_t kb_drv; + lv_indev_drv_init(&kb_drv); + kb_drv.type = LV_INDEV_TYPE_KEYPAD; + kb_drv.read_cb = lv_win32_keyboard_driver_read_callback; + lv_indev_drv_register(&kb_drv); + + lv_indev_drv_t enc_drv; + lv_indev_drv_init(&enc_drv); + enc_drv.type = LV_INDEV_TYPE_ENCODER; + enc_drv.read_cb = lv_win32_mousewheel_driver_read_callback; + lv_indev_drv_register(&enc_drv); +#endif + + ShowWindow(g_window_handle, show_window_mode); + UpdateWindow(g_window_handle); + + return true; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static HDC lv_win32_create_frame_buffer( + HWND WindowHandle, + LONG Width, + LONG Height, + UINT32** PixelBuffer, + SIZE_T* PixelBufferSize) +{ + HDC hFrameBufferDC = NULL; + + if (PixelBuffer && PixelBufferSize) + { + HDC hWindowDC = GetDC(WindowHandle); + if (hWindowDC) + { + hFrameBufferDC = CreateCompatibleDC(hWindowDC); + ReleaseDC(WindowHandle, hWindowDC); + } + + if (hFrameBufferDC) + { + BITMAPINFO BitmapInfo = { 0 }; + BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + BitmapInfo.bmiHeader.biWidth = Width; + BitmapInfo.bmiHeader.biHeight = -Height; + BitmapInfo.bmiHeader.biPlanes = 1; + BitmapInfo.bmiHeader.biBitCount = 32; + BitmapInfo.bmiHeader.biCompression = BI_RGB; + + HBITMAP hBitmap = CreateDIBSection( + hFrameBufferDC, + &BitmapInfo, + DIB_RGB_COLORS, + (void**)PixelBuffer, + NULL, + 0); + if (hBitmap) + { + *PixelBufferSize = Width * Height * sizeof(UINT32); + DeleteObject(SelectObject(hFrameBufferDC, hBitmap)); + DeleteObject(hBitmap); + } + else + { + DeleteDC(hFrameBufferDC); + hFrameBufferDC = NULL; + } + } + } + + return hFrameBufferDC; +} + +static BOOL lv_win32_enable_child_window_dpi_message( + HWND WindowHandle) +{ + // This hack is only for Windows 10 only. + if (!IsWindowsVersionOrGreater(10, 0, 0)) + { + return FALSE; + } + + // We don't need this hack if the Per Monitor Aware V2 is existed. + OSVERSIONINFOEXW OSVersionInfoEx = { 0 }; + OSVersionInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); + OSVersionInfoEx.dwBuildNumber = 14393; + if (VerifyVersionInfoW( + &OSVersionInfoEx, + VER_BUILDNUMBER, + VerSetConditionMask(0, VER_BUILDNUMBER, VER_GREATER_EQUAL))) + { + return FALSE; + } + + HMODULE ModuleHandle = GetModuleHandleW(L"user32.dll"); + if (!ModuleHandle) + { + return FALSE; + } + + typedef BOOL(WINAPI* FunctionType)(HWND, BOOL); + + FunctionType pFunction = (FunctionType)( + GetProcAddress(ModuleHandle, "EnableChildWindowDpiMessage")); + if (!pFunction) + { + return FALSE; + } + + return pFunction(WindowHandle, TRUE); +} + +static void lv_win32_display_driver_flush_callback( + lv_disp_drv_t* disp_drv, + const lv_area_t* area, + lv_color_t* color_p) +{ +#if LV_COLOR_DEPTH == 32 + UNREFERENCED_PARAMETER(area); + memcpy(g_pixel_buffer, color_p, g_pixel_buffer_size); +#else + for (int y = area->y1; y <= area->y2; ++y) + { + for (int x = area->x1; x <= area->x2; ++x) + { + g_pixel_buffer[y * disp_drv->hor_res + x] = lv_color_to32(*color_p); + color_p++; + } + } +#endif + + HDC hWindowDC = GetDC(g_window_handle); + if (hWindowDC) + { + StretchBlt( + hWindowDC, + 0, + 0, + disp_drv->hor_res * WIN32DRV_MONITOR_ZOOM, + disp_drv->ver_res * WIN32DRV_MONITOR_ZOOM, + g_buffer_dc_handle, + 0, + 0, + disp_drv->hor_res, + disp_drv->ver_res, + SRCCOPY); + + ReleaseDC(g_window_handle, hWindowDC); + } + + lv_disp_flush_ready(disp_drv); +} + +static void lv_win32_display_driver_rounder_callback( + lv_disp_drv_t* disp_drv, + lv_area_t* area) +{ + area->x1 = 0; + area->x2 = disp_drv->hor_res - 1; + area->y1 = 0; + area->y2 = disp_drv->ver_res - 1; +} + +static void lv_win32_mouse_driver_read_callback( + lv_indev_drv_t* indev_drv, + lv_indev_data_t* data) +{ + UNREFERENCED_PARAMETER(indev_drv); + + data->state = (lv_indev_state_t)( + g_mouse_pressed ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL); + data->point.x = GET_X_LPARAM(g_mouse_value) / WIN32DRV_MONITOR_ZOOM; + data->point.y = GET_Y_LPARAM(g_mouse_value) / WIN32DRV_MONITOR_ZOOM; +} + +static void lv_win32_keyboard_driver_read_callback( + lv_indev_drv_t* indev_drv, + lv_indev_data_t* data) +{ + UNREFERENCED_PARAMETER(indev_drv); + + data->state = (lv_indev_state_t)( + g_keyboard_pressed ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL); + + WPARAM KeyboardValue = g_keyboard_value; + + switch (KeyboardValue) + { + case VK_UP: + data->key = LV_KEY_UP; + break; + case VK_DOWN: + data->key = LV_KEY_DOWN; + break; + case VK_LEFT: + data->key = LV_KEY_LEFT; + break; + case VK_RIGHT: + data->key = LV_KEY_RIGHT; + break; + case VK_ESCAPE: + data->key = LV_KEY_ESC; + break; + case VK_DELETE: + data->key = LV_KEY_DEL; + break; + case VK_BACK: + data->key = LV_KEY_BACKSPACE; + break; + case VK_RETURN: + data->key = LV_KEY_ENTER; + break; + case VK_NEXT: + data->key = LV_KEY_NEXT; + break; + case VK_PRIOR: + data->key = LV_KEY_PREV; + break; + case VK_HOME: + data->key = LV_KEY_HOME; + break; + case VK_END: + data->key = LV_KEY_END; + break; + default: + if (KeyboardValue >= 'A' && KeyboardValue <= 'Z') + { + KeyboardValue += 0x20; + } + + data->key = (uint32_t)KeyboardValue; + + break; + } +} + +static void lv_win32_mousewheel_driver_read_callback( + lv_indev_drv_t* indev_drv, + lv_indev_data_t* data) +{ + UNREFERENCED_PARAMETER(indev_drv); + + data->state = (lv_indev_state_t)( + g_mousewheel_pressed ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL); + data->enc_diff = g_mousewheel_value; + g_mousewheel_value = 0; +} + +static LRESULT CALLBACK lv_win32_window_message_callback( + HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + switch (uMsg) + { + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + { + g_mouse_value = lParam; + if (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP) + { + g_mouse_pressed = (uMsg == WM_LBUTTONDOWN); + } + else if (uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONUP) + { + g_mousewheel_pressed = (uMsg == WM_MBUTTONDOWN); + } + return 0; + } + case WM_KEYDOWN: + case WM_KEYUP: + { + g_keyboard_pressed = (uMsg == WM_KEYDOWN); + g_keyboard_value = wParam; + break; + } + case WM_MOUSEWHEEL: + { + g_mousewheel_value = -(GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA); + break; + } + case WM_DPICHANGED: + { + LPRECT SuggestedRect = (LPRECT)lParam; + + SetWindowPos( + hWnd, + NULL, + SuggestedRect->left, + SuggestedRect->top, + SuggestedRect->right, + SuggestedRect->bottom, + SWP_NOZORDER | SWP_NOACTIVATE); + + RECT ClientRect; + GetClientRect(hWnd, &ClientRect); + +#if LV_VERSION_CHECK(8, 0, 0) + int WindowWidth = g_display->driver->hor_res; + int WindowHeight = g_display->driver->ver_res; +#else + int WindowWidth = g_display->driver.hor_res; + int WindowHeight = g_display->driver.ver_res; +#endif + + SetWindowPos( + hWnd, + NULL, + SuggestedRect->left, + SuggestedRect->top, + SuggestedRect->right + (WindowWidth - ClientRect.right), + SuggestedRect->bottom + (WindowHeight - ClientRect.bottom), + SWP_NOZORDER | SWP_NOACTIVATE); + + break; + } + case WM_DESTROY: + PostQuitMessage(0); + break; + default: + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + } + + return 0; +} + +#if LV_VERSION_CHECK(8, 0, 0) +static void lv_win32_message_handler( + lv_timer_t* param) +#else +static void lv_win32_message_handler( + lv_task_t* param) +#endif +{ + UNREFERENCED_PARAMETER(param); + + MSG Message; + BOOL Result = PeekMessageW(&Message, NULL, 0, 0, TRUE); + if (Result != 0 && Result != -1) + { + TranslateMessage(&Message); + DispatchMessageW(&Message); + + if (Message.message == WM_QUIT) + { + lv_win32_quit_signal = true; + } + } +} + +#endif /*USE_WIN32DRV*/ diff --git a/lv_drivers/win32drv/win32drv.h b/lv_drivers/win32drv/win32drv.h new file mode 100644 index 0000000..5bbb694 --- /dev/null +++ b/lv_drivers/win32drv/win32drv.h @@ -0,0 +1,72 @@ +/** + * @file win32drv.h + * + */ + +#ifndef LV_WIN32DRV_H +#define LV_WIN32DRV_H + +/********************* + * INCLUDES + *********************/ + +#ifndef LV_DRV_NO_CONF +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_drv_conf.h" +#else +#include "../../lv_drv_conf.h" +#endif +#endif + +#if USE_WIN32DRV + +#include + +#if _MSC_VER >= 1200 + // Disable compilation warnings. +#pragma warning(push) +// nonstandard extension used : bit field types other than int +#pragma warning(disable:4214) +// 'conversion' conversion from 'type1' to 'type2', possible loss of data +#pragma warning(disable:4244) +#endif + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +#if _MSC_VER >= 1200 +// Restore compilation warnings. +#pragma warning(pop) +#endif + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +EXTERN_C bool lv_win32_quit_signal; + +EXTERN_C bool lv_win32_init( + HINSTANCE instance_handle, + int show_window_mode, + lv_coord_t hor_res, + lv_coord_t ver_res, + HICON icon_handle); + +/********************** + * MACROS + **********************/ + +#endif /*USE_WIN32DRV*/ + +#endif /*LV_WIN32DRV_H*/ diff --git a/lv_drivers/win_drv.c b/lv_drivers/win_drv.c new file mode 100644 index 0000000..f41e2ee --- /dev/null +++ b/lv_drivers/win_drv.c @@ -0,0 +1,304 @@ +/** + * @file win_drv.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "win_drv.h" +#if USE_WINDOWS + +#include +#include +#include "lvgl/lvgl.h" + +#if LV_COLOR_DEPTH < 16 +#error Windows driver only supports true RGB colors at this time +#endif + +/********************** + * DEFINES + **********************/ + + #define WINDOW_STYLE (WS_OVERLAPPEDWINDOW & ~(WS_SIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME)) + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void do_register(void); +static void win_drv_flush(lv_disp_t *drv, lv_area_t *area, const lv_color_t * color_p); +static void win_drv_fill(int32_t x1, int32_t y1, int32_t x2, int32_t y2, lv_color_t color); +static void win_drv_map(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_color_t * color_p); +static void win_drv_read(lv_indev_t *drv, lv_indev_data_t * data); +static void msg_handler(void *param); + +static COLORREF lv_color_to_colorref(const lv_color_t color); + +static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); + + +/********************** + * GLOBAL VARIABLES + **********************/ + +bool lv_win_exit_flag = false; +lv_disp_t *lv_windows_disp; + +/********************** + * STATIC VARIABLES + **********************/ +static HWND hwnd; +static uint32_t *fbp = NULL; /* Raw framebuffer memory */ +static bool mouse_pressed; +static int mouse_x, mouse_y; + + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ +const char g_szClassName[] = "LVGL"; + +HWND windrv_init(void) +{ + WNDCLASSEX wc; + RECT winrect; + HICON lvgl_icon; + + //Step 1: Registering the Window Class + wc.cbSize = sizeof(WNDCLASSEX); + wc.style = 0; + wc.lpfnWndProc = WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = GetModuleHandle(NULL); + lvgl_icon = (HICON) LoadImage( NULL, "lvgl_icon.bmp", IMAGE_ICON, 0, 0, LR_LOADFROMFILE); + + if(lvgl_icon == NULL) + lvgl_icon = LoadIcon(NULL, IDI_APPLICATION); + + wc.hIcon = lvgl_icon; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = NULL; + wc.lpszClassName = g_szClassName; + wc.hIconSm = lvgl_icon; + + if(!RegisterClassEx(&wc)) + { + return NULL; + } + + winrect.left = 0; + winrect.right = WINDOW_HOR_RES - 1; + winrect.top = 0; + winrect.bottom = WINDOW_VER_RES - 1; + AdjustWindowRectEx(&winrect, WINDOW_STYLE, FALSE, WS_EX_CLIENTEDGE); + OffsetRect(&winrect, -winrect.left, -winrect.top); + // Step 2: Creating the Window + hwnd = CreateWindowEx( + WS_EX_CLIENTEDGE, + g_szClassName, + "LVGL Simulator", + WINDOW_STYLE, + CW_USEDEFAULT, CW_USEDEFAULT, winrect.right, winrect.bottom, + NULL, NULL, GetModuleHandle(NULL), NULL); + + if(hwnd == NULL) + { + return NULL; + } + + ShowWindow(hwnd, SW_SHOWDEFAULT); + UpdateWindow(hwnd); + + + lv_task_create(msg_handler, 0, LV_TASK_PRIO_HIGHEST, NULL); + lv_win_exit_flag = false; + do_register(); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void do_register(void) +{ + static lv_disp_draw_buf_t disp_buf_1; + static lv_color_t buf1_1[WINDOW_HOR_RES * 100]; /*A buffer for 10 rows*/ + lv_disp_draw_buf_init(&disp_draw_buf_1, buf1_1, NULL, WINDOW_HOR_RES * 100); /*Initialize the display buffer*/ + + + /*----------------------------------- + * Register the display in LVGLGL + *----------------------------------*/ + + static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/ + lv_disp_drv_init(&disp_drv); /*Basic initialization*/ + + /*Set up the functions to access to your display*/ + + /*Set the resolution of the display*/ + disp_drv.hor_res = WINDOW_HOR_RES; + disp_drv.ver_res = WINDOW_VER_RES; + + /*Used to copy the buffer's content to the display*/ + disp_drv.flush_cb = win_drv_flush; + + /*Set a display buffer*/ + disp_drv.draw_buf = &disp_buf_1; + + /*Finally register the driver*/ + lv_windows_disp = lv_disp_drv_register(&disp_drv); + static lv_indev_drv_t indev_drv; + lv_indev_drv_init(&indev_drv); + indev_drv.type = LV_INDEV_TYPE_POINTER; + indev_drv.read_cb = win_drv_read; + lv_indev_drv_register(&indev_drv); +} + +static void msg_handler(void *param) +{ + (void)param; + + MSG msg; + BOOL bRet; + if( (bRet = PeekMessage( &msg, NULL, 0, 0, TRUE )) != 0) + { + if (bRet == -1) + { + return; + } + else + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + if(msg.message == WM_QUIT) + lv_win_exit_flag = true; + } +} + + static void win_drv_read(lv_indev_t *drv, lv_indev_data_t * data) +{ + data->state = mouse_pressed ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL; + data->point.x = mouse_x; + data->point.y = mouse_y; +} + + static void on_paint(void) + { + HBITMAP bmp = CreateBitmap(WINDOW_HOR_RES, WINDOW_VER_RES, 1, 32, fbp); + PAINTSTRUCT ps; + + HDC hdc = BeginPaint(hwnd, &ps); + + HDC hdcMem = CreateCompatibleDC(hdc); + HBITMAP hbmOld = SelectObject(hdcMem, bmp); + + BitBlt(hdc, 0, 0, WINDOW_HOR_RES, WINDOW_VER_RES, hdcMem, 0, 0, SRCCOPY); + + SelectObject(hdcMem, hbmOld); + DeleteDC(hdcMem); + + EndPaint(hwnd, &ps); + DeleteObject(bmp); + +} +/** + * Flush a buffer to the marked area + * @param x1 left coordinate + * @param y1 top coordinate + * @param x2 right coordinate + * @param y2 bottom coordinate + * @param color_p an array of colors + */ +static void win_drv_flush(lv_disp_t *drv, lv_area_t *area, const lv_color_t * color_p) +{ + win_drv_map(area->x1, area->y1, area->x2, area->y2, color_p); + lv_disp_flush_ready(drv); +} + +/** + * Put a color map to the marked area + * @param x1 left coordinate + * @param y1 top coordinate + * @param x2 right coordinate + * @param y2 bottom coordinate + * @param color_p an array of colors + */ +static void win_drv_map(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_color_t * color_p) +{ + for(int y = y1; y <= y2; y++) + { + for(int x = x1; x <= x2; x++) + { + fbp[y*WINDOW_HOR_RES+x] = lv_color_to32(*color_p); + color_p++; + } + } + InvalidateRect(hwnd, NULL, FALSE); + UpdateWindow(hwnd); +} + +static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + HDC hdc; + PAINTSTRUCT ps; + switch(msg) { + case WM_CREATE: + fbp = malloc(4*WINDOW_HOR_RES*WINDOW_VER_RES); + if(fbp == NULL) + return 1; + SetTimer(hwnd, 0, 10, (TIMERPROC)lv_task_handler); + SetTimer(hwnd, 1, 25, NULL); + + return 0; + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + mouse_x = GET_X_LPARAM(lParam); + mouse_y = GET_Y_LPARAM(lParam); + if(msg == WM_LBUTTONDOWN || msg == WM_LBUTTONUP) { + mouse_pressed = (msg == WM_LBUTTONDOWN); + } + return 0; + case WM_CLOSE: + free(fbp); + fbp = NULL; + DestroyWindow(hwnd); + return 0; + case WM_PAINT: + on_paint(); + return 0; + case WM_TIMER: + lv_tick_inc(25); + return 0; + case WM_DESTROY: + PostQuitMessage(0); + return 0; + default: + break; + } + return DefWindowProc(hwnd, msg, wParam, lParam); +} +static COLORREF lv_color_to_colorref(const lv_color_t color) +{ + uint32_t raw_color = lv_color_to32(color); + lv_color32_t tmp; + tmp.full = raw_color; + uint32_t colorref = RGB(tmp.ch.red, tmp.ch.green, tmp.ch.blue); + return colorref; +} +#endif + + + diff --git a/lv_drivers/win_drv.h b/lv_drivers/win_drv.h new file mode 100644 index 0000000..784fc9d --- /dev/null +++ b/lv_drivers/win_drv.h @@ -0,0 +1,60 @@ +/** + * @file fbdev.h + * + */ + +#ifndef WINDRV_H +#define WINDRV_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifndef LV_DRV_NO_CONF +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_drv_conf.h" +#else +#include "../lv_drv_conf.h" +#endif +#endif + +#if USE_WINDOWS + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +extern bool lv_win_exit_flag; +extern lv_disp_t *lv_windows_disp; + +HWND windrv_init(void); + +/********************** + * MACROS + **********************/ + +#endif /*USE_WINDOWS*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*WIN_DRV_H*/ diff --git a/lv_drv_conf.h b/lv_drv_conf.h new file mode 100644 index 0000000..fb7c8c6 --- /dev/null +++ b/lv_drv_conf.h @@ -0,0 +1,423 @@ +/** + * @file lv_drv_conf.h + * Configuration file for v8.0.0 + */ + +/* + * COPY THIS FILE AS lv_drv_conf.h + */ + +#if 1 /*Set it to "1" to enable the content*/ + +#ifndef LV_DRV_CONF_H +#define LV_DRV_CONF_H + +#include "lv_conf.h" + +/********************* + * DELAY INTERFACE + *********************/ +#define LV_DRV_DELAY_INCLUDE /*Dummy include by default*/ +#define LV_DRV_DELAY_US(us) /*delay_us(us)*/ /*Delay the given number of microseconds*/ +#define LV_DRV_DELAY_MS(ms) /*delay_ms(ms)*/ /*Delay the given number of milliseconds*/ + +/********************* + * DISPLAY INTERFACE + *********************/ + +/*------------ + * Common + *------------*/ +#define LV_DRV_DISP_INCLUDE /*Dummy include by default*/ +#define LV_DRV_DISP_CMD_DATA(val) /*pin_x_set(val)*/ /*Set the command/data pin to 'val'*/ +#define LV_DRV_DISP_RST(val) /*pin_x_set(val)*/ /*Set the reset pin to 'val'*/ + +/*--------- + * SPI + *---------*/ +#define LV_DRV_DISP_SPI_CS(val) /*spi_cs_set(val)*/ /*Set the SPI's Chip select to 'val'*/ +#define LV_DRV_DISP_SPI_WR_BYTE(data) /*spi_wr(data)*/ /*Write a byte the SPI bus*/ +#define LV_DRV_DISP_SPI_WR_ARRAY(adr, n) /*spi_wr_mem(adr, n)*/ /*Write 'n' bytes to SPI bus from 'adr'*/ + +/*------------------ + * Parallel port + *-----------------*/ +#define LV_DRV_DISP_PAR_CS(val) /*par_cs_set(val)*/ /*Set the Parallel port's Chip select to 'val'*/ +#define LV_DRV_DISP_PAR_SLOW /*par_slow()*/ /*Set low speed on the parallel port*/ +#define LV_DRV_DISP_PAR_FAST /*par_fast()*/ /*Set high speed on the parallel port*/ +#define LV_DRV_DISP_PAR_WR_WORD(data) /*par_wr(data)*/ /*Write a word to the parallel port*/ +#define LV_DRV_DISP_PAR_WR_ARRAY(adr, n) /*par_wr_mem(adr,n)*/ /*Write 'n' bytes to Parallel ports from 'adr'*/ + +/*************************** + * INPUT DEVICE INTERFACE + ***************************/ + +/*---------- + * Common + *----------*/ +#define LV_DRV_INDEV_INCLUDE /*Dummy include by default*/ +#define LV_DRV_INDEV_RST(val) /*pin_x_set(val)*/ /*Set the reset pin to 'val'*/ +#define LV_DRV_INDEV_IRQ_READ 0 /*pn_x_read()*/ /*Read the IRQ pin*/ + +/*--------- + * SPI + *---------*/ +#define LV_DRV_INDEV_SPI_CS(val) /*spi_cs_set(val)*/ /*Set the SPI's Chip select to 'val'*/ +#define LV_DRV_INDEV_SPI_XCHG_BYTE(data) 0 /*spi_xchg(val)*/ /*Write 'val' to SPI and give the read value*/ + +/*--------- + * I2C + *---------*/ +#define LV_DRV_INDEV_I2C_START /*i2c_start()*/ /*Make an I2C start*/ +#define LV_DRV_INDEV_I2C_STOP /*i2c_stop()*/ /*Make an I2C stop*/ +#define LV_DRV_INDEV_I2C_RESTART /*i2c_restart()*/ /*Make an I2C restart*/ +#define LV_DRV_INDEV_I2C_WR(data) /*i2c_wr(data)*/ /*Write a byte to the I1C bus*/ +#define LV_DRV_INDEV_I2C_READ(last_read) 0 /*i2c_rd()*/ /*Read a byte from the I2C bud*/ + + +/********************* + * DISPLAY DRIVERS + *********************/ + +/*------------------- + * Monitor of PC + *-------------------*/ +#ifndef USE_MONITOR +# define USE_MONITOR 1 +#endif + +#if USE_MONITOR +# define MONITOR_HOR_RES 800 +# define MONITOR_VER_RES 600 + +/* Scale window by this factor (useful when simulating small screens) */ +# define MONITOR_ZOOM 1 + +/* Used to test true double buffering with only address changing. + * Use 2 draw buffers, bith with MONITOR_HOR_RES x MONITOR_VER_RES size*/ +# define MONITOR_DOUBLE_BUFFERED 0 + +/*Eclipse: Visual Studio: */ +# define MONITOR_SDL_INCLUDE_PATH + +/*Open two windows to test multi display support*/ +# define MONITOR_DUAL 0 +#endif + +/*----------------------------------- + * Native Windows (including mouse) + *----------------------------------*/ +#ifndef USE_WINDOWS +# define USE_WINDOWS 0 +#endif + +#if USE_WINDOWS +# define WINDOW_HOR_RES 480 +# define WINDOW_VER_RES 320 +#endif + +/*---------------------------- + * Native Windows (win32drv) + *---------------------------*/ +#ifndef USE_WIN32DRV +# define USE_WIN32DRV 0 +#endif + +#if USE_WIN32DRV +/* Scale window by this factor (useful when simulating small screens) */ +# define WIN32DRV_MONITOR_ZOOM 1 +#endif + +/*---------------------------------------- + * GTK drivers (monitor, mouse, keyboard + *---------------------------------------*/ +#ifndef USE_GTK +# define USE_GTK 0 +#endif + +/*---------------------------------------- + * Wayland drivers (monitor, mouse, keyboard, touchscreen) + *---------------------------------------*/ +#ifndef USE_WAYLAND +# define USE_WAYLAND 0 +#endif + +#if USE_WAYLAND +# define WAYLAND_HOR_RES 480 +# define WAYLAND_VER_RES 320 +# define WAYLAND_SURF_TITLE "LVGL" +#endif + +/*---------------- + * SSD1963 + *--------------*/ +#ifndef USE_SSD1963 +# define USE_SSD1963 0 +#endif + +#if USE_SSD1963 +# define SSD1963_HOR_RES LV_HOR_RES +# define SSD1963_VER_RES LV_VER_RES +# define SSD1963_HT 531 +# define SSD1963_HPS 43 +# define SSD1963_LPS 8 +# define SSD1963_HPW 10 +# define SSD1963_VT 288 +# define SSD1963_VPS 12 +# define SSD1963_FPS 4 +# define SSD1963_VPW 10 +# define SSD1963_HS_NEG 0 /*Negative hsync*/ +# define SSD1963_VS_NEG 0 /*Negative vsync*/ +# define SSD1963_ORI 0 /*0, 90, 180, 270*/ +# define SSD1963_COLOR_DEPTH 16 +#endif + +/*---------------- + * R61581 + *--------------*/ +#ifndef USE_R61581 +# define USE_R61581 0 +#endif + +#if USE_R61581 +# define R61581_HOR_RES LV_HOR_RES +# define R61581_VER_RES LV_VER_RES +# define R61581_HSPL 0 /*HSYNC signal polarity*/ +# define R61581_HSL 10 /*HSYNC length (Not Implemented)*/ +# define R61581_HFP 10 /*Horitontal Front poarch (Not Implemented)*/ +# define R61581_HBP 10 /*Horitontal Back poarch (Not Implemented */ +# define R61581_VSPL 0 /*VSYNC signal polarity*/ +# define R61581_VSL 10 /*VSYNC length (Not Implemented)*/ +# define R61581_VFP 8 /*Vertical Front poarch*/ +# define R61581_VBP 8 /*Vertical Back poarch */ +# define R61581_DPL 0 /*DCLK signal polarity*/ +# define R61581_EPL 1 /*ENABLE signal polarity*/ +# define R61581_ORI 0 /*0, 180*/ +# define R61581_LV_COLOR_DEPTH 16 /*Fix 16 bit*/ +#endif + +/*------------------------------ + * ST7565 (Monochrome, low res.) + *-----------------------------*/ +#ifndef USE_ST7565 +# define USE_ST7565 0 +#endif + +#if USE_ST7565 +/*No settings*/ +#endif /*USE_ST7565*/ + +/*------------------------------ + * GC9A01 (color, low res.) + *-----------------------------*/ +#ifndef USE_GC9A01 +# define USE_GC9A01 0 +#endif + +#if USE_GC9A01 +/*No settings*/ +#endif /*USE_GC9A01*/ + +/*------------------------------------------ + * UC1610 (4 gray 160*[104|128]) + * (EA DOGXL160 160x104 tested) + *-----------------------------------------*/ +#ifndef USE_UC1610 +# define USE_UC1610 0 +#endif + +#if USE_UC1610 +# define UC1610_HOR_RES LV_HOR_RES +# define UC1610_VER_RES LV_VER_RES +# define UC1610_INIT_CONTRAST 33 /* init contrast, values in [%] */ +# define UC1610_INIT_HARD_RST 0 /* 1 : hardware reset at init, 0 : software reset */ +# define UC1610_TOP_VIEW 0 /* 0 : Bottom View, 1 : Top View */ +#endif /*USE_UC1610*/ + +/*------------------------------------------------- + * SHARP memory in pixel monochrome display series + * LS012B7DD01 (184x38 pixels.) + * LS013B7DH03 (128x128 pixels.) + * LS013B7DH05 (144x168 pixels.) + * LS027B7DH01 (400x240 pixels.) (tested) + * LS032B7DD02 (336x536 pixels.) + * LS044Q7DH01 (320x240 pixels.) + *------------------------------------------------*/ +#ifndef USE_SHARP_MIP +# define USE_SHARP_MIP 0 +#endif + +#if USE_SHARP_MIP +# define SHARP_MIP_HOR_RES LV_HOR_RES +# define SHARP_MIP_VER_RES LV_VER_RES +# define SHARP_MIP_SOFT_COM_INVERSION 0 +# define SHARP_MIP_REV_BYTE(b) /*((uint8_t) __REV(__RBIT(b)))*/ /*Architecture / compiler dependent byte bits order reverse*/ +#endif /*USE_SHARP_MIP*/ + +/*------------------------------------------------- + * ILI9341 240X320 TFT LCD + *------------------------------------------------*/ +#ifndef USE_ILI9341 +# define USE_ILI9341 0 +#endif + +#if USE_ILI9341 +# define ILI9341_HOR_RES LV_HOR_RES +# define ILI9341_VER_RES LV_VER_RES +# define ILI9341_GAMMA 1 +# define ILI9341_TEARING 0 +#endif /*USE_ILI9341*/ + +/*----------------------------------------- + * Linux frame buffer device (/dev/fbx) + *-----------------------------------------*/ +#ifndef USE_FBDEV +# define USE_FBDEV 0 +#endif + +#if USE_FBDEV +# define FBDEV_PATH "/dev/fb0" +#endif + +/*----------------------------------------- + * FreeBSD frame buffer device (/dev/fbx) + *.........................................*/ +#ifndef USE_BSD_FBDEV +# define USE_BSD_FBDEV 0 +#endif + +#if USE_BSD_FBDEV +# define FBDEV_PATH "/dev/fb0" +#endif + +/*----------------------------------------- + * DRM/KMS device (/dev/dri/cardX) + *-----------------------------------------*/ +#ifndef USE_DRM +# define USE_DRM 0 +#endif + +#if USE_DRM +# define DRM_CARD "/dev/dri/card0" +# define DRM_CONNECTOR_ID -1 /* -1 for the first connected one */ +#endif + +/********************* + * INPUT DEVICES + *********************/ + +/*-------------- + * XPT2046 + *--------------*/ +#ifndef USE_XPT2046 +# define USE_XPT2046 0 +#endif + +#if USE_XPT2046 +# define XPT2046_HOR_RES 480 +# define XPT2046_VER_RES 320 +# define XPT2046_X_MIN 200 +# define XPT2046_Y_MIN 200 +# define XPT2046_X_MAX 3800 +# define XPT2046_Y_MAX 3800 +# define XPT2046_AVG 4 +# define XPT2046_X_INV 0 +# define XPT2046_Y_INV 0 +# define XPT2046_XY_SWAP 0 +#endif + +/*----------------- + * FT5406EE8 + *-----------------*/ +#ifndef USE_FT5406EE8 +# define USE_FT5406EE8 0 +#endif + +#if USE_FT5406EE8 +# define FT5406EE8_I2C_ADR 0x38 /*7 bit address*/ +#endif + +/*--------------- + * AD TOUCH + *--------------*/ +#ifndef USE_AD_TOUCH +# define USE_AD_TOUCH 0 +#endif + +#if USE_AD_TOUCH +/*No settings*/ +#endif + + +/*--------------------------------------- + * Mouse or touchpad on PC (using SDL) + *-------------------------------------*/ +#ifndef USE_MOUSE +# define USE_MOUSE 1 +#endif + +#if USE_MOUSE +/*No settings*/ +#endif + +/*------------------------------------------- + * Mousewheel as encoder on PC (using SDL) + *------------------------------------------*/ +#ifndef USE_MOUSEWHEEL +# define USE_MOUSEWHEEL 1 +#endif + +#if USE_MOUSEWHEEL +/*No settings*/ +#endif + +/*------------------------------------------------- + * Touchscreen as libinput interface (for Linux based systems) + *------------------------------------------------*/ +#ifndef USE_LIBINPUT +# define USE_LIBINPUT 0 +#endif + +#if USE_LIBINPUT +# define LIBINPUT_NAME "/dev/input/event0" /*You can use the "evtest" Linux tool to get the list of devices and test them*/ +#endif /*USE_LIBINPUT*/ + +/*------------------------------------------------- + * Mouse or touchpad as evdev interface (for Linux based systems) + *------------------------------------------------*/ +#ifndef USE_EVDEV +# define USE_EVDEV 0 +#endif + +#ifndef USE_BSD_EVDEV +# define USE_BSD_EVDEV 0 +#endif + +#if USE_EVDEV || USE_BSD_EVDEV +# define EVDEV_NAME "/dev/input/event0" /*You can use the "evtest" Linux tool to get the list of devices and test them*/ +# define EVDEV_SWAP_AXES 0 /*Swap the x and y axes of the touchscreen*/ + +# define EVDEV_CALIBRATE 0 /*Scale and offset the touchscreen coordinates by using maximum and minimum values for each axis*/ + +# if EVDEV_CALIBRATE +# define EVDEV_HOR_MIN 0 /*to invert axis swap EVDEV_XXX_MIN by EVDEV_XXX_MAX*/ +# define EVDEV_HOR_MAX 4096 /*"evtest" Linux tool can help to get the correct calibraion values>*/ +# define EVDEV_VER_MIN 0 +# define EVDEV_VER_MAX 4096 +# endif /*EVDEV_CALIBRATE*/ +#endif /*USE_EVDEV*/ + +/*------------------------------- + * Keyboard of a PC (using SDL) + *------------------------------*/ +#ifndef USE_KEYBOARD +# define USE_KEYBOARD 1 +#endif + +#if USE_KEYBOARD +/*No settings*/ +#endif + +#endif /*LV_DRV_CONF_H*/ + +#endif /*End of "Content enable"*/ diff --git a/lvgl/.codecov.yml b/lvgl/.codecov.yml new file mode 100644 index 0000000..6492c29 --- /dev/null +++ b/lvgl/.codecov.yml @@ -0,0 +1,8 @@ +codecov: + notify: + require_ci_to_pass: true +comment: off +coverage: + status: + patch: off + project: off diff --git a/lvgl/.editorconfig b/lvgl/.editorconfig new file mode 100644 index 0000000..3f0bdad --- /dev/null +++ b/lvgl/.editorconfig @@ -0,0 +1,6 @@ +[*.{c,h,ino}] +indent_style = space +indent_size = 4 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/lvgl/.gitignore b/lvgl/.gitignore new file mode 100644 index 0000000..f057154 --- /dev/null +++ b/lvgl/.gitignore @@ -0,0 +1,27 @@ +**/*.o +**/*bin +**/*.swp +**/*.swo +**/*.gcda +**/*.gcno +tags +docs/api_doc +scripts/cppcheck_res.txt +scripts/built_in_font/lv_font_* +docs/doxygen_html +docs/xml +docs/examples.md +docs/out_latex +docs/_static/built_lv_examples +docs/LVGL.pdf +docs/env +out_html +__pycache__ +/emscripten_builder +test_screenshot_error.h +build/ +tests/build_*/ +tests/report/ +.DS_Store +.vscode +*.bak diff --git a/lvgl/.pre-commit-config.yaml b/lvgl/.pre-commit-config.yaml new file mode 100644 index 0000000..9d8088a --- /dev/null +++ b/lvgl/.pre-commit-config.yaml @@ -0,0 +1,30 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.2.0 + hooks: + - id: trailing-whitespace + +- repo: local + hooks: + # Run astyle over the staged files with c and h extension found in the directories + # listed in the files regex pattern. Ignoring the files in the exclude pattern. + - id: format-source + name: Formatting source files + entry: astyle --options=scripts/code-format.cfg --ignore-exclude-errors + stages: [ commit ] + language: system + pass_filenames: true + verbose: true + files: | + (?x)^( + src/ | + tests/src/test_cases/ + ) + exclude: | + (?x)^( + src/extra/libs/ | + src/lv_conf_internal.h + ) + types_or: ["c", "header"] \ No newline at end of file diff --git a/lvgl/CMakeLists.txt b/lvgl/CMakeLists.txt new file mode 100644 index 0000000..c1f72b8 --- /dev/null +++ b/lvgl/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.12.4) + +if(NOT ESP_PLATFORM) + project(lvgl HOMEPAGE_URL https://github.com/lvgl/lvgl) +endif() + +set(LVGL_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}) + +if(ESP_PLATFORM) + include(${CMAKE_CURRENT_LIST_DIR}/env_support/cmake/esp.cmake) +elseif(ZEPHYR_BASE) + include(${CMAKE_CURRENT_LIST_DIR}/env_support/cmake/zephyr.cmake) +elseif(MICROPY_DIR) + include(${CMAKE_CURRENT_LIST_DIR}/env_support/cmake/micropython.cmake) +else() + include(${CMAKE_CURRENT_LIST_DIR}/env_support/cmake/custom.cmake) +endif() diff --git a/lvgl/Kconfig b/lvgl/Kconfig new file mode 100644 index 0000000..23c2c25 --- /dev/null +++ b/lvgl/Kconfig @@ -0,0 +1,1121 @@ +# Kconfig file for LVGL v8.0 + +menu "LVGL configuration" + + # Define CONFIG_LV_CONF_SKIP so we can use LVGL + # without lv_conf.h file, the lv_conf_internal.h and + # lv_conf_kconfig.h files are used instead. + config LV_CONF_SKIP + bool "Uncheck this to use custom lv_conf.h" + default y + + config LV_CONF_MINIMAL + bool "LVGL minimal configuration." + + menu "Color settings" + choice LV_COLOR_DEPTH + prompt "Color depth." + default LV_COLOR_DEPTH_16 + help + Color depth to be used. + + config LV_COLOR_DEPTH_32 + bool "32: ARGB8888" + config LV_COLOR_DEPTH_16 + bool "16: RGB565" + config LV_COLOR_DEPTH_8 + bool "8: RGB232" + config LV_COLOR_DEPTH_1 + bool "1: 1 byte per pixel" + endchoice + + config LV_COLOR_DEPTH + int + default 1 if LV_COLOR_DEPTH_1 + default 8 if LV_COLOR_DEPTH_8 + default 16 if LV_COLOR_DEPTH_16 + default 32 if LV_COLOR_DEPTH_32 + + config LV_COLOR_16_SWAP + bool "Swap the 2 bytes of RGB565 color. Useful if the display has an 8-bit interface (e.g. SPI)." + depends on LV_COLOR_DEPTH_16 + + config LV_COLOR_SCREEN_TRANSP + bool "Enable more complex drawing routines to manage screens transparency." + help + Can be used if the UI is above another layer, e.g. an OSD menu or video player. + The screen's `bg_opa` should be set to non LV_OPA_COVER value + + config LV_COLOR_MIX_ROUND_OFS + int "Adjust color mix functions rounding" + default 128 if !LV_COLOR_DEPTH_32 + default 0 if LV_COLOR_DEPTH_32 + range 0 254 + help + 0: no adjustment, get the integer part of the result (round down) + 64: round up from x.75 + 128: round up from half + 192: round up from x.25 + 254: round up + + config LV_COLOR_CHROMA_KEY_HEX + hex "Images pixels with this color will not be drawn (if they are chroma keyed)." + range 0x000000 0xFFFFFF + default 0x00FF00 + help + See misc/lv_color.h for some color values examples. + endmenu + + menu "Memory settings" + config LV_MEM_CUSTOM + bool "If true use custom malloc/free, otherwise use the built-in `lv_mem_alloc()` and `lv_mem_free()`" + + config LV_MEM_SIZE_KILOBYTES + int "Size of the memory used by `lv_mem_alloc` in kilobytes (>= 2kB)" + range 2 128 + default 32 + depends on !LV_MEM_CUSTOM + + config LV_MEM_ADDR + hex "Address for the memory pool instead of allocating it as a normal array" + default 0x0 + depends on !LV_MEM_CUSTOM + + config LV_MEM_CUSTOM_INCLUDE + string "Header to include for the custom memory function" + default "stdlib.h" + depends on LV_MEM_CUSTOM + + config LV_MEM_BUF_MAX_NUM + int "Number of the memory buffer" + default 16 + help + Number of the intermediate memory buffer used during rendering and other + internal processing mechanisms. You will see an error log message if + there wasn't enough buffers. + + config LV_MEMCPY_MEMSET_STD + bool "Use the standard memcpy and memset instead of LVGL's own functions" + endmenu + + menu "HAL Settings" + config LV_DISP_DEF_REFR_PERIOD + int "Default display refresh period (ms)." + default 30 + help + Can be changed in the display driver (`lv_disp_drv_t`). + + config LV_INDEV_DEF_READ_PERIOD + int "Input device read period [ms]." + default 30 + + config LV_TICK_CUSTOM + bool "Use a custom tick source" + + config LV_TICK_CUSTOM_INCLUDE + string "Header for the system time function" + default "Arduino.h" + depends on LV_TICK_CUSTOM + + config LV_DPI_DEF + int "Default Dots Per Inch (in px)." + default 130 + help + Used to initialize default sizes such as widgets sized, style paddings. + (Not so important, you can adjust it to modify default sizes and spaces) + endmenu + + menu "Feature configuration" + + menu "Drawing" + config LV_DRAW_COMPLEX + bool "Enable complex draw engine" + default y + help + Required to draw shadow, gradient, rounded corners, circles, arc, skew lines, + image transformations or any masks. + + config LV_SHADOW_CACHE_SIZE + int "Allow buffering some shadow calculation" + depends on LV_DRAW_COMPLEX + default 0 + help + LV_SHADOW_CACHE_SIZE is the max shadow size to buffer, where + shadow size is `shadow_width + radius`. + Caching has LV_SHADOW_CACHE_SIZE^2 RAM cost. + + config LV_CIRCLE_CACHE_SIZE + int "Set number of maximally cached circle data" + depends on LV_DRAW_COMPLEX + default 4 + help + The circumference of 1/4 circle are saved for anti-aliasing + radius * 4 bytes are used per circle (the most often used + radiuses are saved). + Set to 0 to disable caching. + + config LV_LAYER_SIMPLE_BUF_SIZE + int "Optimal size to buffer the widget with opacity" + default 24576 + help + "Simple layers" are used when a widget has `style_opa < 255` + to buffer the widget into a layer and blend it as an image + with the given opacity. Note that `bg_opa`, `text_opa` etc + don't require buffering into layer. + + config LV_IMG_CACHE_DEF_SIZE + int "Default image cache size. 0 to disable caching." + default 0 + help + If only the built-in image formats are used there is no real advantage of caching. + (I.e. no new image decoder is added). + + With complex image decoders (e.g. PNG or JPG) caching can + save the continuous open/decode of images. + However the opened images might consume additional RAM. + + config LV_GRADIENT_MAX_STOPS + int "Number of stops allowed per gradient." + default 2 + help + Increase this to allow more stops. + This adds (sizeof(lv_color_t) + 1) bytes per additional stop + + config LV_GRAD_CACHE_DEF_SIZE + int "Default gradient buffer size." + default 0 + help + When LVGL calculates the gradient "maps" it can save them into a cache to avoid calculating them again. + LV_GRAD_CACHE_DEF_SIZE sets the size of this cache in bytes. + If the cache is too small the map will be allocated only while it's required for the drawing. + 0 mean no caching. + + config LV_DITHER_GRADIENT + bool "Allow dithering the gradients" + help + Allow dithering the gradients (to achieve visual smooth color gradients on limited color depth display) + LV_DITHER_GRADIENT implies allocating one or two more lines of the object's rendering surface + The increase in memory consumption is (32 bits * object width) plus 24 bits * object width if using error diffusion + + config LV_DITHER_ERROR_DIFFUSION + bool "Add support for error diffusion dithering" + depends on LV_DITHER_GRADIENT + help + Error diffusion dithering gets a much better visual result, but implies more CPU consumption and memory when drawing. + The increase in memory consumption is (24 bits * object's width) + + config LV_DISP_ROT_MAX_BUF + int "Maximum buffer size to allocate for rotation" + default 10240 + help + Only used if software rotation is enabled in the display driver. + endmenu + + menu "GPU" + config LV_USE_GPU_ARM2D + bool "Enable Arm's 2D image processing library (Arm-2D) for all Cortex-M processors." + default n + help + Must deploy arm-2d library to your project and add include PATH for "arm_2d.h". + + config LV_USE_GPU_STM32_DMA2D + bool "Enable STM32 DMA2D (aka Chrom Art) GPU." + config LV_GPU_DMA2D_CMSIS_INCLUDE + string "include path of CMSIS header of target processor" + depends on LV_USE_GPU_STM32_DMA2D + default "" + help + Must be defined to include path of CMSIS header of target processor + e.g. "stm32f769xx.h" or "stm32f429xx.h" + + config LV_USE_GPU_RA6M3_G2D + bool "Enable RA6M3 G2D GPU." + config LV_GPU_RA6M3_G2D_INCLUDE + string "include path of target processor" + depends on LV_USE_GPU_RA6M3_G2D + default "hal_data.h" + help + Must be defined to include path of target processor + e.g. "hal_data.h" + + config LV_USE_GPU_SWM341_DMA2D + bool "Enable SWM341 DMA2D GPU." + config LV_GPU_SWM341_DMA2D_INCLUDE + string "include path of CMSIS header of target processor" + depends on LV_USE_GPU_SWM341_DMA2D + default "SWM341.h" + help + Must be defined to include path of CMSIS header of target processor + e.g. "SWM341.h" + + config LV_USE_GPU_NXP_PXP + bool "Use NXP's PXP GPU iMX RTxxx platforms." + config LV_USE_GPU_NXP_PXP_AUTO_INIT + bool "Call lv_gpu_nxp_pxp_init() automatically or manually." + depends on LV_USE_GPU_NXP_PXP + help + 1: Add default bare metal and FreeRTOS interrupt handling + routines for PXP (lv_gpu_nxp_pxp_osa.c) and call + lv_gpu_nxp_pxp_init() automatically during lv_init(). + Note that symbol SDK_OS_FREE_RTOS has to be defined in order + to use FreeRTOS OSA, otherwise bare-metal implementation is + selected. + 0: lv_gpu_nxp_pxp_init() has to be called manually before + lv_init(). + + config LV_USE_GPU_NXP_VG_LITE + bool "Use NXP's VG-Lite GPU iMX RTxxx platforms." + + config LV_USE_GPU_SDL + bool "Use SDL renderer API" + default n + config LV_GPU_SDL_INCLUDE_PATH + string "include path of SDL header" + depends on LV_USE_GPU_SDL + default "SDL2/SDL.h" + config LV_GPU_SDL_LRU_SIZE + int "Maximum buffer size to allocate for rotation" + depends on LV_USE_GPU_SDL + default 8388608 + help + Texture cache size, 8MB by default. + endmenu + + menu "Logging" + config LV_USE_LOG + bool "Enable the log module" + + choice + bool "Default log verbosity" if LV_USE_LOG + default LV_LOG_LEVEL_WARN + help + Specify how important log should be added. + + config LV_LOG_LEVEL_TRACE + bool "A lot of logs to give detailed information" + config LV_LOG_LEVEL_INFO + bool "Log important events" + config LV_LOG_LEVEL_WARN + bool "Log if something unwanted happened but didn't cause a problem" + config LV_LOG_LEVEL_ERROR + bool "Only critical issues, when the system may fail" + config LV_LOG_LEVEL_USER + bool "Only logs added by the user" + config LV_LOG_LEVEL_NONE + bool "Do not log anything" + endchoice + + config LV_LOG_LEVEL + int + default 0 if LV_LOG_LEVEL_TRACE + default 1 if LV_LOG_LEVEL_INFO + default 2 if LV_LOG_LEVEL_WARN + default 3 if LV_LOG_LEVEL_ERROR + default 4 if LV_LOG_LEVEL_USER + default 5 if LV_LOG_LEVEL_NONE + + config LV_LOG_PRINTF + bool "Print the log with 'printf'" if LV_USE_LOG + help + Use printf for log output. + If not set the user needs to register a callback with `lv_log_register_print_cb`. + + config LV_LOG_TRACE_MEM + bool "Enable/Disable LV_LOG_TRACE in mem module" + default y + depends on LV_USE_LOG + + config LV_LOG_TRACE_TIMER + bool "Enable/Disable LV_LOG_TRACE in timer module" + default y + depends on LV_USE_LOG + + config LV_LOG_TRACE_INDEV + bool "Enable/Disable LV_LOG_TRACE in indev module" + default y + depends on LV_USE_LOG + + config LV_LOG_TRACE_DISP_REFR + bool "Enable/Disable LV_LOG_TRACE in disp refr module" + default y + depends on LV_USE_LOG + + config LV_LOG_TRACE_EVENT + bool "Enable/Disable LV_LOG_TRACE in event module" + default y + depends on LV_USE_LOG + + config LV_LOG_TRACE_OBJ_CREATE + bool "Enable/Disable LV_LOG_TRACE in obj create module" + default y + depends on LV_USE_LOG + + config LV_LOG_TRACE_LAYOUT + bool "Enable/Disable LV_LOG_TRACE in layout module" + default y + depends on LV_USE_LOG + + config LV_LOG_TRACE_ANIM + bool "Enable/Disable LV_LOG_TRACE in anim module" + default y + depends on LV_USE_LOG + endmenu + + menu "Asserts" + config LV_USE_ASSERT_NULL + bool "Check if the parameter is NULL. (Very fast, recommended)" + default y if !LV_CONF_MINIMAL + + config LV_USE_ASSERT_MALLOC + bool "Checks if the memory is successfully allocated or no. (Very fast, recommended)" + default y if !LV_CONF_MINIMAL + + config LV_USE_ASSERT_STYLE + bool "Check if the styles are properly initialized. (Very fast, recommended)" + + config LV_USE_ASSERT_MEM_INTEGRITY + bool "Check the integrity of `lv_mem` after critical operations. (Slow)" + + config LV_USE_ASSERT_OBJ + bool "Check NULL, the object's type and existence (e.g. not deleted). (Slow)." + + config LV_ASSERT_HANDLER_INCLUDE + string "Header to include for the custom assert function" + default "assert.h" + endmenu + + menu "Others" + config LV_USE_PERF_MONITOR + bool "Show CPU usage and FPS count." + + choice + prompt "Performance monitor position." + depends on LV_USE_PERF_MONITOR + default LV_PERF_MONITOR_ALIGN_BOTTOM_RIGHT + + config LV_PERF_MONITOR_ALIGN_TOP_LEFT + bool "Top left" + config LV_PERF_MONITOR_ALIGN_TOP_MID + bool "Top middle" + config LV_PERF_MONITOR_ALIGN_TOP_RIGHT + bool "Top right" + config LV_PERF_MONITOR_ALIGN_BOTTOM_LEFT + bool "Bottom left" + config LV_PERF_MONITOR_ALIGN_BOTTOM_MID + bool "Bottom middle" + config LV_PERF_MONITOR_ALIGN_BOTTOM_RIGHT + bool "Bottom right" + config LV_PERF_MONITOR_ALIGN_LEFT_MID + bool "Left middle" + config LV_PERF_MONITOR_ALIGN_RIGHT_MID + bool "Right middle" + config LV_PERF_MONITOR_ALIGN_CENTER + bool "Center" + endchoice + + config LV_USE_MEM_MONITOR + bool "Show the used memory and the memory fragmentation." + depends on !LV_MEM_CUSTOM + + choice + prompt "Memory monitor position." + depends on LV_USE_MEM_MONITOR + default LV_MEM_MONITOR_ALIGN_BOTTOM_LEFT + + config LV_MEM_MONITOR_ALIGN_TOP_LEFT + bool "Top left" + config LV_MEM_MONITOR_ALIGN_TOP_MID + bool "Top middle" + config LV_MEM_MONITOR_ALIGN_TOP_RIGHT + bool "Top right" + config LV_MEM_MONITOR_ALIGN_BOTTOM_LEFT + bool "Bottom left" + config LV_MEM_MONITOR_ALIGN_BOTTOM_MID + bool "Bottom middle" + config LV_MEM_MONITOR_ALIGN_BOTTOM_RIGHT + bool "Bottom right" + config LV_MEM_MONITOR_ALIGN_LEFT_MID + bool "Left middle" + config LV_MEM_MONITOR_ALIGN_RIGHT_MID + bool "Right middle" + config LV_MEM_MONITOR_ALIGN_CENTER + bool "Center" + endchoice + + config LV_USE_REFR_DEBUG + bool "Draw random colored rectangles over the redrawn areas." + + config LV_SPRINTF_CUSTOM + bool "Change the built-in (v)snprintf functions" + + config LV_SPRINTF_INCLUDE + string "Header to include for the custom sprintf function" + depends on LV_SPRINTF_CUSTOM + default "stdio.h" + + config LV_SPRINTF_USE_FLOAT + bool "Enable float in built-in (v)snprintf functions" + depends on !LV_SPRINTF_CUSTOM + + config LV_USE_USER_DATA + bool "Add a 'user_data' to drivers and objects." + default y + + config LV_ENABLE_GC + bool "Enable garbage collector" + + config LV_GC_INCLUDE + string "Header to include for the garbage collector related things" + depends on LV_ENABLE_GC + default "gc.h" + endmenu + + menu "Compiler settings" + config LV_BIG_ENDIAN_SYSTEM + bool "For big endian systems set to 1" + + config LV_ATTRIBUTE_MEM_ALIGN_SIZE + int "Required alignment size for buffers" + default 1 + + config LV_ATTRIBUTE_FAST_MEM_USE_IRAM + bool "Set IRAM as LV_ATTRIBUTE_FAST_MEM" + help + Set this option to configure IRAM as LV_ATTRIBUTE_FAST_MEM + + config LV_USE_LARGE_COORD + bool "Extend the default -32k..32k coordinate range to -4M..4M by using int32_t for coordinates instead of int16_t" + endmenu + endmenu + + menu "Font usage" + menu "Enable built-in fonts" + config LV_FONT_MONTSERRAT_8 + bool "Enable Montserrat 8" + config LV_FONT_MONTSERRAT_10 + bool "Enable Montserrat 10" + config LV_FONT_MONTSERRAT_12 + bool "Enable Montserrat 12" + config LV_FONT_MONTSERRAT_14 + bool "Enable Montserrat 14" + default y if !LV_CONF_MINIMAL + config LV_FONT_MONTSERRAT_16 + bool "Enable Montserrat 16" + config LV_FONT_MONTSERRAT_18 + bool "Enable Montserrat 18" + config LV_FONT_MONTSERRAT_20 + bool "Enable Montserrat 20" + config LV_FONT_MONTSERRAT_22 + bool "Enable Montserrat 22" + config LV_FONT_MONTSERRAT_24 + bool "Enable Montserrat 24" + config LV_FONT_MONTSERRAT_26 + bool "Enable Montserrat 26" + config LV_FONT_MONTSERRAT_28 + bool "Enable Montserrat 28" + config LV_FONT_MONTSERRAT_30 + bool "Enable Montserrat 30" + config LV_FONT_MONTSERRAT_32 + bool "Enable Montserrat 32" + config LV_FONT_MONTSERRAT_34 + bool "Enable Montserrat 34" + config LV_FONT_MONTSERRAT_36 + bool "Enable Montserrat 36" + config LV_FONT_MONTSERRAT_38 + bool "Enable Montserrat 38" + config LV_FONT_MONTSERRAT_40 + bool "Enable Montserrat 40" + config LV_FONT_MONTSERRAT_42 + bool "Enable Montserrat 42" + config LV_FONT_MONTSERRAT_44 + bool "Enable Montserrat 44" + config LV_FONT_MONTSERRAT_46 + bool "Enable Montserrat 46" + config LV_FONT_MONTSERRAT_48 + bool "Enable Montserrat 48" + + config LV_FONT_MONTSERRAT_12_SUBPX + bool "Enable Montserrat 12 sub-pixel" + config LV_FONT_MONTSERRAT_28_COMPRESSED + bool "Enable Montserrat 28 compressed" + config LV_FONT_DEJAVU_16_PERSIAN_HEBREW + bool "Enable Dejavu 16 Persian, Hebrew, Arabic letters" + config LV_FONT_SIMSUN_16_CJK + bool "Enable Simsun 16 CJK" + + config LV_FONT_UNSCII_8 + bool "Enable UNSCII 8 (Perfect monospace font)" + default y if LV_CONF_MINIMAL + config LV_FONT_UNSCII_16 + bool "Enable UNSCII 16 (Perfect monospace font)" + + config LV_FONT_CUSTOM + bool "Enable the custom font" + config LV_FONT_CUSTOM_DECLARE + string "Header to include for the custom font" + depends on LV_FONT_CUSTOM + endmenu + + choice LV_FONT_DEFAULT + prompt "Select theme default title font" + default LV_FONT_DEFAULT_MONTSERRAT_14 if !LV_CONF_MINIMAL + default LV_FONT_DEFAULT_UNSCII_8 if LV_CONF_MINIMAL + help + Select theme default title font + + config LV_FONT_DEFAULT_MONTSERRAT_8 + bool "Montserrat 8" + select LV_FONT_MONTSERRAT_8 + config LV_FONT_DEFAULT_MONTSERRAT_12 + bool "Montserrat 12" + select LV_FONT_MONTSERRAT_12 + config LV_FONT_DEFAULT_MONTSERRAT_14 + bool "Montserrat 14" + select LV_FONT_MONTSERRAT_14 + config LV_FONT_DEFAULT_MONTSERRAT_16 + bool "Montserrat 16" + select LV_FONT_MONTSERRAT_16 + config LV_FONT_DEFAULT_MONTSERRAT_18 + bool "Montserrat 18" + select LV_FONT_MONTSERRAT_18 + config LV_FONT_DEFAULT_MONTSERRAT_20 + bool "Montserrat 20" + select LV_FONT_MONTSERRAT_20 + config LV_FONT_DEFAULT_MONTSERRAT_22 + bool "Montserrat 22" + select LV_FONT_MONTSERRAT_22 + config LV_FONT_DEFAULT_MONTSERRAT_24 + bool "Montserrat 24" + select LV_FONT_MONTSERRAT_24 + config LV_FONT_DEFAULT_MONTSERRAT_26 + bool "Montserrat 26" + select LV_FONT_MONTSERRAT_26 + config LV_FONT_DEFAULT_MONTSERRAT_28 + bool "Montserrat 28" + select LV_FONT_MONTSERRAT_28 + config LV_FONT_DEFAULT_MONTSERRAT_30 + bool "Montserrat 30" + select LV_FONT_MONTSERRAT_30 + config LV_FONT_DEFAULT_MONTSERRAT_32 + bool "Montserrat 32" + select LV_FONT_MONTSERRAT_