1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

drivers/lcd: add common driver for lcd display

This commit is contained in:
Francisco Molina 2021-03-15 14:58:12 +01:00 committed by Alexandre Abadie
parent e3f9252947
commit 8f2fa772e9
No known key found for this signature in database
GPG Key ID: 1C919A403CAE1405
10 changed files with 651 additions and 0 deletions

View File

@ -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

210
drivers/include/lcd.h Normal file
View File

@ -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 <koen@bergzand.net>
* @author Francisco Molina <francois-xavier.molina@inria.fr>
*
* 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 */
/** @} */

32
drivers/lcd/Kconfig Normal file
View File

@ -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

1
drivers/lcd/Makefile Normal file
View File

@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base

2
drivers/lcd/Makefile.dep Normal file
View File

@ -0,0 +1,2 @@
FEATURES_REQUIRED += periph_spi
FEATURES_REQUIRED += periph_gpio

View File

@ -0,0 +1,2 @@
USEMODULE_INCLUDES_lcd := $(LAST_MAKEFILEDIR)/include
USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_lcd)

View File

@ -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 <alexandre.abadie@inria.fr>
*/
#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 */
/** @} */

View File

@ -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 <koen@bergzand.net>
* @author Francisco Molina <francois-xavier.molina@inria.fr>
*
* @}
*/
#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 */

184
drivers/lcd/lcd.c Normal file
View File

@ -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 <koen@bergzand.net>
* @author Francisco Molina <francois-xavier.molina@inria.fr>
*
* @}
*/
#include <assert.h>
#include <string.h>
#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, &param, 1);
}

View File

@ -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 <alexandre.abadie@inria.fr>
* @}
*/
#include <assert.h>
#include <stdint.h>
#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,
};