/* * Copyright (C) 2018 Koen Zandberg * 2021 Francisco Molina * 2023 Gunar Schorcht * * 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 * * The LCD is a generic display driver for small RGB displays. It communicates * with the device either via an * * - SPI serial interface (if module `lcd_spi` enabled) or an * - MCU 8080 8-/16-bit parallel interface (if module `lcd_parallel` or * module `lcd_parallel_16` is enabled). * * Usually the device driver is used either for a single display with SPI serial * interface or for a display with parallel MCU 8080 8-/16-bit parallel * interface. However, the device driver can also be used simultaneously for * multiple displays with different interfaces if several of the `lcd_spi`, * `lcd_parallel` and `lcd_parallel_16bit` modules are enabled at the same time. * In this case, please refer to the notes in @ref lcd_params_t. * * 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. * * @{ * * @file * * @author Koen Zandberg * @author Francisco Molina * @author Gunar Schorcht * */ #ifndef LCD_H #define LCD_H #include "board.h" #include "mutex.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 /** * @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 */ /** @} */ #if MODULE_LCD_PARALLEL || DOXYGEN /** * @brief Display interface modi * * This enumeration is only needed if the MCU 8080 8-/16-bit interfaces are * enabled by `lcd_parallel` or `lcd_parallel_16bit`. Otherwise the serial * SPI interface is implicitly assumed. */ typedef enum { LCD_IF_SPI, /**< SPI serial interface mode */ LCD_IF_PARALLEL_8BIT, /**< MCU 8080 8-bit parallel interface mode */ LCD_IF_PARALLEL_16BIT, /**< MCU 8080 16-bit parallel interface mode */ } lcd_if_mode_t; #endif /** * @brief Device initialization parameters * * @note The device driver can be used simultaneously for displays with * SPI serial interface and parallel MCU 8080 8-/16-bit interfaces * if the modules `lcd_spi` and `lcd_parallel` or `lcd_parallel_16bit` * are enabled at the same time. In this case the interface parameters * for the SPI serial interface and the MCU 8080 parallel 8-/16-bit * interfaces are defined. @ref lcd_params_t::spi must then be set to * @ref SPI_UNDEF for displays that use the MCU-8080-parallel-8-/16-bit * interface, i.e. @ref lcd_params_t::spi is then used to detect the * interface mode. */ typedef struct { #if MODULE_LCD_SPI || DOXYGEN /* Interface parameters used for serial interface */ 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 */ #endif #if MODULE_LCD_PARALLEL || DOXYGEN lcd_if_mode_t mode; /**< LCD driver interface mode */ /* Interface parameters used for MCU 8080 8-bit parallel interface */ gpio_t wrx_pin; /**< pin connected to the WRITE ENABLE line */ gpio_t rdx_pin; /**< pin connected to the READ ENABLE line */ gpio_t d0_pin; /**< pin connected to the D0 line */ gpio_t d1_pin; /**< pin connected to the D1 line */ gpio_t d2_pin; /**< pin connected to the D2 line */ gpio_t d3_pin; /**< pin connected to the D3 line */ gpio_t d4_pin; /**< pin connected to the D4 line */ gpio_t d5_pin; /**< pin connected to the D5 line */ gpio_t d6_pin; /**< pin connected to the D6 line */ gpio_t d7_pin; /**< pin connected to the D7 line */ #if MODULE_LCD_PARALLEL_16BIT || DOXYGEN /* Interface parameters used for MCU 8080 16-bit parallel interface */ gpio_t d8_pin; /**< pin connected to the D8 line */ gpio_t d9_pin; /**< pin connected to the D9 line */ gpio_t d10_pin; /**< pin connected to the D10 line */ gpio_t d11_pin; /**< pin connected to the D11 line */ gpio_t d12_pin; /**< pin connected to the D12 line */ gpio_t d13_pin; /**< pin connected to the D13 line */ gpio_t d14_pin; /**< pin connected to the D14 line */ gpio_t d15_pin; /**< pin connected to the D15 line */ #endif /* MODULE_LCD_PARALLEL_16BIT */ #endif /* MODULE_LCD_PARALLEL */ /* Common interface parameters */ 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\n 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 the number of lines supported by the driver IC in 8 line steps */ uint16_t rgb_channels; /**< Display rgb channels */ uint8_t rotation; /**< Display rotation mode */ uint8_t offset_x; /**< LCD offset to apply on x axis. */ uint8_t offset_y; /**< LCD offset to apply on y axis. */ #if MODULE_LCD_MULTI_CNTRL || DOXYGEN uint8_t cntrl; /**< controller variant used, if the controller- specific driver supports multiple controller variants */ #endif } 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 { #if MODULE_DISP_DEV || DOXYGEN 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 */ #if MODULE_LCD_PARALLEL || DOXYGEN mutex_t lock; /**< Mutex used to lock the device in MCU 8080 parallel interface mode */ #endif #if MODULE_LCD_PARALLEL_16BIT || DOXYGEN bool word_access; /**< indicates that a word access is active */ #endif } lcd_t; /** * @brief LCD driver interface * * This defines 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 the LCD work area * * This function pointer can be NULL if the controller specific driver * does not require anything special. In this case the default * implementation is used which sets the column addresses and the row * addresses of the area including the coordinates of the opposite corner. * * @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)(lcd_t *dev, uint16_t x1, uint16_t x2, uint16_t y1, uint16_t y2); }; /** * @name Low-level LCD API * * Low-level functions are used to acquire a device, write commands with data * to the device, or read data from the device and release it when it is no * longer needed. They are usually called by the high-level functions such * as @ref lcd_init, @ref lcd_fill, @ref lcd_pixmap, etc., but can also be * used by the application to implement low-level operations if needed. * * @{ */ /** * @brief Low-level function to acquire the device * * @param[out] dev device descriptor */ void lcd_ll_acquire(lcd_t *dev); /** * @brief Low-level function to release the device * * @param[out] dev device descriptor */ void lcd_ll_release(lcd_t *dev); /** * @brief Low-level function to write a command * * @pre The device must have already been acquired with @ref lcd_ll_acquire * before this function can be called. * * @param[in] dev device descriptor * @param[in] cmd command code * @param[in] data command data to the device or NULL for commands without data * @param[in] len length of the command data or 0 for commands without data */ void lcd_ll_write_cmd(lcd_t *dev, uint8_t cmd, const uint8_t *data, size_t len); /** * @brief Low-level function for read command * * @note Very often the SPI MISO signal of the serial interface or the RDX * signal of the MCU 8080 parallel interface are not connected to the * display. In this case the read command does not provide valid data. * * @pre The device must have already been acquired with @ref lcd_ll_acquire * before this function can be called. * @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_ll_read_cmd(lcd_t *dev, uint8_t cmd, uint8_t *data, size_t len); /** * @brief Set the 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 lcd_ll_set_area(lcd_t *dev, uint16_t x1, uint16_t x2, uint16_t y1, uint16_t y2); /** @} */ /** * @name High-level LCD API * * The functions of the high-level LCD API are used by the application. They * use the low-level LCD API to implement more complex operations. * * @{ */ /** * @brief Setup an LCD display device * * @param[in] 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(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(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 or NULL for commands without data * @param[in] len length of the command data or 0 for commands without data */ void lcd_write_cmd(lcd_t *dev, uint8_t cmd, const uint8_t *data, size_t len); /** * @brief Raw read command * * @note Very often the SPI MISO signal of the serial interface or the RDX * signal of the MCU 8080 parallel interface are not connected to the * display. In this case the read command does not provide valid data. * * @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(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(lcd_t *dev); /** * @brief Disable color inversion * * @param[in] dev device descriptor */ void lcd_invert_off(lcd_t *dev); /** @} */ #if MODULE_LCD_PARALLEL || DOXYGEN /** * @name Low-level MCU 8080 8-/16-bit parallel interface * * The low-level MCU 8080 8-/16-bit parallel interface (low-level parallel * interface for short) is used when the LCD device is connected via a parallel * interface. Either the GPIO-driven low-level parallel interface provided by * this LCD driver or a low-level parallel interface implemented by the MCU, * such as the STM32 FMC peripheral, can be used. If the MCU provides its * own implementation of the low-level parallel interface, it can be used * by implementing the following low-level parallel interface driver functions, * enabling the `lcd_parallel_ll_mcu` module and defining the * @ref lcd_ll_par_driver variable of type @ref lcd_ll_par_driver_t. * * @{ */ /** * @brief Low-level MCU 8080 8-/16-bit parallel interface driver * * If the MCU-driven low-level parallel interface is enabled by * module `lcd_ll_parallel_mcu`, the implementation of the MCU low-level * parallel interface has to define a variable @ref lcd_ll_par_driver of this * type. All or a set of members have to point to the low-level parallel * interface functions implemented by the MCU. For functions that are not * implemented by the MCU, the members have to be set to the corresponding * GPIO-driven low-level parallel interface functions provided by the LCD * driver. */ typedef struct { /** * @brief Initialize the MCU-driven low-level parallel interface * * @param[in] dev device descriptor */ void (*init)(lcd_t *dev); /** * @brief Set the data direction of the low-level parallel interface * * @param[in] dev device descriptor * @param[in] output set to output mode if true and to input mode otherwise */ void (*set_data_dir)(lcd_t *dev, bool output); /** * @brief Write command using the MCU-driven low-level parallel interface * * @param[in] dev device descriptor * @param[in] cmd command * @param[in] cont operation is continued */ void (*cmd_start)(lcd_t *dev, uint8_t cmd, bool cont); /** * @brief Write a byte using the MCU-driven low-level parallel interface * * @param[in] dev device descriptor * @param[in] cont operation is continued * @param[in] out byte to be written */ void (*write_byte)(lcd_t *dev, bool cont, uint8_t out); /** * @brief Read a byte using the MCU-driven low-level parallel interface * * @param[in] dev device descriptor * @param[in] cont operation is continued * * @return byte read */ uint8_t (*read_byte)(lcd_t *dev, bool cont); #if MODULE_LCD_PARALLEL_16BIT || DOXYGEN /** * @brief Write a word using the MCU-driven low-level parallel interface * * @param[in] dev device descriptor * @param[in] cont operation is continued * @param[in] out word to be written */ void (*write_word)(lcd_t *dev, bool cont, uint16_t out); /** * @brief Read a word using the MCU-driven low-level parallel interface * * @param[in] dev device descriptor * @param[in] cont operation is continued * * @return word read */ uint16_t (*read_word)(lcd_t *dev, bool cont); #endif } lcd_ll_par_driver_t; /** * @brief Low-level parallel interface driver instance */ extern const lcd_ll_par_driver_t lcd_ll_par_driver; /** @} */ #endif #ifdef __cplusplus } #endif #endif /* LCD_H */ /** @} */