From 548f4b652c440dfa81f9b22cff6899a847f6d0c3 Mon Sep 17 00:00:00 2001 From: Michal Lenc Date: Thu, 13 Jul 2023 16:13:52 +0200 Subject: [PATCH] st7789: add support for 3 wire interface 3 wire interface for ST7789 LCD controller does not use CMD/DATA pin to specify whether data or command is send but uses 9th bit of SPI transfer. This commit adds support for 3 wire interface to ST7789 controller. Signed-off-by: Michal Lenc --- drivers/lcd/Kconfig | 8 +++ drivers/lcd/st7789.c | 148 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 125 insertions(+), 31 deletions(-) diff --git a/drivers/lcd/Kconfig b/drivers/lcd/Kconfig index ef271dea18..4f09f5ae90 100644 --- a/drivers/lcd/Kconfig +++ b/drivers/lcd/Kconfig @@ -705,6 +705,14 @@ config LCD_ST7789 default n if LCD_ST7789 +config LCD_ST7789_3WIRE + bool "Use 3 wire interface" + default n + ---help--- + Specifies whether 3 wire or 4 wire (default) interface is + used. 3 wire interface drops CMD/DATA pin and uses 9th bit + to determine data/command message. + config LCD_ST7789_XRES int "ST7789 X Resolution" default 240 diff --git a/drivers/lcd/st7789.c b/drivers/lcd/st7789.c index ffc6c0609b..1e92f82864 100644 --- a/drivers/lcd/st7789.c +++ b/drivers/lcd/st7789.c @@ -45,9 +45,9 @@ * Pre-processor Definitions ****************************************************************************/ -/* CONFIG_SPI_CMDDATA has to be set */ +/* CONFIG_SPI_CMDDATA has to be set for 4 wire interface */ -#ifndef CONFIG_SPI_CMDDATA +#if !defined(CONFIG_LCD_ST7789_3WIRE) && !defined(CONFIG_SPI_CMDDATA) # error "CONFIG_SPI_CMDDATA option has to be set for SPI communication" #endif @@ -103,6 +103,18 @@ # define CONFIG_LCD_RPORTRAIT 1 #endif +/* Define prefixes for 3 wire communication if used */ + +#ifdef CONFIG_LCD_ST7789_3WIRE +# define LCD_ST7789_SPI_BITS 9 +# define LCD_ST7789_DATA_PREFIX (1 << 8) +# define LCD_ST7789_CMD_PREFIX (0 << 8) +#else +# define LCD_ST7789_SPI_BITS 8 +# define LCD_ST7789_DATA_PREFIX (0) +# define LCD_ST7789_CMD_PREFIX (0) +#endif + /* Display Resolution */ #if !defined(CONFIG_LCD_ST7789_XRES) @@ -184,6 +196,18 @@ struct st7789_dev_s uint16_t runbuffer[ST7789_LUT_SIZE]; }; + /* 3 wire interface for ST7789 requires the driver to send information + * about command/data transfer as 9th bit of SPI transfer. This would + * require non standard SPI interface that is not supported so a little + * workaround is used here (inspire by SSD1351 driver). We split our + * buffer into rows and send those rows separately with added 9th bit. + * The price for this is a small overhead in SPI communication. + */ + +#ifdef CONFIG_LCD_ST7789_3WIRE +uint16_t rowbuff[ST7789_XRES * ST7789_BYTESPP]; +#endif + /**************************************************************************** * Private Function Protototypes ****************************************************************************/ @@ -325,11 +349,23 @@ static void st7789_deselect(FAR struct spi_dev_s *spi) static inline void st7789_sendcmd(FAR struct st7789_dev_s *dev, uint8_t cmd) { - st7789_select(dev->spi, 8); +#ifdef CONFIG_LCD_ST7789_3WIRE + uint16_t txbuf; + + /* Add command prefix (9th bit shoudl be 0 ) */ + + txbuf = LCD_ST7789_CMD_PREFIX | cmd; + + st7789_select(dev->spi, LCD_ST7789_SPI_BITS); + SPI_SEND(dev->spi, txbuf); + st7789_deselect(dev->spi); +#else + st7789_select(dev->spi, LCD_ST7789_SPI_BITS); SPI_CMDDATA(dev->spi, SPIDEV_DISPLAY(0), true); SPI_SEND(dev->spi, cmd); SPI_CMDDATA(dev->spi, SPIDEV_DISPLAY(0), false); st7789_deselect(dev->spi); +#endif } /**************************************************************************** @@ -380,26 +416,26 @@ static void st7789_setorientation(FAR struct st7789_dev_s *dev, if (orientation != LCD_PORTRAIT) { st7789_sendcmd(dev, ST7789_MADCTL); - st7789_select(dev->spi, 8); + st7789_select(dev->spi, LCD_ST7789_SPI_BITS); } if (orientation == LCD_RLANDSCAPE) { /* RLANDSCAPE : MY=1 MV=1 */ - SPI_SEND(dev->spi, 0xa0); + SPI_SEND(dev->spi, LCD_ST7789_DATA_PREFIX | 0xa0); } else if (orientation == LCD_LANDSCAPE) { /* LANDSCAPE : MX=1 MV=1 */ - SPI_SEND(dev->spi, 0x70); + SPI_SEND(dev->spi, LCD_ST7789_DATA_PREFIX | 0x70); } else if (orientation == LCD_RPORTRAIT) { /* RPORTRAIT : MX=1 MY=1 */ - SPI_SEND(dev->spi, 0xc0); + SPI_SEND(dev->spi, LCD_ST7789_DATA_PREFIX | 0xc0); } st7789_deselect(dev->spi); @@ -412,7 +448,7 @@ static void st7789_setorientation(FAR struct st7789_dev_s *dev) uint8_t madctl = 0x00; st7789_sendcmd(dev, ST7789_MADCTL); - st7789_select(dev->spi, 8); + st7789_select(dev->spi, LCD_ST7789_SPI_BITS); #if !defined(CONFIG_LCD_PORTRAIT) @@ -444,7 +480,7 @@ static void st7789_setorientation(FAR struct st7789_dev_s *dev) madctl ^= 0x80; #endif - SPI_SEND(dev->spi, madctl); + SPI_SEND(dev->spi, LCD_ST7789_DATA_PREFIX | madctl); st7789_deselect(dev->spi); } @@ -465,34 +501,38 @@ static void st7789_setarea(FAR struct st7789_dev_s *dev, /* Set row address */ st7789_sendcmd(dev, ST7789_RASET); - st7789_select(dev->spi, 8); + st7789_select(dev->spi, LCD_ST7789_SPI_BITS); #ifdef CONFIG_LCD_DYN_ORIENTATION - SPI_SEND(dev->spi, (y0 + g_lcddev.yoff) >> 8); - SPI_SEND(dev->spi, (y0 + g_lcddev.yoff) & 0xff); - SPI_SEND(dev->spi, (y1 + g_lcddev.yoff) >> 8); - SPI_SEND(dev->spi, (y1 + g_lcddev.yoff) & 0xff); + SPI_SEND(dev->spi, LCD_ST7789_DATA_PREFIX | ((y0 + g_lcddev.yoff) >> 8)); + SPI_SEND(dev->spi, LCD_ST7789_DATA_PREFIX | ((y0 + g_lcddev.yoff) & 0xff)); + SPI_SEND(dev->spi, LCD_ST7789_DATA_PREFIX | ((y1 + g_lcddev.yoff) >> 8)); + SPI_SEND(dev->spi, LCD_ST7789_DATA_PREFIX | ((y1 + g_lcddev.yoff) & 0xff)); #else - SPI_SEND(dev->spi, (y0 + ST7789_YOFFSET) >> 8); - SPI_SEND(dev->spi, (y0 + ST7789_YOFFSET) & 0xff); - SPI_SEND(dev->spi, (y1 + ST7789_YOFFSET) >> 8); - SPI_SEND(dev->spi, (y1 + ST7789_YOFFSET) & 0xff); + SPI_SEND(dev->spi, LCD_ST7789_DATA_PREFIX | ((y0 + ST7789_YOFFSET) >> 8)); + SPI_SEND(dev->spi, LCD_ST7789_DATA_PREFIX | + ((y0 + ST7789_YOFFSET) & 0xff)); + SPI_SEND(dev->spi, LCD_ST7789_DATA_PREFIX | ((y1 + ST7789_YOFFSET) >> 8)); + SPI_SEND(dev->spi, LCD_ST7789_DATA_PREFIX | + ((y1 + ST7789_YOFFSET) & 0xff)); #endif st7789_deselect(dev->spi); /* Set column address */ st7789_sendcmd(dev, ST7789_CASET); - st7789_select(dev->spi, 8); + st7789_select(dev->spi, LCD_ST7789_SPI_BITS); #ifdef CONFIG_LCD_DYN_ORIENTATION - SPI_SEND(dev->spi, (x0 + g_lcddev.xoff) >> 8); - SPI_SEND(dev->spi, (x0 + g_lcddev.xoff) & 0xff); - SPI_SEND(dev->spi, (x1 + g_lcddev.xoff) >> 8); - SPI_SEND(dev->spi, (x1 + g_lcddev.xoff) & 0xff); + SPI_SEND(dev->spi, LCD_ST7789_DATA_PREFIX | ((x0 + g_lcddev.xoff) >> 8)); + SPI_SEND(dev->spi, LCD_ST7789_DATA_PREFIX | ((x0 + g_lcddev.xoff) & 0xff)); + SPI_SEND(dev->spi, LCD_ST7789_DATA_PREFIX | ((x1 + g_lcddev.xoff) >> 8)); + SPI_SEND(dev->spi, LCD_ST7789_DATA_PREFIX | ((x1 + g_lcddev.xoff) & 0xff)); #else - SPI_SEND(dev->spi, (x0 + ST7789_XOFFSET) >> 8); - SPI_SEND(dev->spi, (x0 + ST7789_XOFFSET) & 0xff); - SPI_SEND(dev->spi, (x1 + ST7789_XOFFSET) >> 8); - SPI_SEND(dev->spi, (x1 + ST7789_XOFFSET) & 0xff); + SPI_SEND(dev->spi, LCD_ST7789_DATA_PREFIX | ((x0 + ST7789_XOFFSET) >> 8)); + SPI_SEND(dev->spi, LCD_ST7789_DATA_PREFIX | + ((x0 + ST7789_XOFFSET) & 0xff)); + SPI_SEND(dev->spi, LCD_ST7789_DATA_PREFIX | ((x1 + ST7789_XOFFSET) >> 8)); + SPI_SEND(dev->spi, LCD_ST7789_DATA_PREFIX | + ((x1 + ST7789_XOFFSET) & 0xff)); #endif st7789_deselect(dev->spi); } @@ -513,8 +553,8 @@ static void st7789_bpp(FAR struct st7789_dev_s *dev, int bpp) depth = bpp >> 2 | 1; st7789_sendcmd(dev, ST7789_COLMOD); - st7789_select(dev->spi, 8); - SPI_SEND(dev->spi, depth); + st7789_select(dev->spi, LCD_ST7789_SPI_BITS); + SPI_SEND(dev->spi, LCD_ST7789_DATA_PREFIX | depth); st7789_deselect(dev->spi); /* Cache the new BPP */ @@ -536,16 +576,52 @@ static void st7789_wrram(FAR struct st7789_dev_s *dev, size_t count) { size_t i; +#ifdef CONFIG_LCD_ST7789_3WIRE + size_t j; +#endif st7789_sendcmd(dev, ST7789_RAMWR); - st7789_select(dev->spi, ST7789_BYTESPP * 8); +#ifdef CONFIG_LCD_ST7789_3WIRE + if (count == 1) + { + /* We cannot send the entire buffer at once, split it to + * separate rows. + */ + + count = ST7789_YRES; + size = ST7789_XRES * ST7789_BYTESPP; + } + + st7789_select(dev->spi, LCD_ST7789_SPI_BITS); + + /* For each row */ + + for (i = 0; i < count; i++) + { + /* Copy data to rowbuff and add 9th bit */ + + for (j = 0; j < ST7789_XRES * ST7789_BYTESPP; j += 2) + { + /* Take care of correct byte order. */ + + rowbuff[j] = LCD_ST7789_DATA_PREFIX | + (uint16_t)buff[j + 1 + (i * (size + skip))]; + rowbuff[j + 1] = LCD_ST7789_DATA_PREFIX | + (uint16_t)buff[j + (i * (size + skip))]; + } + + SPI_SNDBLOCK(dev->spi, rowbuff, size); + } +#else + st7789_select(dev->spi, ST7789_BYTESPP * LCD_ST7789_SPI_BITS); for (i = 0; i < count; i++) { SPI_SNDBLOCK(dev->spi, buff + (i * (size + skip)), size / ST7789_BYTESPP); } +#endif st7789_deselect(dev->spi); } @@ -585,12 +661,22 @@ static void st7789_fill(FAR struct st7789_dev_s *dev, uint16_t color) st7789_setarea(dev, 0, 0, ST7789_XRES - 1, ST7789_YRES - 1); st7789_sendcmd(dev, ST7789_RAMWR); - st7789_select(dev->spi, ST7789_BYTESPP *8); +#ifdef CONFIG_LCD_ST7789_3WIRE + st7789_select(dev->spi, LCD_ST7789_SPI_BITS); + + for (i = 0; i < ST7789_XRES * ST7789_YRES; i++) + { + SPI_SEND(dev->spi, LCD_ST7789_DATA_PREFIX | (color & 0xff)); + SPI_SEND(dev->spi, LCD_ST7789_DATA_PREFIX | (color & 0xff00) >> 8); + } +#else + st7789_select(dev->spi, ST7789_BYTESPP * LCD_ST7789_SPI_BITS); for (i = 0; i < ST7789_XRES * ST7789_YRES; i++) { SPI_SEND(dev->spi, color); } +#endif st7789_deselect(dev->spi); }