mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge #19816
19816: drivers/lcd: code deduplication for st7735 and ili9341 r=aabadie a=gschorcht ### Contribution description In preparation for the parallel interface support the following changes were made: 1. The code for basic communication (acquire/release SPI device, SPI transfers), which were always duplicated by copy & paste in each display driver again and again, has been moved to the LCD driver as low-level functions that are now used by the display drivers. These low level function allow - code deduplication and - to define a more abstract communication interface that can then be extended later by parallel communication interface 2. Identical GPIO initializations has also been moved from display drivers to the LCD driver. 3. Using a default implementation of `lcd_set_area` function allows further code deduplication. Finally, the `ili9341` and `st7735` drivers only implement the inialization sequence for the LCD driver IC. ### Testing procedure `tests/drivers/ili9341` should still work for a board with an ILI9341. Tested with `esp32-wrover-kit`. `tests/drivers/st7735` should still work for a board with a ST77xx. Tested with `esp32s3-usb-otg`. ### Issues/PRs references Co-authored-by: Gunar Schorcht <gunar@schorcht.net>
This commit is contained in:
commit
ae2118cbf6
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Koen Zandberg
|
||||
* 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
|
||||
@ -14,6 +15,7 @@
|
||||
* @brief Device driver implementation for the ili9341 display controller
|
||||
*
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
* @author Gunar Schorcht <gunar@schorcht.net>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
@ -22,8 +24,8 @@
|
||||
#include <string.h>
|
||||
#include "byteorder.h"
|
||||
#include "periph/spi.h"
|
||||
#include "ztimer.h"
|
||||
#include "kernel_defines.h"
|
||||
#include "ztimer.h"
|
||||
|
||||
#include "ili9341.h"
|
||||
#include "ili9341_internal.h"
|
||||
@ -33,18 +35,6 @@
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
static void _write_cmd(const lcd_t *dev, uint8_t cmd, const uint8_t *data,
|
||||
size_t len)
|
||||
{
|
||||
gpio_clear(dev->params->dcx_pin);
|
||||
spi_transfer_byte(dev->params->spi, dev->params->cs_pin, len ? true : false, cmd);
|
||||
gpio_set(dev->params->dcx_pin);
|
||||
if (len) {
|
||||
spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, false, data,
|
||||
NULL, len);
|
||||
}
|
||||
}
|
||||
|
||||
/* datasheet page 178, table converted to equation.
|
||||
* gvdd in 1mv increments: 4850 = 4.85V */
|
||||
static uint8_t _ili9341_calc_pwrctl1(uint16_t gvdd)
|
||||
@ -66,72 +56,57 @@ static uint8_t _ili9341_calc_vml(int16_t vcoml)
|
||||
static int _init(lcd_t *dev, const lcd_params_t *params)
|
||||
{
|
||||
assert(params->lines >= 16 && params->lines <= 320 && !(params->lines & 0x7));
|
||||
dev->params = params;
|
||||
uint8_t command_params[4] = { 0 };
|
||||
gpio_init(dev->params->dcx_pin, GPIO_OUT);
|
||||
int res = spi_init_cs(dev->params->spi, dev->params->cs_pin);
|
||||
if (res != SPI_OK) {
|
||||
DEBUG("[ili9341] init: error initializing the CS pin [%i]\n", res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(dev->params->rst_pin)) {
|
||||
gpio_init(dev->params->rst_pin, GPIO_OUT);
|
||||
gpio_clear(dev->params->rst_pin);
|
||||
ztimer_sleep(ZTIMER_MSEC, 120);
|
||||
gpio_set(dev->params->rst_pin);
|
||||
}
|
||||
ztimer_sleep(ZTIMER_MSEC, 120);
|
||||
uint8_t command_params[4] = { 0 };
|
||||
|
||||
/* Acquire once at release at the end */
|
||||
spi_acquire(dev->params->spi, dev->params->cs_pin, dev->params->spi_mode,
|
||||
dev->params->spi_clk);
|
||||
lcd_ll_acquire(dev);
|
||||
|
||||
/* Soft Reset */
|
||||
_write_cmd(dev, LCD_CMD_SWRESET, NULL, 0);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_SWRESET, NULL, 0);
|
||||
ztimer_sleep(ZTIMER_MSEC, 120);
|
||||
|
||||
/* Display off */
|
||||
_write_cmd(dev, LCD_CMD_DISPOFF, NULL, 0);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_DISPOFF, NULL, 0);
|
||||
|
||||
/* PWRCTL1/2 */
|
||||
command_params[0] = _ili9341_calc_pwrctl1(CONFIG_ILI9341_GVDD);
|
||||
_write_cmd(dev, LCD_CMD_PWCTRL1, command_params, 1);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_PWCTRL1, command_params, 1);
|
||||
|
||||
command_params[0] = 0x10; /* PWRCTL 0 0 0 */
|
||||
_write_cmd(dev, LCD_CMD_PWCTRL2, command_params, 1);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_PWCTRL2, command_params, 1);
|
||||
|
||||
/* VCOMCTL */
|
||||
command_params[0] = _ili9341_calc_vmh(CONFIG_ILI9341_VCOMH);
|
||||
command_params[1] = _ili9341_calc_vml(CONFIG_ILI9341_VCOML);
|
||||
_write_cmd(dev, LCD_CMD_VMCTRL1, command_params, 2);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_VMCTRL1, command_params, 2);
|
||||
|
||||
command_params[0] = 0x86;
|
||||
_write_cmd(dev, LCD_CMD_VMCTRL2, command_params, 1);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_VMCTRL2, command_params, 1);
|
||||
|
||||
/* Memory access CTL */
|
||||
command_params[0] = dev->params->rotation;
|
||||
command_params[0] |= dev->params->rgb ? 0 : LCD_MADCTL_BGR;
|
||||
_write_cmd(dev, LCD_CMD_MADCTL, command_params, 1);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_MADCTL, command_params, 1);
|
||||
|
||||
/* Frame control */
|
||||
command_params[0] = 0x00;
|
||||
command_params[1] = 0x18;
|
||||
_write_cmd(dev, LCD_CMD_FRAMECTL1, command_params, 2);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_FRAMECTL1, command_params, 2);
|
||||
|
||||
/* Display function control */
|
||||
command_params[0] = 0x08;
|
||||
command_params[1] = 0x82;
|
||||
/* number of lines, see datasheet p. 166 (DISCTRL::NL) */
|
||||
command_params[2] = (params->lines >> 3) - 1;
|
||||
_write_cmd(dev, LCD_CMD_DFUNC, command_params, 3);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_DFUNC, command_params, 3);
|
||||
|
||||
/* Pixel format */
|
||||
command_params[0] = 0x55; /* 16 bit mode */
|
||||
_write_cmd(dev, LCD_CMD_PIXSET, command_params, 1);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_PIXSET, command_params, 1);
|
||||
|
||||
command_params[0] = 0x01;
|
||||
_write_cmd(dev, LCD_CMD_GAMSET, command_params, 1);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_GAMSET, command_params, 1);
|
||||
|
||||
/* Gamma correction */
|
||||
{
|
||||
@ -139,52 +114,34 @@ static int _init(lcd_t *dev, const lcd_params_t *params)
|
||||
0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1,
|
||||
0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00
|
||||
};
|
||||
_write_cmd(dev, LCD_CMD_PGAMCTRL, gamma_pos,
|
||||
sizeof(gamma_pos));
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_PGAMCTRL, gamma_pos,
|
||||
sizeof(gamma_pos));
|
||||
}
|
||||
{
|
||||
static const uint8_t gamma_neg[] = {
|
||||
0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1,
|
||||
0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F
|
||||
};
|
||||
_write_cmd(dev, LCD_CMD_NGAMCTRL, gamma_neg,
|
||||
sizeof(gamma_neg));
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_NGAMCTRL, gamma_neg,
|
||||
sizeof(gamma_neg));
|
||||
|
||||
}
|
||||
|
||||
if (dev->params->inverted) {
|
||||
_write_cmd(dev, LCD_CMD_DINVON, NULL, 0);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_DINVON, NULL, 0);
|
||||
}
|
||||
/* Sleep out (turn off sleep mode) */
|
||||
_write_cmd(dev, LCD_CMD_SLPOUT, NULL, 0);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_SLPOUT, NULL, 0);
|
||||
/* Display on */
|
||||
_write_cmd(dev, LCD_CMD_DISPON, NULL, 0);
|
||||
spi_release(dev->params->spi);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_DISPON, NULL, 0);
|
||||
|
||||
/* Finally release the device */
|
||||
lcd_ll_release(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _set_area(const lcd_t *dev, uint16_t x1, uint16_t x2,
|
||||
uint16_t y1, uint16_t y2)
|
||||
{
|
||||
be_uint16_t params[2];
|
||||
|
||||
x1 += dev->params->offset_x;
|
||||
x2 += dev->params->offset_x;
|
||||
y1 += dev->params->offset_y;
|
||||
y2 += dev->params->offset_y;
|
||||
|
||||
params[0] = byteorder_htons(x1);
|
||||
params[1] = byteorder_htons(x2);
|
||||
|
||||
_write_cmd(dev, LCD_CMD_CASET, (uint8_t *)params,
|
||||
sizeof(params));
|
||||
params[0] = byteorder_htons(y1);
|
||||
params[1] = byteorder_htons(y2);
|
||||
_write_cmd(dev, LCD_CMD_PASET, (uint8_t *)params,
|
||||
sizeof(params));
|
||||
}
|
||||
|
||||
const lcd_driver_t lcd_ili9341_driver = {
|
||||
.init = _init,
|
||||
.set_area = _set_area,
|
||||
.set_area = NULL, /* default implementation is used */
|
||||
};
|
||||
|
@ -126,6 +126,11 @@ struct lcd_driver {
|
||||
/**
|
||||
* @brief Set area 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
|
||||
@ -137,6 +142,52 @@ struct lcd_driver {
|
||||
uint16_t y2);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Low Level to acquire the device
|
||||
*
|
||||
* @param[out] dev device descriptor
|
||||
*/
|
||||
void lcd_ll_acquire(const lcd_t *dev);
|
||||
|
||||
/**
|
||||
* @brief Low Level function to release the device
|
||||
*
|
||||
* @param[out] dev device descriptor
|
||||
*/
|
||||
void lcd_ll_release(const 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
|
||||
* @param[in] len length of the command data
|
||||
*/
|
||||
void lcd_ll_write_cmd(const lcd_t *dev, uint8_t cmd, const uint8_t *data,
|
||||
size_t len);
|
||||
|
||||
/**
|
||||
* @brief Low level function for read command 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(const lcd_t *dev, uint8_t cmd, uint8_t *data, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Setup an lcd display device
|
||||
*
|
||||
|
@ -83,7 +83,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name ILI9341 display rotation modes
|
||||
* @name ST7735 display rotation modes
|
||||
* @{
|
||||
*/
|
||||
#define ST7735_ROTATION_VERT 0 /**< Vertical mode */
|
||||
|
@ -1,6 +1,7 @@
|
||||
/*
|
||||
* 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
|
||||
@ -16,6 +17,7 @@
|
||||
*
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
* @author Francisco Molina <francois-xavier.molina@inria.fr>
|
||||
* @author Gunar Schorcht <gunar@schorcht.net>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
@ -25,6 +27,7 @@
|
||||
#include "byteorder.h"
|
||||
#include "periph/spi.h"
|
||||
#include "kernel_defines.h"
|
||||
#include "ztimer.h"
|
||||
|
||||
#include "lcd.h"
|
||||
#include "lcd_internal.h"
|
||||
@ -32,38 +35,155 @@
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
static void _lcd_spi_acquire(const lcd_t *dev)
|
||||
static inline void _lcd_write_byte(const lcd_t *dev, bool cont, uint8_t data)
|
||||
{
|
||||
spi_acquire(dev->params->spi, dev->params->cs_pin, dev->params->spi_mode,
|
||||
dev->params->spi_clk);
|
||||
if (dev->params->spi != SPI_UNDEF) {
|
||||
/* SPI serial interface is used */
|
||||
spi_transfer_byte(dev->params->spi, dev->params->cs_pin, cont, data);
|
||||
}
|
||||
else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void _lcd_write_bytes(const lcd_t *dev, bool cont,
|
||||
const void *data, size_t len)
|
||||
{
|
||||
if (dev->params->spi != SPI_UNDEF) {
|
||||
/* SPI serial interface is used */
|
||||
spi_transfer_bytes(dev->params->spi,
|
||||
dev->params->cs_pin, cont, data, NULL, len);
|
||||
}
|
||||
else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void _lcd_read_bytes(const lcd_t *dev, bool cont,
|
||||
void *data, size_t len)
|
||||
{
|
||||
if (dev->params->spi != SPI_UNDEF) {
|
||||
/* SPI serial interface is used */
|
||||
/* Dummy read */
|
||||
spi_transfer_byte(dev->params->spi,
|
||||
dev->params->cs_pin, true, 0x00);
|
||||
spi_transfer_bytes(dev->params->spi,
|
||||
dev->params->cs_pin, cont, NULL, data, len);
|
||||
}
|
||||
else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
_lcd_write_byte(dev, 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)
|
||||
static void _lcd_set_area_default(const lcd_t *dev, uint16_t x1, uint16_t x2,
|
||||
uint16_t y1, uint16_t y2)
|
||||
{
|
||||
_lcd_cmd_start(dev, cmd, len ? true : false);
|
||||
if (len) {
|
||||
spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, false, data,
|
||||
NULL, len);
|
||||
}
|
||||
be_uint16_t params[2];
|
||||
|
||||
x1 += dev->params->offset_x;
|
||||
x2 += dev->params->offset_x;
|
||||
y1 += dev->params->offset_y;
|
||||
y2 += dev->params->offset_y;
|
||||
|
||||
/* Function is called by a high level function of the LCD driver where
|
||||
* the device is already acquired. So we don't must acquire it here.
|
||||
* Therefore the low level write command function is called. */
|
||||
|
||||
params[0] = byteorder_htons(x1);
|
||||
params[1] = byteorder_htons(x2);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_CASET, (uint8_t *)params,
|
||||
sizeof(params));
|
||||
params[0] = byteorder_htons(y1);
|
||||
params[1] = byteorder_htons(y2);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_PASET, (uint8_t *)params,
|
||||
sizeof(params));
|
||||
}
|
||||
|
||||
static void _lcd_set_area(const lcd_t *dev, uint16_t x1, uint16_t x2,
|
||||
uint16_t y1, uint16_t y2)
|
||||
uint16_t y1, uint16_t y2)
|
||||
{
|
||||
assert(dev->driver->set_area);
|
||||
dev->driver->set_area(dev, x1, x2, y1, y2);
|
||||
if (dev->driver->set_area) {
|
||||
dev->driver->set_area(dev, x1, x2, y1, y2);
|
||||
}
|
||||
else {
|
||||
_lcd_set_area_default(dev, x1, x2, y1, y2);
|
||||
}
|
||||
}
|
||||
|
||||
void lcd_ll_acquire(const lcd_t *dev)
|
||||
{
|
||||
if (dev->params->spi != SPI_UNDEF) {
|
||||
/* SPI serial interface is used */
|
||||
spi_acquire(dev->params->spi, dev->params->cs_pin,
|
||||
dev->params->spi_mode, dev->params->spi_clk);
|
||||
}
|
||||
else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void lcd_ll_release(const lcd_t *dev)
|
||||
{
|
||||
if (dev->params->spi != SPI_UNDEF) {
|
||||
/* SPI serial interface is used */
|
||||
spi_release(dev->params->spi);
|
||||
}
|
||||
else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void lcd_ll_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) {
|
||||
_lcd_write_bytes(dev, false, data, len);
|
||||
}
|
||||
}
|
||||
|
||||
void lcd_ll_read_cmd(const lcd_t *dev, uint8_t cmd, uint8_t *data, size_t len)
|
||||
{
|
||||
assert(len);
|
||||
_lcd_cmd_start(dev, cmd, len ? true : false);
|
||||
_lcd_read_bytes(dev, false, data, len);
|
||||
}
|
||||
|
||||
int lcd_init(lcd_t *dev, const lcd_params_t *params)
|
||||
{
|
||||
dev->params = params;
|
||||
|
||||
assert(gpio_is_valid(dev->params->dcx_pin));
|
||||
gpio_init(dev->params->dcx_pin, GPIO_OUT);
|
||||
|
||||
if (dev->params->spi != SPI_UNDEF) {
|
||||
/* SPI serial interface is used */
|
||||
int res = spi_init_cs(dev->params->spi, dev->params->cs_pin);
|
||||
|
||||
if (res != SPI_OK) {
|
||||
DEBUG("[st7735] init: error initializing the CS pin [%i]\n", res);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if (gpio_is_valid(dev->params->rst_pin)) {
|
||||
gpio_init(dev->params->rst_pin, GPIO_OUT);
|
||||
gpio_clear(dev->params->rst_pin);
|
||||
ztimer_sleep(ZTIMER_MSEC, 120);
|
||||
gpio_set(dev->params->rst_pin);
|
||||
}
|
||||
ztimer_sleep(ZTIMER_MSEC, 120);
|
||||
|
||||
if (dev->driver->init) {
|
||||
return dev->driver->init(dev, params);
|
||||
}
|
||||
@ -73,28 +193,22 @@ int lcd_init(lcd_t *dev, const lcd_params_t *params)
|
||||
}
|
||||
|
||||
void lcd_write_cmd(const lcd_t *dev, uint8_t cmd, const uint8_t *data,
|
||||
size_t len)
|
||||
size_t len)
|
||||
{
|
||||
_lcd_spi_acquire(dev);
|
||||
_write_cmd(dev, cmd, data, len);
|
||||
spi_release(dev->params->spi);
|
||||
lcd_ll_acquire(dev);
|
||||
lcd_ll_write_cmd(dev, cmd, data, len);
|
||||
lcd_ll_release(dev);
|
||||
}
|
||||
|
||||
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);
|
||||
lcd_ll_acquire(dev);
|
||||
lcd_ll_read_cmd(dev, cmd, data, len);
|
||||
lcd_ll_release(dev);
|
||||
}
|
||||
|
||||
|
||||
void lcd_fill(const lcd_t *dev, uint16_t x1, uint16_t x2, uint16_t y1,
|
||||
uint16_t y2, uint16_t color)
|
||||
uint16_t y2, uint16_t color)
|
||||
{
|
||||
/* Send fill area to the display */
|
||||
|
||||
@ -106,7 +220,7 @@ void lcd_fill(const lcd_t *dev, uint16_t x1, uint16_t x2, uint16_t y1,
|
||||
x1, x2, y1, y2, (unsigned long)num_pix);
|
||||
|
||||
/* Send fill area to the display */
|
||||
_lcd_spi_acquire(dev);
|
||||
lcd_ll_acquire(dev);
|
||||
|
||||
_lcd_set_area(dev, x1, x2, y1, y2);
|
||||
/* Memory access command */
|
||||
@ -117,16 +231,14 @@ void lcd_fill(const lcd_t *dev, uint16_t x1, uint16_t x2, uint16_t y1,
|
||||
}
|
||||
|
||||
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));
|
||||
_lcd_write_bytes(dev, true, (uint8_t *)&color, sizeof(color));
|
||||
}
|
||||
spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, false,
|
||||
(uint8_t *)&color, NULL, sizeof(color));
|
||||
spi_release(dev->params->spi);
|
||||
_lcd_write_bytes(dev, false, (uint8_t *)&color, sizeof(color));
|
||||
lcd_ll_release(dev);
|
||||
}
|
||||
|
||||
void lcd_pixmap(const lcd_t *dev, uint16_t x1, uint16_t x2,
|
||||
uint16_t y1, uint16_t y2, const uint16_t *color)
|
||||
uint16_t y1, uint16_t y2, const uint16_t *color)
|
||||
{
|
||||
size_t num_pix = (x2 - x1 + 1) * (y2 - y1 + 1);
|
||||
|
||||
@ -134,7 +246,7 @@ void lcd_pixmap(const lcd_t *dev, uint16_t x1, uint16_t x2,
|
||||
"y1: %" PRIu16 ", y2: %" PRIu16 ". Num pixels: %lu\n",
|
||||
x1, x2, y1, y2, (unsigned long)num_pix);
|
||||
|
||||
_lcd_spi_acquire(dev);
|
||||
lcd_ll_acquire(dev);
|
||||
|
||||
/* Send fill area to the display */
|
||||
_lcd_set_area(dev, x1, x2, y1, y2);
|
||||
@ -145,19 +257,16 @@ void lcd_pixmap(const lcd_t *dev, uint16_t x1, uint16_t x2,
|
||||
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));
|
||||
_lcd_write_bytes(dev, true, &ncolor, 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));
|
||||
_lcd_write_bytes(dev, false, &ncolor, sizeof(uint16_t));
|
||||
}
|
||||
else {
|
||||
spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, false,
|
||||
(const uint8_t *)color, NULL, num_pix * 2);
|
||||
_lcd_write_bytes(dev, false, (const uint8_t *)color, num_pix * 2);
|
||||
}
|
||||
|
||||
spi_release(dev->params->spi);
|
||||
lcd_ll_release(dev);
|
||||
}
|
||||
|
||||
void lcd_invert_on(const lcd_t *dev)
|
||||
|
@ -16,12 +16,6 @@ config MODULE_ST7735
|
||||
select MODULE_ZTIMER
|
||||
select MODULE_ZTIMER_MSEC
|
||||
|
||||
config HAVE_ST7735
|
||||
bool
|
||||
select MODULE_ST7735 if MODULE_DISP_DEV
|
||||
help
|
||||
Indicates that an ST7735 display is present.
|
||||
|
||||
config MODULE_ST7789
|
||||
bool
|
||||
depends on HAVE_ST7789
|
||||
@ -29,6 +23,12 @@ config MODULE_ST7789
|
||||
help
|
||||
ST7789 display driver
|
||||
|
||||
config HAVE_ST7735
|
||||
bool
|
||||
select MODULE_ST7735 if MODULE_DISP_DEV
|
||||
help
|
||||
Indicates that an ST7735 display is present.
|
||||
|
||||
config HAVE_ST7789
|
||||
bool
|
||||
select MODULE_ST7735 if MODULE_DISP_DEV
|
||||
|
@ -1,6 +1,7 @@
|
||||
/*
|
||||
* 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
|
||||
@ -16,16 +17,21 @@
|
||||
*
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
* @author Francisco Molina <francois-xavier.molina@inria.fr>
|
||||
* @author Gunar Schorcht <gunar@schorcht.net>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "byteorder.h"
|
||||
#include "periph/spi.h"
|
||||
#include "ztimer.h"
|
||||
#include "kernel_defines.h"
|
||||
#include "ztimer.h"
|
||||
|
||||
#if IS_USED(MODULE_LCD_SPI)
|
||||
#include "periph/spi.h"
|
||||
#endif
|
||||
|
||||
#include "st7735.h"
|
||||
#include "st7735_internal.h"
|
||||
@ -35,18 +41,6 @@
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
static void _write_cmd(const lcd_t *dev, uint8_t cmd, const uint8_t *data,
|
||||
size_t len)
|
||||
{
|
||||
gpio_clear(dev->params->dcx_pin);
|
||||
spi_transfer_byte(dev->params->spi, dev->params->cs_pin, len ? true : false, cmd);
|
||||
gpio_set(dev->params->dcx_pin);
|
||||
if (len) {
|
||||
spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, false, data,
|
||||
NULL, len);
|
||||
}
|
||||
}
|
||||
|
||||
/* datasheet page 178, table converted to equation.
|
||||
* gvdd in 1mv increments: 4850 = 4.85V */
|
||||
static uint8_t _st7735_calc_pwrctl1(uint16_t gvdd)
|
||||
@ -72,92 +66,75 @@ static int _init(lcd_t *dev, const lcd_params_t *params)
|
||||
else {
|
||||
assert(params->lines <= 162);
|
||||
}
|
||||
dev->params = params;
|
||||
|
||||
uint8_t command_params[4] = { 0 };
|
||||
|
||||
gpio_init(dev->params->dcx_pin, GPIO_OUT);
|
||||
int res = spi_init_cs(dev->params->spi, dev->params->cs_pin);
|
||||
|
||||
if (res != SPI_OK) {
|
||||
DEBUG("[st7735] init: error initializing the CS pin [%i]\n", res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(dev->params->rst_pin)) {
|
||||
gpio_init(dev->params->rst_pin, GPIO_OUT);
|
||||
gpio_clear(dev->params->rst_pin);
|
||||
ztimer_sleep(ZTIMER_MSEC, 120);
|
||||
gpio_set(dev->params->rst_pin);
|
||||
}
|
||||
ztimer_sleep(ZTIMER_MSEC, 120);
|
||||
|
||||
/* Acquire once at release at the end */
|
||||
spi_acquire(dev->params->spi, dev->params->cs_pin, dev->params->spi_mode,
|
||||
dev->params->spi_clk);
|
||||
/* Acquire once and release at the end */
|
||||
lcd_ll_acquire(dev);
|
||||
|
||||
/* Soft Reset */
|
||||
_write_cmd(dev, LCD_CMD_SWRESET, NULL, 0);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_SWRESET, NULL, 0);
|
||||
ztimer_sleep(ZTIMER_MSEC, 120);
|
||||
|
||||
/* Display off */
|
||||
_write_cmd(dev, LCD_CMD_DISPOFF, NULL, 0);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_DISPOFF, NULL, 0);
|
||||
|
||||
/* PWRCTL1 */
|
||||
command_params[0] = _st7735_calc_pwrctl1(CONFIG_ST7735_GVDD);
|
||||
_write_cmd(dev, LCD_CMD_PWCTRL1, command_params, 1);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_PWCTRL1, command_params, 1);
|
||||
|
||||
/* PWCTR2 VGH = 14.7V, VGL = -7.35V */
|
||||
command_params[0] = 0x01;
|
||||
command_params[1] = 0x05;
|
||||
_write_cmd(dev, LCD_CMD_PWCTRL2, command_params, 2);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_PWCTRL2, command_params, 2);
|
||||
|
||||
/* PWCTR3 Opamp current small, Boost frequency */
|
||||
command_params[0] = 0x02;
|
||||
command_params[1] = 0x01;
|
||||
command_params[2] = 0x02;
|
||||
_write_cmd(dev, LCD_CMD_PWCTRL3, command_params, 3);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_PWCTRL3, command_params, 3);
|
||||
|
||||
/* PWCTR6 */
|
||||
command_params[0] = 0x02;
|
||||
command_params[1] = 0x11;
|
||||
command_params[2] = 0x15;
|
||||
_write_cmd(dev, LCD_CMD_PWCTRL6, command_params, 3);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_PWCTRL6, command_params, 3);
|
||||
|
||||
/* No display Inversion , Line inversion */
|
||||
command_params[0] = 0x07;
|
||||
_write_cmd(dev, LCD_CMD_INVCTR, command_params, 1);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_INVCTR, command_params, 1);
|
||||
|
||||
/* VCOMCTL */
|
||||
command_params[0] = _st7735_calc_vmh(CONFIG_ST7735_VCOMH);
|
||||
command_params[1] = _st7735_calc_vml(CONFIG_ST7735_VCOML);
|
||||
_write_cmd(dev, LCD_CMD_VMCTRL1, command_params, 2);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_VMCTRL1, command_params, 2);
|
||||
|
||||
command_params[0] = 0x86;
|
||||
_write_cmd(dev, LCD_CMD_VMCTRL2, command_params, 1);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_VMCTRL2, command_params, 1);
|
||||
|
||||
/* Memory access CTL */
|
||||
command_params[0] = dev->params->rotation;
|
||||
command_params[0] |= dev->params->rgb ? 0 : LCD_MADCTL_BGR;
|
||||
_write_cmd(dev, LCD_CMD_MADCTL, command_params, 1);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_MADCTL, command_params, 1);
|
||||
|
||||
/* Frame control */
|
||||
command_params[0] = 0x00;
|
||||
command_params[1] = 0x18;
|
||||
_write_cmd(dev, LCD_CMD_FRAMECTL1, command_params, 2);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_FRAMECTL1, command_params, 2);
|
||||
|
||||
/* Display function control */
|
||||
command_params[0] = 0x08;
|
||||
command_params[1] = 0x82;
|
||||
/* number of lines, see datasheet p. 166 (DISCTRL::NL) */
|
||||
command_params[2] = (params->lines >> 3) - 1;
|
||||
_write_cmd(dev, LCD_CMD_DFUNC, command_params, 3);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_DFUNC, command_params, 3);
|
||||
|
||||
/* Pixel format */
|
||||
command_params[0] = 0x55; /* 16 bit mode */
|
||||
_write_cmd(dev, LCD_CMD_PIXSET, command_params, 1);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_PIXSET, command_params, 1);
|
||||
|
||||
command_params[0] = 0x01;
|
||||
_write_cmd(dev, LCD_CMD_GAMSET, command_params, 1);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_GAMSET, command_params, 1);
|
||||
|
||||
/* Gamma correction */
|
||||
{
|
||||
@ -165,57 +142,38 @@ static int _init(lcd_t *dev, const lcd_params_t *params)
|
||||
0x02, 0x1c, 0x07, 0x12, 0x37, 0x32, 0x29, 0x2d,
|
||||
0x29, 0x25, 0x2B, 0x39, 0x00, 0x01, 0x03, 0x10
|
||||
};
|
||||
_write_cmd(dev, LCD_CMD_PGAMCTRL, gamma_pos,
|
||||
sizeof(gamma_pos));
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_PGAMCTRL, gamma_pos,
|
||||
sizeof(gamma_pos));
|
||||
}
|
||||
{
|
||||
static const uint8_t gamma_neg[] = {
|
||||
0x03, 0x1d, 0x07, 0x06, 0x2E, 0x2C, 0x29, 0x2D,
|
||||
0x2E, 0x2E, 0x37, 0x3F, 0x00, 0x00, 0x02, 0x10
|
||||
};
|
||||
_write_cmd(dev, LCD_CMD_NGAMCTRL, gamma_neg,
|
||||
sizeof(gamma_neg));
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_NGAMCTRL, gamma_neg,
|
||||
sizeof(gamma_neg));
|
||||
}
|
||||
|
||||
if (dev->params->inverted) {
|
||||
_write_cmd(dev, LCD_CMD_DINVON, NULL, 0);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_DINVON, NULL, 0);
|
||||
}
|
||||
/* Sleep out (turn off sleep mode) */
|
||||
_write_cmd(dev, LCD_CMD_SLPOUT, NULL, 0);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_SLPOUT, NULL, 0);
|
||||
|
||||
/* Normal display mode on */
|
||||
_write_cmd(dev, LCD_CMD_NORON, NULL, 0);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_NORON, NULL, 0);
|
||||
ztimer_sleep(ZTIMER_MSEC, 1);
|
||||
|
||||
/* Display on */
|
||||
_write_cmd(dev, LCD_CMD_DISPON, NULL, 0);
|
||||
spi_release(dev->params->spi);
|
||||
lcd_ll_write_cmd(dev, LCD_CMD_DISPON, NULL, 0);
|
||||
|
||||
/* Finally release the device */
|
||||
lcd_ll_release(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _set_area(const lcd_t *dev, uint16_t x1, uint16_t x2,
|
||||
uint16_t y1, uint16_t y2)
|
||||
{
|
||||
be_uint16_t params[2];
|
||||
|
||||
x1 += dev->params->offset_x;
|
||||
x2 += dev->params->offset_x;
|
||||
y1 += dev->params->offset_y;
|
||||
y2 += dev->params->offset_y;
|
||||
|
||||
params[0] = byteorder_htons(x1);
|
||||
params[1] = byteorder_htons(x2);
|
||||
|
||||
_write_cmd(dev, LCD_CMD_CASET, (uint8_t *)params,
|
||||
sizeof(params));
|
||||
params[0] = byteorder_htons(y1);
|
||||
params[1] = byteorder_htons(y2);
|
||||
_write_cmd(dev, LCD_CMD_PASET, (uint8_t *)params,
|
||||
sizeof(params));
|
||||
}
|
||||
|
||||
const lcd_driver_t lcd_st7735_driver = {
|
||||
.init = _init,
|
||||
.set_area = _set_area,
|
||||
.set_area = NULL, /* default implementation is used */
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user