From 8f2fa772e94cdcab10da6990f8b0645e7ec9af1c Mon Sep 17 00:00:00 2001 From: Francisco Molina Date: Mon, 15 Mar 2021 14:58:12 +0100 Subject: [PATCH] drivers/lcd: add common driver for lcd display --- drivers/Kconfig | 1 + drivers/include/lcd.h | 210 +++++++++++++++++++++++++++++ drivers/lcd/Kconfig | 32 +++++ drivers/lcd/Makefile | 1 + drivers/lcd/Makefile.dep | 2 + drivers/lcd/Makefile.include | 2 + drivers/lcd/include/lcd_disp_dev.h | 38 ++++++ drivers/lcd/include/lcd_internal.h | 102 ++++++++++++++ drivers/lcd/lcd.c | 184 +++++++++++++++++++++++++ drivers/lcd/lcd_disp_dev.c | 79 +++++++++++ 10 files changed, 651 insertions(+) create mode 100644 drivers/include/lcd.h create mode 100644 drivers/lcd/Kconfig create mode 100644 drivers/lcd/Makefile create mode 100644 drivers/lcd/Makefile.dep create mode 100644 drivers/lcd/Makefile.include create mode 100644 drivers/lcd/include/lcd_disp_dev.h create mode 100644 drivers/lcd/include/lcd_internal.h create mode 100644 drivers/lcd/lcd.c create mode 100644 drivers/lcd/lcd_disp_dev.c diff --git a/drivers/Kconfig b/drivers/Kconfig index aec35ab748..5b7fc91462 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -32,6 +32,7 @@ rsource "disp_dev/Kconfig" rsource "dsp0401/Kconfig" rsource "hd44780/Kconfig" rsource "ili9341/Kconfig" +rsource "lcd/Kconfig" rsource "touch_dev/Kconfig" endmenu # Display Device Drivers diff --git a/drivers/include/lcd.h b/drivers/include/lcd.h new file mode 100644 index 0000000000..b3129fc606 --- /dev/null +++ b/drivers/include/lcd.h @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2018 Koen Zandberg + * 2021 Francisco Molina + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup drivers_lcd LCD display driver + * @ingroup drivers_display + * + * @brief Driver for the LCD display + * + * @{ + * + * @file + * + * @author Koen Zandberg + * @author Francisco Molina + * + * The LCD is a generic display driver for small RGB displays. The driver + * implemented here operates over SPI to communicate with the device. + * + * The device requires colors to be send in big endian RGB-565 format. The + * @ref CONFIG_LCD_LE_MODE compile time option can switch this, but only use this + * when strictly necessary. This option will slow down the driver as it + * certainly can't use DMA anymore, every short has to be converted before + * transfer. + */ + + +#ifndef LCD_H +#define LCD_H + +#include "board.h" +#include "periph/spi.h" +#include "periph/gpio.h" + +#ifdef MODULE_DISP_DEV +#include "disp_dev.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Convert little endian colors to big endian. + * + * Compile time switch to change the driver to convert little endian + * colors to big endian. + */ +#ifdef DOXYGEN +#define CONFIG_LCD_LE_MODE +#endif + +/** + * @brief Device initialization parameters + */ +typedef struct { + spi_t spi; /**< SPI device that the display is connected to */ + spi_clk_t spi_clk; /**< SPI clock speed to use */ + spi_mode_t spi_mode;/**< SPI mode */ + gpio_t cs_pin; /**< pin connected to the CHIP SELECT line */ + gpio_t dcx_pin; /**< pin connected to the DC line */ + gpio_t rst_pin; /**< pin connected to the reset line */ + bool rgb; /**< True when display is connected in RGB mode + * False when display is connected in BGR mode */ + bool inverted; /**< Display works in inverted color mode */ + uint16_t lines; /**< Number of lines, from 16 to 320 in 8 line steps */ + uint16_t rgb_channels; /**< Display rgb channels */ +} lcd_params_t; + +/** + * @brief LCD driver interface + * + * This define the functions to access a LCD. + */ +typedef struct lcd_driver lcd_driver_t; + +/** + * @brief Device descriptor for a lcd + */ +typedef struct { +#ifdef MODULE_DISP_DEV + disp_dev_t *dev; /**< Pointer to the generic display device */ +#endif + const lcd_driver_t *driver; /**< LCD driver */ + const lcd_params_t *params; /**< Device initialization parameters */ +} lcd_t; + +/** + * @brief LCD driver interface + * + * This define the functions to access a LCD. + * + */ +struct lcd_driver { + /** + * @brief Initialize LCD controller + * + * @param[in] dev Pointer to the selected driver + * + * @returns 0 on success + * @returns < 0 value in error + */ + int (*init)(lcd_t *dev, const lcd_params_t *params); + + /** + * @brief Set area LCD work area + * + * @param[in] dev Pointer to the selected driver + * @param[in] x1 x coordinate of the first corner + * @param[in] x2 x coordinate of the opposite corner + * @param[in] y1 y coordinate of the first corner + * @param[in] y2 y coordinate of the opposite corner + * + */ + void (*set_area)(const lcd_t *dev, uint16_t x1, uint16_t x2, uint16_t y1, + uint16_t y2); +}; + +/** + * @brief Setup an lcd display device + * + * @param[out] dev device descriptor + * @param[in] params parameters for device initialization + */ +int lcd_init(lcd_t *dev, const lcd_params_t *params); + +/** + * @brief Fill a rectangular area with a single pixel color + * + * the rectangular area is defined as x1 being the first column of pixels and + * x2 being the last column of pixels to fill. similar to that, y1 is the first + * row to fill and y2 is the last row to fill. + * + * @param[in] dev device descriptor + * @param[in] x1 x coordinate of the first corner + * @param[in] x2 x coordinate of the opposite corner + * @param[in] y1 y coordinate of the first corner + * @param[in] y2 y coordinate of the opposite corner + * @param[in] color single color to fill the area with + */ +void lcd_fill(const lcd_t *dev, uint16_t x1, uint16_t x2, + uint16_t y1, uint16_t y2, uint16_t color); + +/** + * @brief Fill a rectangular area with an array of pixels + * + * the rectangular area is defined as x1 being the first column of pixels and + * x2 being the last column of pixels to fill. similar to that, y1 is the first + * row to fill and y2 is the last row to fill. + * + * @note @p color must have a length equal to `(x2 - x1 + 1) * (y2 - y1 + 1)` + * + * @param[in] dev device descriptor + * @param[in] x1 x coordinate of the first corner + * @param[in] x2 x coordinate of the opposite corner + * @param[in] y1 y coordinate of the first corner + * @param[in] y2 y coordinate of the opposite corner + * @param[in] color array of colors to fill the area with + */ +void lcd_pixmap(const lcd_t *dev, uint16_t x1, uint16_t x2, uint16_t y1, + uint16_t y2, const uint16_t *color); + +/** + * @brief Raw write command + * + * @param[in] dev device descriptor + * @param[in] cmd command code + * @param[in] data command data to the device + * @param[in] len length of the command data + */ +void lcd_write_cmd(const lcd_t *dev, uint8_t cmd, const uint8_t *data, + size_t len); + +/** + * @brief Raw read command + * + * @pre len > 0 + * + * @param[in] dev device descriptor + * @param[in] cmd command + * @param[out] data data from the device + * @param[in] len length of the returned data + */ +void lcd_read_cmd(const lcd_t *dev, uint8_t cmd, uint8_t *data, size_t len); + +/** + * @brief Invert the display colors + * + * @param[in] dev device descriptor + */ +void lcd_invert_on(const lcd_t *dev); + +/** + * @brief Disable color inversion + * + * @param[in] dev device descriptor + */ +void lcd_invert_off(const lcd_t *dev); + +#ifdef __cplusplus +} +#endif +#endif /* LCD_H */ +/** @} */ diff --git a/drivers/lcd/Kconfig b/drivers/lcd/Kconfig new file mode 100644 index 0000000000..d5036cc0df --- /dev/null +++ b/drivers/lcd/Kconfig @@ -0,0 +1,32 @@ +# Copyright (c) 2020 HAW Hamburg +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. +# + +config MODULE_LCD + bool "LCD display driver" + depends on HAS_PERIPH_SPI + depends on HAS_PERIPH_GPIO + depends on TEST_KCONFIG + select MODULE_PERIPH_SPI + select MODULE_PERIPH_GPIO + +menuconfig KCONFIG_USEMODULE_LCD + bool "Configure LCD driver" + depends on USEMODULE_LCD + help + Configure the LCD display driver using Kconfig. + +if KCONFIG_USEMODULE_LCD + +config LCD_LE_MODE + bool "Enable little endian to big endian conversion" + help + Enable this configuration to convert little endian colors to big endian. + LCD device requires colors to be send in big endian RGB-565 format. + Enabling this option allows for little endian colors. Enabling this + however will slow down the driver as it cannot use DMA anymore. + +endif # KCONFIG_USEMODULE_LCD diff --git a/drivers/lcd/Makefile b/drivers/lcd/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/drivers/lcd/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/lcd/Makefile.dep b/drivers/lcd/Makefile.dep new file mode 100644 index 0000000000..81b9570471 --- /dev/null +++ b/drivers/lcd/Makefile.dep @@ -0,0 +1,2 @@ +FEATURES_REQUIRED += periph_spi +FEATURES_REQUIRED += periph_gpio diff --git a/drivers/lcd/Makefile.include b/drivers/lcd/Makefile.include new file mode 100644 index 0000000000..29c438bfd9 --- /dev/null +++ b/drivers/lcd/Makefile.include @@ -0,0 +1,2 @@ +USEMODULE_INCLUDES_lcd := $(LAST_MAKEFILEDIR)/include +USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_lcd) diff --git a/drivers/lcd/include/lcd_disp_dev.h b/drivers/lcd/include/lcd_disp_dev.h new file mode 100644 index 0000000000..cbfefa1ce9 --- /dev/null +++ b/drivers/lcd/include/lcd_disp_dev.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2019 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_lcd + * @{ + * + * @file + * @brief Definition of the driver for the disp_dev generic interface + * + * @author Alexandre Abadie + */ + +#ifndef LCD_DISP_DEV_H +#define LCD_DISP_DEV_H + +#include "disp_dev.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Reference to the display device driver struct + */ +extern const disp_dev_driver_t lcd_disp_dev_driver; + +#ifdef __cplusplus +} +#endif + +#endif /* LCD_DISP_DEV_H */ +/** @} */ diff --git a/drivers/lcd/include/lcd_internal.h b/drivers/lcd/include/lcd_internal.h new file mode 100644 index 0000000000..032aa78b54 --- /dev/null +++ b/drivers/lcd/include/lcd_internal.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2018 Koen Zandberg + * 2021 Francisco Molina + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_lcd + * @{ + * + * @file + * @brief Device driver implementation for the lcd display controller + * + * @author Koen Zandberg + * @author Francisco Molina + * + * @} + */ + +#ifndef LCD_INTERNAL_H +#define LCD_INTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name LCD commands + * + * Not exhaustive, please extend when required + * @{ + */ +#define LCD_CMD_SWRESET 0x01 /**< Software reset */ +#define LCD_CMD_RDDIDIF 0x04 /**< Read display ID */ +#define LCD_CMD_SPLIN 0x10 /**< Enter sleep mode */ +#define LCD_CMD_SLPOUT 0x11 /**< Sleep out */ +#define LCD_CMD_NORON 0x11 /**< Normal display mode on */ +#define LCD_CMD_DINVOFF 0x20 /**< Display inversion off */ +#define LCD_CMD_DINVON 0x21 /**< Display inversion on */ + +#define LCD_CMD_GAMSET 0x26 /**< Gamma Set */ +#define LCD_CMD_DISPOFF 0x28 /**< Display OFF */ +#define LCD_CMD_DISPON 0x29 /**< Display ON */ +#define LCD_CMD_CASET 0x2A /**< Column Address Set */ +#define LCD_CMD_PASET 0x2b /**< Page Address Set */ +#define LCD_CMD_RAMWR 0x2c /**< Memory Write */ +#define LCD_CMD_RAMRD 0x2e /**< Memory Read */ +#define LCD_CMD_MADCTL 0x36 /**< Memory data access control */ +#define LCD_CMD_IDMOFF 0x38 /**< Idle Mode OFF */ +#define LCD_CMD_IDMON 0x39 /**< Idle Mode ON */ +#define LCD_CMD_PIXSET 0x3A /**< COLMOD: Pixel Format Set */ +#define LCD_CMD_WRDISBV 0x51 /**< Write Display Brightness */ +#define LCD_CMD_WRCTRLD 0x53 /**< Write Control Display */ +#define LCD_CMD_RDCTRLD 0x54 /**< Read Control Display */ +#define LCD_CMD_FRAMECTL1 0xb1 /**< Frame control normal*/ +#define LCD_CMD_FRAMECTL2 0xb2 /**< Frame control idle */ +#define LCD_CMD_FRAMECTL3 0xb3 /**< Frame control partial */ +#define LCD_CMD_DFUNC 0xb6 /**< Display function control */ +#define LCD_CMD_PWCTRL1 0xc0 /**< Power control 1 */ +#define LCD_CMD_PWCTRL2 0xc1 /**< Power control 2 */ +#define LCD_CMD_VMCTRL1 0xc5 /**< VCOM control 1 */ +#define LCD_CMD_VMCTRL2 0xc7 /**< VCOM control 2 */ +#define LCD_CMD_PGAMCTRL 0xe0 /**< Positive gamma correction */ +#define LCD_CMD_NGAMCTRL 0xe1 /**< Negative gamma correction */ +#define LCD_CMD_IFCTL 0xf6 /**< Interface control */ +/** @} */ + +/** + * @name Memory access control bits + * @{ + */ +#define LCD_MADCTL_MY 0x80 /**< Row address order */ +#define LCD_MADCTL_MX 0x40 /**< Column access order */ +#define LCD_MADCTL_MV 0x20 /**< Row column exchange */ +#define LCD_MADCTL_ML 0x10 /**< Vertical refresh order */ +#define LCD_MADCTL_BGR 0x08 /**< Color selector switch control */ +#define LCD_MADCTL_MH 0x04 /**< Horizontal refresh direction */ +/** @} */ + +/** + * @name Display rotation modes + * @{ + */ +#define LCD_MADCTL_VERT LCD_MADCTL_MX /**< Vertical mode */ +#define LCD_MADCTL_VERT_FLIP LCD_MADCTL_MY /**< Flipped vertical */ +#define LCD_MADCTL_HORZ LCD_MADCTL_MV /**< Horizontal mode */ +#define LCD_MADCTL_HORZ_FLIP LCD_MADCTL_MV | \ + LCD_MADCTL_MY | \ + LCD_MADCTL_MX /**< Horizontal flipped */ +/** @} */ + +#define LCD_PIXSET_16BIT 0x55 /**< MCU and RGB 16 bit interface */ +#define LCD_PIXSET_18BIT 0x66 /**< MCU and RGB 18 bit interface (not implemented) */ + +#ifdef __cplusplus +} +#endif + +#endif /* LCD_INTERNAL_H */ diff --git a/drivers/lcd/lcd.c b/drivers/lcd/lcd.c new file mode 100644 index 0000000000..bb6b0cfd74 --- /dev/null +++ b/drivers/lcd/lcd.c @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2018 Koen Zandberg + * 2021 Francisco Molina + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_lcd + * @{ + * + * @file + * @brief Device driver implementation for the lcd display controller + * + * @author Koen Zandberg + * @author Francisco Molina + * + * @} + */ + +#include +#include +#include "byteorder.h" +#include "periph/spi.h" +#include "kernel_defines.h" + +#include "lcd.h" +#include "lcd_internal.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +static void _lcd_spi_acquire(const lcd_t *dev) +{ + spi_acquire(dev->params->spi, dev->params->cs_pin, dev->params->spi_mode, + dev->params->spi_clk); +} + +static void _lcd_cmd_start(const lcd_t *dev, uint8_t cmd, bool cont) +{ + gpio_clear(dev->params->dcx_pin); + spi_transfer_byte(dev->params->spi, dev->params->cs_pin, cont, cmd); + gpio_set(dev->params->dcx_pin); +} + +static void _write_cmd(const lcd_t *dev, uint8_t cmd, const uint8_t *data, + size_t len) +{ + _lcd_cmd_start(dev, cmd, len ? true : false); + if (len) { + spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, false, data, + NULL, len); + } +} + +static void _lcd_set_area(const lcd_t *dev, uint16_t x1, uint16_t x2, + uint16_t y1, uint16_t y2) +{ + assert(dev->driver->set_area); + dev->driver->set_area(dev, x1, x2, y1, y2); +} + +int lcd_init(lcd_t *dev, const lcd_params_t *params) +{ + if (dev->driver->init) { + return dev->driver->init(dev, params); + } + else { + return -ENOTSUP; + } +} + +void lcd_write_cmd(const lcd_t *dev, uint8_t cmd, const uint8_t *data, + size_t len) +{ + _lcd_spi_acquire(dev); + _write_cmd(dev, cmd, data, len); + spi_release(dev->params->spi); +} + +void lcd_read_cmd(const lcd_t *dev, uint8_t cmd, uint8_t *data, size_t len) +{ + assert(len); + _lcd_spi_acquire(dev); + _lcd_cmd_start(dev, cmd, true); + /* Dummy transfer */ + spi_transfer_byte(dev->params->spi, dev->params->cs_pin, true, 0x00); + spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, false, NULL, + data, len); + spi_release(dev->params->spi); +} + + +void lcd_fill(const lcd_t *dev, uint16_t x1, uint16_t x2, uint16_t y1, + uint16_t y2, uint16_t color) +{ + /* Send fill area to the display */ + + /* Calculate number of pixels */ + int32_t num_pix = (x2 - x1 + 1) * (y2 - y1 + 1); + + DEBUG("[lcd]: Write x1: %" PRIu16 ", x2: %" PRIu16 ", " + "y1: %" PRIu16 ", y2: %" PRIu16 ". Num pixels: %lu\n", + x1, x2, y1, y2, (unsigned long)num_pix); + + /* Send fill area to the display */ + _lcd_spi_acquire(dev); + + _lcd_set_area(dev, x1, x2, y1, y2); + /* Memory access command */ + _lcd_cmd_start(dev, LCD_CMD_RAMWR, true); + + if (IS_ACTIVE(CONFIG_LCD_LE_MODE)) { + color = htons(color); + } + + for (int i = 0; i < (num_pix - 1); i++) { + spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, true, + (uint8_t *)&color, NULL, sizeof(color)); + } + spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, false, + (uint8_t *)&color, NULL, sizeof(color)); + spi_release(dev->params->spi); +} + +void lcd_pixmap(const lcd_t *dev, uint16_t x1, uint16_t x2, + uint16_t y1, uint16_t y2, const uint16_t *color) +{ + size_t num_pix = (x2 - x1 + 1) * (y2 - y1 + 1); + + DEBUG("[lcd]: Write x1: %" PRIu16 ", x2: %" PRIu16 ", " + "y1: %" PRIu16 ", y2: %" PRIu16 ". Num pixels: %lu\n", + x1, x2, y1, y2, (unsigned long)num_pix); + + _lcd_spi_acquire(dev); + + /* Send fill area to the display */ + _lcd_set_area(dev, x1, x2, y1, y2); + + /* Memory access command */ + _lcd_cmd_start(dev, LCD_CMD_RAMWR, true); + + if (IS_ACTIVE(CONFIG_LCD_LE_MODE)) { + for (size_t i = 0; i < num_pix - 1; i++) { + uint16_t ncolor = htons(*(color + i)); + spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, true, + &ncolor, NULL, sizeof(uint16_t)); + } + uint16_t ncolor = htons(*(color + num_pix - 1)); + spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, false, + &ncolor, NULL, sizeof(uint16_t)); + } + else { + spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, false, + (const uint8_t *)color, NULL, num_pix * 2); + } + + spi_release(dev->params->spi); +} + +void lcd_invert_on(const lcd_t *dev) +{ + uint8_t command = (dev->params->inverted) ? LCD_CMD_DINVOFF + : LCD_CMD_DINVON; + + lcd_write_cmd(dev, command, NULL, 0); +} + +void lcd_invert_off(const lcd_t *dev) +{ + uint8_t command = (dev->params->inverted) ? LCD_CMD_DINVON + : LCD_CMD_DINVOFF; + + lcd_write_cmd(dev, command, NULL, 0); +} + +void lcd_set_brightness(const lcd_t *dev, uint8_t brightness) +{ + lcd_write_cmd(dev, LCD_CMD_WRDISBV, &brightness, 1); + uint8_t param = 0x26; + lcd_write_cmd(dev, LCD_CMD_WRCTRLD, ¶m, 1); +} diff --git a/drivers/lcd/lcd_disp_dev.c b/drivers/lcd/lcd_disp_dev.c new file mode 100644 index 0000000000..0199a37916 --- /dev/null +++ b/drivers/lcd/lcd_disp_dev.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2020 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_lcd + * @{ + * + * @file + * @brief Driver adaption to disp_dev generic interface + * + * @author Alexandre Abadie + * @} + */ + +#include +#include + +#include "lcd.h" +#include "lcd_disp_dev.h" + +#ifndef LCD_DISP_COLOR_DEPTH +#define LCD_DISP_COLOR_DEPTH (16U) +#endif + +static void _lcd_map(const disp_dev_t *dev, uint16_t x1, uint16_t x2, + uint16_t y1, uint16_t y2, const uint16_t *color) +{ + lcd_t *lcd = (lcd_t *)dev; + lcd_pixmap(lcd, x1, x2, y1, y2, color); +} + +static uint16_t _lcd_height(const disp_dev_t *disp_dev) +{ + const lcd_t *dev = (lcd_t *)disp_dev; + assert(dev); + + return dev->params->rgb_channels; +} + +static uint16_t _lcd_width(const disp_dev_t *disp_dev) +{ + const lcd_t *dev = (lcd_t *)disp_dev; + assert(dev); + + return dev->params->lines; +} + +static uint8_t _lcd_color_depth(const disp_dev_t *disp_dev) +{ + (void)disp_dev; + return LCD_DISP_COLOR_DEPTH; +} + +static void _lcd_set_invert(const disp_dev_t *disp_dev, bool invert) +{ + const lcd_t *dev = (lcd_t *)disp_dev; + + assert(dev); + + if (invert) { + lcd_invert_on(dev); + } + else { + lcd_invert_off(dev); + } +} + +const disp_dev_driver_t lcd_disp_dev_driver = { + .map = _lcd_map, + .height = _lcd_height, + .width = _lcd_width, + .color_depth = _lcd_color_depth, + .set_invert = _lcd_set_invert, +};