diff --git a/CODEOWNERS b/CODEOWNERS index 5ef44cec568..e9cf5317824 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -313,6 +313,7 @@ /samples/boards/intel_s1000_crb/ @sathishkuttan @dcpleung @nashif /samples/display/ @vanwinkeljan /samples/drivers/CAN/ @alexanderwachter +/samples/drivers/display/ @vanwinkeljan /samples/drivers/ht16k33/ @henrikbrixandersen /samples/drivers/lora/ @Mani-Sadhasivam /samples/gui/ @vanwinkeljan diff --git a/samples/drivers/display/CMakeLists.txt b/samples/drivers/display/CMakeLists.txt new file mode 100644 index 00000000000..8bcc4f98473 --- /dev/null +++ b/samples/drivers/display/CMakeLists.txt @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(display) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/drivers/display/README.rst b/samples/drivers/display/README.rst new file mode 100644 index 00000000000..0e7ff423a91 --- /dev/null +++ b/samples/drivers/display/README.rst @@ -0,0 +1,47 @@ +.. _display-sample: + +Display Sample +############## + +Overview +******** + +This sample will draw some basic rectangles onto the display. +The rectangle colors and positions are chosen so that you can check the +orientation of the LCD and correct RGB bit order. The rectangles are drawn +in clockwise order, from top left corner: red, green, blue, grey. The shade of +grey changes from black through to white. If the grey looks too green or red +at any point or the order of the corners is not as described above then the LCD +may be endian swapped. + +Building and Running +******************** + +As this is a generic sample it should work with any display supported by Zephyr. + +Below is an example on how to build for a :ref:`nrf52840_pca10056` board with a +:ref:`adafruit_2_8_tft_touch_v2`. + +.. zephyr-app-commands:: + :zephyr-app: samples/drivers/display_shield + :board: nrf52840_pca10056 + :goals: build + :shield: adafruit_2_8_tft_touch_v2 + :compact: + +For testing purpose without the need of any hardware, the :ref:`native_posix` +board is also supported and can be built as follows; + +.. zephyr-app-commands:: + :zephyr-app: samples/drivers/display_shield + :board: native_posix + :goals: build + :compact: + +List of Arduino-based display shields +************************************* + +- :ref:`adafruit_2_8_tft_touch_v2` +- :ref:`ssd1306_128_shield` +- :ref:`st7789v_generic` +- :ref:`waveshare_e_paper_raw_panel_shield` diff --git a/samples/drivers/display/dts_fixup.h b/samples/drivers/display/dts_fixup.h new file mode 100644 index 00000000000..0b0166b7c95 --- /dev/null +++ b/samples/drivers/display/dts_fixup.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 Jan Van Winkel + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifdef DT_INST_0_ILITEK_ILI9340_LABEL +#define DISPLAY_DEV_NAME DT_INST_0_ILITEK_ILI9340_LABEL +#endif + +#ifdef DT_INST_0_SOLOMON_SSD1306FB_LABEL +#define DISPLAY_DEV_NAME DT_INST_0_SOLOMON_SSD1306FB_LABEL +#endif + +#ifdef DT_INST_0_GOODDISPLAY_GDEH0213B1_LABEL +#define DISPLAY_DEV_NAME DT_INST_0_GOODDISPLAY_GDEH0213B1_LABEL +#endif + +#ifdef DT_INST_0_SITRONIX_ST7789V_LABEL +#define DISPLAY_DEV_NAME DT_INST_0_SITRONIX_ST7789V_LABEL +#endif + +#ifdef DT_INST_0_FSL_IMX6SX_LCDIF_LABEL +#define DISPLAY_DEV_NAME DT_INST_0_FSL_IMX6SX_LCDIF_LABEL +#endif + +#ifdef CONFIG_SDL_DISPLAY_DEV_NAME +#define DISPLAY_DEV_NAME CONFIG_SDL_DISPLAY_DEV_NAME +#endif + +#ifdef CONFIG_DUMMY_DISPLAY_DEV_NAME +#define DISPLAY_DEV_NAME CONFIG_DUMMY_DISPLAY_DEV_NAME +#endif + diff --git a/samples/drivers/display/prj.conf b/samples/drivers/display/prj.conf new file mode 100644 index 00000000000..41d5305a158 --- /dev/null +++ b/samples/drivers/display/prj.conf @@ -0,0 +1,3 @@ +CONFIG_HEAP_MEM_POOL_SIZE=16384 +CONFIG_LOG=y +CONFIG_DISPLAY=y diff --git a/samples/drivers/display/sample.yaml b/samples/drivers/display/sample.yaml new file mode 100644 index 00000000000..0b18c9e3595 --- /dev/null +++ b/samples/drivers/display/sample.yaml @@ -0,0 +1,41 @@ +sample: + description: Sample application for displays + name: display_sample +tests: + sample.display.shield.adafruit_2_8_tft_touch_v2: + platform_whitelist: nrf52840_pca10056 + extra_args: SHIELD=adafruit_2_8_tft_touch_v2 + tags: display shield + sample.display.shield.ssd1306_128x32: + platform_whitelist: nrf52840_pca10056 + extra_args: SHIELD=ssd1306_128x32 + tags: display shield + sample.display.shield.ssd1306_128x64: + platform_whitelist: nrf52840_pca10056 + extra_args: SHIELD=ssd1306_128x64 + tags: display shield + sample.display.shield.waveshare_epaper_gdeh0213b1: + platform_whitelist: nrf52840_pca10056 + extra_args: SHIELD=waveshare_epaper_gdeh0213b1 + sample.display.st7789v_tl019fqv01: + platform_whitelist: nrf52_pca10040 + extra_args: SHIELD=st7789v_tl019fqv01 + tags: display shield + sample.display.st7789v_waveshare_240x240: + platform_whitelist: nrf52_pca10040 + extra_args: SHIELD=st7789v_waveshare_240x240 + tags: display shield + sample.display.mcux_elcdif: + platform_whitelist: mimxrt1050_evk + tags: display + sample.display.sdl: + build_only: true + platform_whitelist: native_posix_64 + tags: display + sample.display.dummy: + platform_whitelist: native_posix + extra_configs: + - CONFIG_DUMMY_DISPLAY=y + - CONFIG_SDL_DISPLAY=n + - CONFIG_TEST=y + tags: display diff --git a/samples/drivers/display/src/main.c b/samples/drivers/display/src/main.c new file mode 100644 index 00000000000..bd2cb41926c --- /dev/null +++ b/samples/drivers/display/src/main.c @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2019 Jan Van Winkel + * + * Based on ST7789V sample: + * Copyright (c) 2019 Marc Reilly + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "display.h" +#include +LOG_MODULE_REGISTER(sample, LOG_LEVEL_INF); + +#include +#include +#include + +#ifdef CONFIG_ARCH_POSIX +#include "posix_board_if.h" +#endif + +#ifdef CONFIG_ARCH_POSIX +#define RETURN_FROM_MAIN(exit_code) posix_exit_main(exit_code) +#else +#define RETURN_FROM_MAIN(exit_code) return +#endif + +enum corner { + TOP_LEFT, + TOP_RIGHT, + BOTTOM_RIGHT, + BOTTOM_LEFT +}; + +typedef void (*fill_buffer)(enum corner corner, u8_t grey, u8_t *buf, + size_t buf_size); + + +#ifdef CONFIG_ARCH_POSIX +static void posix_exit_main(int exit_code) +{ +#if CONFIG_TEST + if (exit_code == 0) { + LOG_INF("PROJECT EXECUTION SUCCESSFUL"); + } else { + LOG_INF("PROJECT EXECUTION FAILED"); + } +#endif + posix_exit(exit_code); +} +#endif + +static void fill_buffer_argb8888(enum corner corner, u8_t grey, u8_t *buf, + size_t buf_size) +{ + u32_t color = 0; + + switch (corner) { + case TOP_LEFT: + color = 0x00FF0000u; + break; + case TOP_RIGHT: + color = 0x0000FF00u; + break; + case BOTTOM_RIGHT: + color = 0x000000FFu; + break; + case BOTTOM_LEFT: + color = grey << 16 | grey << 8 | grey; + break; + } + + for (size_t idx = 0; idx < buf_size; idx += 4) { + *((u32_t *)(buf + idx)) = color; + } +} + +static void fill_buffer_rgb888(enum corner corner, u8_t grey, u8_t *buf, + size_t buf_size) +{ + u32_t color = 0; + + switch (corner) { + case TOP_LEFT: + color = 0x00FF0000u; + break; + case TOP_RIGHT: + color = 0x0000FF00u; + break; + case BOTTOM_RIGHT: + color = 0x000000FFu; + break; + case BOTTOM_LEFT: + color = grey << 16 | grey << 8 | grey; + break; + } + + for (size_t idx = 0; idx < buf_size; idx += 3) { + *(buf + idx + 0) = color >> 16; + *(buf + idx + 1) = color >> 8; + *(buf + idx + 2) = color >> 0; + } +} + +static void fill_buffer_rgb565(enum corner corner, u8_t grey, u8_t *buf, + size_t buf_size) +{ + u16_t color = 0; + u16_t grey_5bit; + + switch (corner) { + case TOP_LEFT: + color = 0xF800u; + break; + case TOP_RIGHT: + color = 0x07E0u; + break; + case BOTTOM_RIGHT: + color = 0x001Fu; + break; + case BOTTOM_LEFT: + grey_5bit = grey & 0x1Fu; + /* shift the green an extra bit, it has 6 bits */ + color = grey_5bit << 11 | grey_5bit << (5 + 1) | grey_5bit; + break; + } + + for (size_t idx = 0; idx < buf_size; idx += 2) { + *(buf + idx + 0) = (color >> 8) & 0xFFu; + *(buf + idx + 1) = (color >> 0) & 0xFFu; + } +} + +static void fill_buffer_mono(enum corner corner, u8_t grey, u8_t *buf, + size_t buf_size) +{ + u16_t color; + + switch (corner) { + case BOTTOM_LEFT: + color = (grey & 0x01u) ? 0xFFu : 0x00u; + break; + default: + color = 0; + break; + } + + memset(buf, color, buf_size); +} + +void main(void) +{ + size_t x; + size_t y; + size_t rect_w; + size_t rect_h; + size_t h_step; + size_t scale; + size_t grey_count; + u8_t *buf; + s32_t grey_scale_sleep; + struct device *display_dev; + struct display_capabilities capabilities; + struct display_buffer_descriptor buf_desc; + size_t buf_size = 0; + fill_buffer fill_buffer_fnc = NULL; + + LOG_INF("Display sample for %s", DISPLAY_DEV_NAME); + + display_dev = device_get_binding(DISPLAY_DEV_NAME); + + if (display_dev == NULL) { + LOG_ERR("Device %s not found. Aborting sample.", + DISPLAY_DEV_NAME); + RETURN_FROM_MAIN(1); + } + + display_get_capabilities(display_dev, &capabilities); + + if (capabilities.screen_info & SCREEN_INFO_MONO_VTILED) { + rect_w = 16; + rect_h = 8; + } else { + rect_w = 2; + rect_h = 1; + } + + h_step = rect_h; + scale = (capabilities.x_resolution / 8) / rect_h; + + rect_w *= scale; + rect_h *= scale; + + if (capabilities.screen_info & SCREEN_INFO_EPD) { + grey_scale_sleep = K_MSEC(10000); + } else { + grey_scale_sleep = K_MSEC(100); + } + + buf_size = rect_w * rect_h; + + if (buf_size < (capabilities.x_resolution * h_step)) { + buf_size = capabilities.x_resolution * h_step; + } + + switch (capabilities.current_pixel_format) { + case PIXEL_FORMAT_ARGB_8888: + fill_buffer_fnc = fill_buffer_argb8888; + buf_size *= 4; + break; + case PIXEL_FORMAT_RGB_888: + fill_buffer_fnc = fill_buffer_rgb888; + buf_size *= 3; + break; + case PIXEL_FORMAT_RGB_565: + fill_buffer_fnc = fill_buffer_rgb565; + buf_size *= 2; + break; + case PIXEL_FORMAT_MONO01: + case PIXEL_FORMAT_MONO10: + fill_buffer_fnc = fill_buffer_mono; + buf_size /= 8; + break; + default: + LOG_ERR("Unsupported pixel format. Aborting sample."); + RETURN_FROM_MAIN(1); + } + + buf = k_malloc(buf_size); + + if (buf == NULL) { + LOG_ERR("Could not allocate memory. Aborting sample."); + RETURN_FROM_MAIN(1); + } + + (void)memset(buf, 0xFFu, buf_size); + + buf_desc.buf_size = buf_size; + buf_desc.pitch = capabilities.x_resolution; + buf_desc.width = capabilities.x_resolution; + buf_desc.height = h_step; + + for (int idx = 0; idx < capabilities.y_resolution; idx += h_step) { + display_write(display_dev, 0, idx, &buf_desc, buf); + } + + buf_desc.pitch = rect_w; + buf_desc.width = rect_w; + buf_desc.height = rect_h; + + fill_buffer_fnc(TOP_LEFT, 0, buf, buf_size); + x = 0; + y = 0; + display_write(display_dev, x, y, &buf_desc, buf); + + fill_buffer_fnc(TOP_RIGHT, 0, buf, buf_size); + x = capabilities.x_resolution - rect_w; + y = 0; + display_write(display_dev, x, y, &buf_desc, buf); + + fill_buffer_fnc(BOTTOM_RIGHT, 0, buf, buf_size); + x = capabilities.x_resolution - rect_w; + y = capabilities.y_resolution - rect_h; + display_write(display_dev, x, y, &buf_desc, buf); + + display_blanking_off(display_dev); + + grey_count = 0; + x = 0; + y = capabilities.y_resolution - rect_h; + + while (1) { + fill_buffer_fnc(BOTTOM_LEFT, grey_count, buf, buf_size); + display_write(display_dev, x, y, &buf_desc, buf); + ++grey_count; + k_sleep(grey_scale_sleep); +#if CONFIG_TEST + if (grey_count >= 1024) { + break; + } +#endif + } + + RETURN_FROM_MAIN(0); +}