This is an automated email from the ASF dual-hosted git repository. xiaoxiang pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
The following commit(s) were added to refs/heads/master by this push: new 88bfadc55d e-ink ssd1680 driver 88bfadc55d is described below commit 88bfadc55d0f5ef0f41b57bed75090ddba4e0a7a Author: Adam Kaliszan <akalis...@altimetrik.com> AuthorDate: Fri Jun 10 14:03:19 2022 +0200 e-ink ssd1680 driver No BSP files --- drivers/lcd/Kconfig | 48 ++ drivers/lcd/Make.defs | 4 + drivers/lcd/ssd1680.c | 1382 +++++++++++++++++++++++++++++++++++++++++++ drivers/lcd/ssd1680.h | 197 ++++++ include/nuttx/lcd/ssd1680.h | 193 ++++++ 5 files changed, 1824 insertions(+) diff --git a/drivers/lcd/Kconfig b/drivers/lcd/Kconfig index 08fdb24a0a..4ad26a82bd 100644 --- a/drivers/lcd/Kconfig +++ b/drivers/lcd/Kconfig @@ -1509,6 +1509,54 @@ config LCD_FT80X_AUDIO_GPIO endif # LCD_FT80X +config LCD_SSD1680 + bool "SSD1680 E-PAPER Single Chip Driver" + default n + select SPI_CMDDATA + ---help--- + e-paper Display Module based on SSD1680 controller. + + Required SPI driver settings: + SPI_CMDDATA - Include support for cmd/data selection. + + +if LCD_SSD1680 + +choice + prompt "Display type and size" + default LCD_SSD1680_2_9 + +config LCD_SSD1680_2_13 + bool "2.13 inch 122/250 mono" + +config LCD_SSD1680_2_13_RED + bool "2.13 inch 122/250 tri color (red)" + +config LCD_SSD1680_2_9 + bool "2.9 inch 128/296 mono" + +config LCD_SSD1680_2_9_RED + bool "2.9 inch 128/296 tri color (red)" + +endchoice # Display size + +config SSD1680_SPIMODE + int "SSD1680 SPI Mode" + default 0 + range 0 3 + ---help--- + Selects the SPI mode used with the SSD1680 device + +config SSD1680_FREQUENCY + int "SSD1680 SPI Frequency" + default 2000000 + range 100000 2000000 + ---help--- + Selects the SPI bus frequency used with the SSD1680 device. + Max for read mode is 2.5 MHz, for write mode is 20 HHz +endif # LCD_SSD1680 + + endmenu # LCD Driver selection endif # LCD diff --git a/drivers/lcd/Make.defs b/drivers/lcd/Make.defs index 4f59a45c48..66bc4f123b 100644 --- a/drivers/lcd/Make.defs +++ b/drivers/lcd/Make.defs @@ -73,6 +73,10 @@ ifeq ($(CONFIG_LCD_SSD1289),y) CSRCS += ssd1289.c endif +ifeq ($(CONFIG_LCD_SSD1680),y) + CSRCS += ssd1680.c +endif + ifeq ($(CONFIG_LCD_SSD1351),y) CSRCS += ssd1351.c endif diff --git a/drivers/lcd/ssd1680.c b/drivers/lcd/ssd1680.c new file mode 100644 index 0000000000..bf89649ce4 --- /dev/null +++ b/drivers/lcd/ssd1680.c @@ -0,0 +1,1382 @@ +/**************************************************************************** + * drivers/lcd/ssd1680.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* Driver for e-paper displays with SSD1680 controller. + * + * References: + * 1. https://www.crystalfontz.com/controllers/SolomonSystech/SSD1680/497/ + * 2. https://www.adafruit.com/product/4947 + * 3. https://github.com/adamkaliszan/TTGO-Electronic-Badge + + * TTGO-Electronic-Badge sequence that display picture: + * cmd: dta: + * Hardware reset and busy wait + * 1) 0x01 27 01 00 + * 2) 0x0C D7 D6 9D boost soft start + * 3) 0x2c A8 write VCom + * 4) 0x3A 1A ??? Can't find it in in SSD documentation + * 5) 0x3B 08 ??? Can't find it in in SSD documentation + * 6) 0x11 01 Data Mode + * 7) 0x44 00 0F + * 8) 0x45 27 01 00 00 + * 9) 0x4E 00 + * 10) 0x4F 27 01 + * 11) 0x32 50 AA 55 AA 11 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 FF FF 1F 00 00 00 00 00 00 00 + * 12) 0x22 C0 + * 13) 0x20 + * Busy Wait + * 14) 0x24 0xFF ... 4736 bytes with bitmap + * 15) 0x22 C4 + * 16) 0x20 + * Busy Wait + * 17) 0xFF + * 18) 0x22 C3 + * 19) 0x20 + * Busy Wait + */ + +/**************************************************************************** + * Device memory organization: + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/types.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/arch.h> +#include <nuttx/spi/spi.h> +#include <nuttx/lcd/lcd.h> +#include <nuttx/lcd/ssd1680.h> +#include <nuttx/signal.h> + +#include <arch/irq.h> + +#include "ssd1680.h" + +#ifdef CONFIG_LCD_SSD1680 +/* This structure describes the state of the SSD1680 driver */ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct ssd1680_dev_s +{ + struct lcd_dev_s dev; /* Publicly visible device structure */ + + /* Private LCD-specific information follows */ + + FAR struct spi_dev_s *spi; /* Cached SPI device reference */ + uint8_t contrast; /* Current contrast setting */ + bool on; /* true: display is on */ + bool is_conf; /* true: display had been configured */ + + FAR const struct ssd1680_priv_s *board_priv; /* Board specific structure */ + + /* The SSD1680 does not support reading from the display memory in SPI + * mode. Since there is 1 BPP and access is byte-by-byte. + * Also shared line (MISO/MOSI) could be problematic. Now implementation + * uses shadow buffer. + * Its size depends on resolution and is between 4kB and 32 kB. + */ + + uint8_t shadow_fb[SSD1680_DEV_FBSIZE]; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Libc extension */ + +FAR void *bitscpy_ds(FAR void *dest, int dest_offset, FAR const void *src, + size_t nbits); + +FAR void *bitscpy_ss(FAR void *dest, FAR const void *src, int src_offset, + size_t nbits); + +/* LCD Data Transfer Methods */ + +static void ssd1680_busy_wait(FAR struct ssd1680_dev_s *priv); + +static void ssd1680_snd_cmd_with_data0(FAR struct ssd1680_dev_s *priv, + uint8_t cmd); + +static void ssd1680_snd_cmd_with_data1(FAR struct ssd1680_dev_s *priv, + uint8_t cmd, uint8_t dta1); + +static void ssd1680_snd_cmd_with_data2(FAR struct ssd1680_dev_s *priv, + uint8_t cmd, uint8_t dta1, uint8_t dta2); + +static void ssd1680_snd_cmd_with_data3(FAR struct ssd1680_dev_s *priv, + uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3); + +static void ssd1680_snd_cmd_with_data4(FAR struct ssd1680_dev_s *priv, + uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3, uint8_t dta4); + +static void ssd1680_snd_cmd_with_data(FAR struct ssd1680_dev_s *priv, + uint8_t cmd, const uint8_t *dta, int dta_len); + +#if !defined(CONFIG_LCD_PORTRAIT) && !defined(CONFIG_LCD_RPORTRAIT) +# if SSD1680_DEV_BPP == 1 +static void ssd1680_snd_cmd_with_data_bitstrip( + FAR struct ssd1680_dev_s *priv, uint8_t cmd, const uint8_t *dta, + int dta_len, int strip_len); +# else +/* Special functions for sending to RAM1 and RAM2 should be implemented + * ssd1680_snd_cmd_with_data_bitstrip works fine with 1 bit per pixel + */ +# error "SSD1680 driver has no implementation for 3 color with landscape" +# endif +#endif + +static void ssd1680_configspi(FAR struct spi_dev_s *spi); +static void ssd1680_select(FAR struct ssd1680_dev_s *priv, bool cs); +static void ssd1680_cmddata(FAR struct ssd1680_dev_s *priv, bool cmd); + +static int ssd1680_putrun(fb_coord_t row, fb_coord_t col, + FAR const uint8_t *buffer, size_t npixels); + +static int ssd1680_getrun(fb_coord_t row, fb_coord_t col, + FAR uint8_t *buffer, size_t npixels); + +/* LCD Configuration */ + +static int ssd1680_getvideoinfo(FAR struct lcd_dev_s *dev, + FAR struct fb_videoinfo_s *vinfo); +static int ssd1680_getplaneinfo(FAR struct lcd_dev_s *dev, + unsigned int planeno, + FAR struct lcd_planeinfo_s *pinfo); + +/* LCD RGB Mapping */ + +#ifdef CONFIG_FB_CMAP +# error "RGB color mapping not supported by this driver" +#endif + +/* Cursor Controls */ + +#ifdef CONFIG_FB_HWCURSOR +# error "Cursor control not supported by this driver" +#endif + +/* LCD Specific Controls */ + +static int ssd1680_getpower(struct lcd_dev_s *dev); +static int ssd1680_setpower(struct lcd_dev_s *dev, int power); +static int ssd1680_getcontrast(struct lcd_dev_s *dev); +static int ssd1680_setcontrast(struct lcd_dev_s *dev, + unsigned int contrast); + +static int ssd1680_configuredisplay(struct ssd1680_dev_s *priv); +static int ssd1680_redraw_display(struct ssd1680_dev_s *priv); +static int ssd1680_redrawfb_row(struct ssd1680_dev_s *priv, int row); +static int ssd1680_redrawfb(struct ssd1680_dev_s *priv); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* This is working memory allocated by the LCD driver for each LCD device + * and for each color plane. This memory will hold one raster line of data. + * The size of the allocated run buffer must therefore be at least + * (bpp * xres / 8). Actual alignment of the buffer must conform to the + * bitwidth of the underlying pixel type. + * + * If there are multiple planes, they may share the same working buffer + * because different planes will not be operate on concurrently. However, + * if there are multiple LCD devices, they must each have unique run buffers. + */ + +static uint8_t g_runbuffer[SSD1680_DEV_ROWSIZE]; + +/* This structure describes the overall LCD video controller */ + +static const struct fb_videoinfo_s g_videoinfo = +{ + .fmt = SSD1680_DEV_COLORFMT, /* Color format: B&W */ + .xres = SSD1680_DEV_FB_XRES, /* Horizontal resolution in pixel columns */ + .yres = SSD1680_DEV_FB_YRES, /* Vertical resolution in pixel rows */ + .nplanes = SSD1680_NO_OF_PLANES, /* Number of color planes supported */ +}; + +/* This is the standard, NuttX Plane information object */ + +static const struct lcd_planeinfo_s g_planeinfo = +{ + .putrun = ssd1680_putrun, /* Put a run into LCD memory */ + .putarea = NULL, /* Not need to implement */ + .getrun = ssd1680_getrun, /* Read content of shadow memory */ + .getarea = NULL, /* Not need to implement */ + .buffer = (FAR uint8_t *)g_runbuffer, /* Run scratch buffer */ + .bpp = SSD1680_DEV_BPP, /* Bits-per-pixel */ +}; + +/* This is the outside visible interface for the OLED driver */ + +static const struct lcd_dev_s g_lcd_epaper_dev = +{ + /* LCD Configuration */ + + .getvideoinfo = ssd1680_getvideoinfo, + .getplaneinfo = ssd1680_getplaneinfo, + + /* LCD RGB Mapping -- Not supported */ + + /* Cursor Controls -- Not supported */ + + /* LCD Specific Controls */ + + .getpower = ssd1680_getpower, + .setpower = ssd1680_setpower, + .getcontrast = ssd1680_getcontrast, + .setcontrast = ssd1680_setcontrast, +}; + +/* This is the OLED driver instance. Only a single device is supported + * for now. + */ + +static struct ssd1680_dev_s g_epaperdev; + +static const uint8_t ssd1680_lut[] = +{ + 0x50, 0xaa, 0x55, 0xaa, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ssd1680_putrun + * + * Description: + * This method can be used to write a partial raster line to the LCD. + * + * Input Parameters: + * row - Starting row to write to (range: 0 <= row < yres) + * col - Starting column to write to (range: 0 <= col <= xres-npixels) + * buffer - The buffer containing the run to be written to the LCD + * npixels - The number of pixels to write to the LCD + * (range: 0 < npixels <= xres-col) + * + ****************************************************************************/ + +static int ssd1680_putrun(fb_coord_t row, fb_coord_t col, + FAR const uint8_t *buffer, size_t npixels) +{ + FAR struct ssd1680_dev_s *priv = (FAR struct ssd1680_dev_s *)&g_epaperdev; + + uint8_t *dst = priv->shadow_fb + + row * SSD1680_DEV_ROWSIZE + (col >> SSD1680_PDF); + + int dst_start_bitshift = col % (SSD1680_PDV); + + /* Write data to shadow memory */ + + bitscpy_ds(dst, dst_start_bitshift, buffer, npixels); + + /* Redraw Screen */ + + ssd1680_redrawfb_row(priv, row); + + return OK; +} + +/**************************************************************************** + * Name: ssd1680_getrun + * + * Description: + * This method can be used to read a partial raster line from the LCD: + * + * Input Parameters: + * + * row - Starting row to read from (range: 0 <= row < yres) + * col - Starting column to read read + * (range: 0 <= col <= xres-npixels) + * buffer - The buffer in which to return the run read from the LCD + * npixels - The number of pixels to read from the LCD + * (range: 0 < npixels <= xres-col) + */ + +static int ssd1680_getrun(fb_coord_t row, fb_coord_t col, + FAR uint8_t *buffer, size_t npixels) +{ + lcdinfo("(%d, %d, %d)\n", row, col, npixels); + FAR struct ssd1680_dev_s *priv = (FAR struct ssd1680_dev_s *)&g_epaperdev; + + bitscpy_ss(buffer, + priv->shadow_fb + row * SSD1680_DEV_FBSIZE + (col >> SSD1680_PDF), + col % SSD1680_PDV, + npixels); + + return OK; +} + +/**************************************************************************** + * Name: ssd1680_getvideoinfo + * + * Description: + * Get information about the LCD video controller configuration. + * + ****************************************************************************/ + +static int ssd1680_getvideoinfo(FAR struct lcd_dev_s *dev, + FAR struct fb_videoinfo_s *vinfo) +{ + DEBUGASSERT(dev && vinfo); + lcdinfo("fmt: %d xres: %d yres: %d nplanes: %d\n", + g_videoinfo.fmt, g_videoinfo.xres, g_videoinfo.yres, + g_videoinfo.nplanes); + memcpy(vinfo, &g_videoinfo, sizeof(struct fb_videoinfo_s)); + return OK; +} + +/**************************************************************************** + * Name: ssd1680_getplaneinfo + * + * Description: + * Get information about the configuration of each LCD color plane. + * + ****************************************************************************/ + +static int ssd1680_getplaneinfo(FAR struct lcd_dev_s *dev, + unsigned int planeno, + FAR struct lcd_planeinfo_s *pinfo) +{ + DEBUGASSERT(pinfo && planeno == 0); + + lcdinfo("planeno: %d bpp: %d\n", planeno, g_planeinfo.bpp); + memcpy(pinfo, &g_planeinfo, sizeof(struct lcd_planeinfo_s)); + return OK; +} + +/**************************************************************************** + * Name: ssd1680_getpower + * + * Description: + * Get the LCD panel power status: + * 0: full off + * CONFIG_LCD_MAXPOWER: full on + * On backlit LCDs, this setting may correspond to the backlight setting. + * + ****************************************************************************/ + +static int ssd1680_getpower(FAR struct lcd_dev_s *dev) +{ + struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *) dev; + DEBUGASSERT(priv); + + lcdinfo("power: %s\n", priv->on ? "ON" : "OFF"); + return priv->on ? CONFIG_LCD_MAXPOWER : 0; +} + +static void ssd1680_reset(struct ssd1680_dev_s *priv) +{ + if (priv->board_priv && priv->board_priv->set_rst) + { + lcdinfo("Hardware reset\n"); + priv->board_priv->set_rst(false); + nxsig_usleep(10); + priv->board_priv->set_rst(true); + } + else + { + lcdinfo("Hardware reset is not available. Operation skipped.\n"); + } +} + +/**************************************************************************** + * Name: ssd1680_setpower + * + * Description: + * Enable/disable LCD panel power: + * 0: full off + * CONFIG_LCD_MAXPOWER: full on + * On backlit LCDs, this setting may correspond to the backlight setting. + * + ****************************************************************************/ + +static int ssd1680_setpower(FAR struct lcd_dev_s *dev, int power) +{ + int ret = OK; + struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *) dev; + DEBUGASSERT(dev); + lcdinfo("power: %d -> %d\n", priv->on ? CONFIG_LCD_MAXPOWER : 0, power); + DEBUGASSERT((unsigned)power <= CONFIG_LCD_MAXPOWER); + + if (power <= 0) + { + priv->on = false; + + /* Try turn off power completely */ + + if (priv->board_priv && priv->board_priv->set_vcc) + { + /* Do power off. */ + + if (priv->board_priv->set_vcc(false)) + { + /* Display is completely powered off, not configured anymore. */ + + priv->is_conf = false; + } + } + } + else + { + if (priv->board_priv && priv->board_priv->set_vcc) + { + /* Do power on. */ + + lcdinfo("Set Pwr Ctrl Linepower ON\n"); + priv->board_priv->set_vcc(true); + nxsig_usleep(10000); + } + else + { + lcdinfo("No line for controlling PWR, operation skipped\n"); + } + + if (!priv->is_conf) + { + ssd1680_reset(priv); + + ret = ssd1680_configuredisplay(priv); + if (ret < 0) + { + return ret; + } + + /* Draw the framebuffer */ + + ret = ssd1680_redrawfb(priv); + } + + if (ret < 0) + { + return ret; + } + + priv->on = true; + } + + return ret; +} + +/**************************************************************************** + * Name: ssd1680_getcontrast + * + * Description: + * Get the current contrast setting (0-CONFIG_LCD_MAXCONTRAST). + * + ****************************************************************************/ + +static int ssd1680_getcontrast(struct lcd_dev_s *dev) +{ + struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *)dev; + DEBUGASSERT(priv); + + lcdinfo("contrast: %d\n", priv->contrast); + return priv->contrast; +} + +/**************************************************************************** + * Name: ssd1680_setcontrast + * + * Description: + * Set LCD panel contrast (0-CONFIG_LCD_MAXCONTRAST). + * + ****************************************************************************/ + +static int ssd1680_setcontrast(struct lcd_dev_s *dev, unsigned int contrast) +{ + struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *)dev; + + lcdinfo("ignoring set constrast(%d)\n", contrast); + DEBUGASSERT(priv); + + /* Verify the contrast value */ + +#ifdef CONFIG_DEBUG_FEATURES + if (contrast > CONFIG_LCD_MAXCONTRAST) + { + return -EINVAL; + } +#endif + + return OK; +} + +/**************************************************************************** + * Name: ssd1680_configuredisplay + * + * Description: + * Setup LCD display. + * + ****************************************************************************/ + +static int ssd1680_configuredisplay(struct ssd1680_dev_s *priv) +{ + lcdinfo("Start\n"); + + /* Software Reset */ + + lcdinfo("Software reset: (0x%02x)\n", SSD1680_SW_RESET); + ssd1680_snd_cmd_with_data0(priv, SSD1680_SW_RESET); + + /* Busy wait */ + + ssd1680_busy_wait(priv); + lcdinfo("SSD1680 is ready\n"); + + /* Step 1: Driver Output Control 3 bytes of data: + * - A[8:0] MUX Gate lines + * - B[2:0] sequence + * last data byte depends on connection between display and controller + */ + + lcdinfo("Set the driver output controll (0x%02x): %d 0x%02x\n", + SSD1680_DRIVER_CONTROL, SSD1680_DEV_NATIVE_YRES - 1, + SSD1680_DEV_GATE_LAYOUT); + ssd1680_snd_cmd_with_data3(priv, SSD1680_DRIVER_CONTROL, + (uint8_t)((SSD1680_DEV_NATIVE_YRES - 1) & 0xff), + (SSD1680_DEV_NATIVE_YRES - 1) >> 8, + SSD1680_DEV_GATE_LAYOUT); + + /* Step 2: SSD1680_BOOST_SOFTSTART 0x0C D7 D6 9D */ + + lcdinfo("Set boost soft start\n"); + ssd1680_snd_cmd_with_data3(priv, SSD1680_BOOST_SOFTSTART, + 0xd7, 0xd6, 0x9d); + + /* Step 3: Vcom Voltage SSD1680_WRITE_VCOM, 0x36 */ + + lcdinfo("Set Vcom voltage (0x%02x): 0x%2x\n", SSD1680_WRITE_VCOM, 0xa8); + ssd1680_snd_cmd_with_data1(priv, SSD1680_WRITE_VCOM, 0xa8); + + /* Step 4: Sending undocumented command: 0x3a with data 1A */ + + lcdinfo("Set (0x%02x): 0x%2x\n", 0x3a, 0x1a); + ssd1680_snd_cmd_with_data1(priv, 0x3a, 0x1a); + + /* Step 5: Sending undocumented command: 0x3b with data 08 */ + + lcdinfo("Set (0x%02x): 0x%2x\n", 0x3b, 0x08); + ssd1680_snd_cmd_with_data1(priv, 0x3b, 0x08); + + /* Step 6: Data entry mode SSD1680_DATA_MODE, 0x03 + * TODO w arduino była wartość 0x01 + */ + + lcdinfo("Set data entry mode (0x%02x): 0x%2x\n", + SSD1680_DATA_MODE, SSD1680_VAL_DATA_MODE); + ssd1680_snd_cmd_with_data1(priv, + SSD1680_DATA_MODE, SSD1680_VAL_DATA_MODE); + + /* Step 7: Set ram X start/end postion 00 FF */ + + lcdinfo("Set ram X start/end position (0x%02x): 0, %d\n", + SSD1680_SET_RAMXPOS, (SSD1680_DEV_X_ROUND_UP >> 3)-1); + ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMXPOS, + 0x00, (SSD1680_DEV_X_ROUND_UP >> 3)-1); + + /* Step 8: Set ram Y start/end postion + * TODO w adruino zamieniona start ze stopem + */ + + lcdinfo("Set ram Y start/end position (%x): 0, %d\n", + SSD1680_SET_RAMYPOS, SSD1680_DEV_NATIVE_YRES - 1); + ssd1680_snd_cmd_with_data4(priv, SSD1680_SET_RAMYPOS, + 0x00, 0x00, + (uint8_t)((SSD1680_DEV_NATIVE_YRES - 1) & 0xff), + (SSD1680_DEV_NATIVE_YRES - 1) >> 8); + + /* Step 9: SSD1680_SET_RAMXCOUNT, 0 */ + + ssd1680_snd_cmd_with_data1(priv, SSD1680_SET_RAMXCOUNT, 0x00); + + /* Step 10: SSD1680_SET_RAMYCOUNT, 0, 0 */ + + ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, 0x00, 0x00); + + /* Step 11: Lookup table */ + + lcdinfo("Write lookup table (%d bytes)\n", sizeof (ssd1680_lut)); + ssd1680_snd_cmd_with_data(priv, SSD1680_WRITE_LUT, ssd1680_lut, + sizeof (ssd1680_lut)); + + /* Step 12: Write sequence */ + + lcdinfo("Write controll sequence 0x%02x\n", 0xc0); + ssd1680_snd_cmd_with_data1(priv, SSD1680_DISP_CTRL2, 0xc0); + + /* Step 13: Master Activate and busy wait */ + + ssd1680_snd_cmd_with_data0(priv, SSD1680_MASTER_ACTIVATE); + ssd1680_busy_wait(priv); + + lcdinfo("Configuration ready\n"); + priv->is_conf = true; + return OK; +} + +/**************************************************************************** + * Name: ssd1680_redrawfb + * + * Description: + * Redraw full framebuffer to display + * + * Input Parameters: + * priv - Reference to private driver structure + * + * Assumptions: + * Caller has selected the OLED section. + * + ****************************************************************************/ + +static int ssd1680_redrawfb(struct ssd1680_dev_s *priv) +{ + /* Step 9: SSD1680_SET_RAMXCOUNT, 0 */ + + ssd1680_snd_cmd_with_data1(priv, SSD1680_SET_RAMXCOUNT, 0x00); + + /* Step 10: SSD1680_SET_RAMYCOUNT, 0, 0 */ + +#if defined(CONFIG_LCD_PORTRAIT) | defined(CONFIG_LCD_RLANDSCAPE) + ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, 0x00, 0x00); +#elif defined (CONFIG_LCD_LANDSCAPE) + ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, 0, 0); +#elif defined (CONFIG_LCD_RORTRAIT) +#error "Not implemented LSC orientation" +#endif + + /* Step 14: */ + +#if SSD1680_DEV_BPP == 1 +#if defined(CONFIG_LCD_PORTRAIT) || defined(CONFIG_LCD_RPORTRAIT) + ssd1680_snd_cmd_with_data(priv, + SSD1680_WRITE_RAM1, priv->shadow_fb, SSD1680_DEV_FBSIZE); +#else + int line; +# if SSD1680_DEV_BPP == 1 + for (line = 0; line < SSD1680_DEV_FB_YRES; line += 8) + { + ssd1680_snd_cmd_with_data_bitstrip(priv, SSD1680_WRITE_RAM1, + priv->shadow_fb + line * SSD1680_DEV_NATIVE_YRES, + SSD1680_DEV_NATIVE_YRES, SSD1680_DEV_ROWSIZE); + } +# else +# error "3 color mode not implemented yet" +# endif +#endif + +#else + ssd1680_snd_cmd_with_data_even_bits(priv, + SSD1680_WRITE_RAM1, priv->shadow_fb, SSD1680_DEV_FBSIZE); + + /* Step 9: SSD1680_SET_RAMXCOUNT, 0 */ + + ssd1680_snd_cmd_with_data1(priv, SSD1680_SET_RAMXCOUNT, 0x00); + + /* Step 10: SSD1680_SET_RAMYCOUNT, 0, 0 */ + + ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, 0x00, 0x00); + + /* Step 14b: */ + + ssd1680_snd_cmd_with_data_odd_bits(priv, + SSD1680_WRITE_RAM2, priv->shadow_fb, SSD1680_DEV_FBSIZE); +#endif + ssd1680_redraw_display(priv); + return OK; +} + +static int ssd1680_redraw_display(struct ssd1680_dev_s *priv) +{ + /* Step 15: + * Set control register 2. + * 1 byte of data with following bits: + * 0x80 - enable clock signal + * 0x40 - enable analog + * 0x20 - load temperature value + * 0x10 - Load LUT + * 0x08 - Display Mode 2 + * 0x04 - disable OSC + * 0x02 - disable analog + * 0x01 - disable clock signal + */ + + ssd1680_snd_cmd_with_data1(priv, SSD1680_DISP_CTRL2, 0xc4); + + /* Step 16: */ + + ssd1680_snd_cmd_with_data0(priv, SSD1680_MASTER_ACTIVATE); + ssd1680_busy_wait(priv); + + /* Step 18: */ + + ssd1680_snd_cmd_with_data1(priv, SSD1680_DISP_CTRL2, 0xc3); + + /* Step 19: */ + + ssd1680_snd_cmd_with_data0(priv, SSD1680_MASTER_ACTIVATE); + ssd1680_busy_wait(priv); + + return OK; +} + +static int ssd1680_redrawfb_row(struct ssd1680_dev_s *priv, int row) +{ +#if defined(CONFIG_LCD_PORTRAIT) || defined(CONFIG_LCD_RPORTRAIT) + uint8_t *src = priv->shadow_fb + row * SSD1680_DEV_ROWSIZE; + + ssd1680_snd_cmd_with_data1(priv, SSD1680_SET_RAMXCOUNT, 0); + ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, row, row >> 8); + +#if SSD1680_DEV_BPP == 1 + ssd1680_snd_cmd_with_data(priv, SSD1680_WRITE_RAM1, src, + SSD1680_DEV_ROWSIZE); +#else + ssd1680_snd_cmd_with_data_even_bits(priv, SSD1680_WRITE_RAM1, src, + SSD1680_DEV_ROWSIZE); + + ssd1680_snd_cmd_with_data1(priv, SSD1680_SET_RAMXCOUNT, 1); + ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, + row, row >> 8); + ssd1680_snd_cmd_with_data_odd_bits(priv, + SSD1680_WRITE_RAM2, src, SSD1680_DEV_FBSIZE); +#endif + + if (row == SSD1680_DEV_FB_YRES - 1) + { + ssd1680_redraw_display(priv); + } +#else + int row_group = (row >> 3) << 3; + uint8_t *src = priv->shadow_fb + row_group * SSD1680_DEV_ROWSIZE; + + ssd1680_snd_cmd_with_data1(priv, SSD1680_SET_RAMXCOUNT, row >> 3); + ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, 0, 0); + +#if SSD1680_DEV_BPP == 1 + ssd1680_snd_cmd_with_data_bitstrip(priv, SSD1680_WRITE_RAM1, src, + SSD1680_DEV_NATIVE_YRES, SSD1680_DEV_ROWSIZE); +#else +#error "Landscape mode with 3 colors is not implemented" + /* TODO send ssd1680_snd_cmd_with_data_even_bits_bitstrip + * (priv, SSD1680_WRITE_RAM1, src, SSD1680_DEV_NATIVE_YRES, + * SSD1680_DEV_ROWSIZE); + */ + + ssd1680_snd_cmd_with_data1(priv, SSD1680_SET_RAMXCOUNT, row >> 3); + ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, 0, 0); + + /* TODO send ssd1680_snd_cmd_with_data_odd_bits_bitstrip(priv, + * SSD1680_WRITE_RAM2, src, SSD1680_DEV_NATIVE_YRESSSD1680_DEV_ROWSIZE, ); + */ + +#endif + + if (row == SSD1680_DEV_FB_YRES - 1) + { + ssd1680_redraw_display(priv); + } +#endif + + return OK; +} + +/**************************************************************************** + * Name: ssd1680_configspi + * + * Description: + * + ****************************************************************************/ + +static void ssd1680_configspi(FAR struct spi_dev_s *spi) +{ + /* Configure SPI for the SSD1680 */ + + SPI_SETMODE(spi, CONFIG_SSD1680_SPIMODE); + SPI_SETBITS(spi, 8); + SPI_HWFEATURES(spi, 0); + SPI_SETFREQUENCY(spi, CONFIG_SSD1680_FREQUENCY); +} + +/**************************************************************************** + * Name: ssd1680_select + * + * Description: + * Enable/Disable SSD1680 SPI CS + * + ****************************************************************************/ + +static void ssd1680_select(FAR struct ssd1680_dev_s *priv, bool cs) +{ + /* If we are selecting the device */ + + if (cs == true) + { + /* If SPI bus is shared then lock and configure it */ + + SPI_LOCK(priv->spi, true); + ssd1680_configspi(priv->spi); + } + + /* Select/deselect SPI device */ + + SPI_SELECT(priv->spi, SPIDEV_DISPLAY(0), cs); + + /* If we are deselecting the device */ + + if (cs == false) + { + /* Unlock bus */ + + SPI_LOCK(priv->spi, false); + } +} + +static void ssd1680_busy_wait(FAR struct ssd1680_dev_s *priv) +{ + if ((priv->board_priv != NULL) && (priv->board_priv->check_busy != NULL)) + { + while (priv->board_priv->check_busy()) + { + nxsig_usleep(1); + } + } + else + { + nxsig_usleep(2000000); + } +} + +static void ssd1680_snd_cmd_with_data0(FAR struct ssd1680_dev_s *priv, + uint8_t cmd) +{ + ssd1680_select(priv, true); + ssd1680_cmddata(priv, true); + SPI_SEND(priv->spi, cmd); + ssd1680_select(priv, false); +} + +static void ssd1680_snd_cmd_with_data1(FAR struct ssd1680_dev_s *priv, + uint8_t cmd, uint8_t dta1) +{ + ssd1680_select(priv, true); + ssd1680_cmddata(priv, true); + SPI_SEND(priv->spi, cmd); + ssd1680_cmddata(priv, false); + SPI_SEND(priv->spi, dta1); + ssd1680_select(priv, false); +} + +static void ssd1680_snd_cmd_with_data2(FAR struct ssd1680_dev_s *priv, + uint8_t cmd, uint8_t dta1, uint8_t dta2) +{ + ssd1680_select(priv, true); + ssd1680_cmddata(priv, true); + SPI_SEND(priv->spi, cmd); + ssd1680_cmddata(priv, false); + SPI_SEND(priv->spi, dta1); + SPI_SEND(priv->spi, dta2); + ssd1680_select(priv, false); +} + +static void ssd1680_snd_cmd_with_data3(FAR struct ssd1680_dev_s *priv, + uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3) +{ + ssd1680_select(priv, true); + ssd1680_cmddata(priv, true); + SPI_SEND(priv->spi, cmd); + ssd1680_cmddata(priv, false); + SPI_SEND(priv->spi, dta1); + SPI_SEND(priv->spi, dta2); + SPI_SEND(priv->spi, dta3); + ssd1680_select(priv, false); +} + +static void ssd1680_snd_cmd_with_data4(FAR struct ssd1680_dev_s *priv, + uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3, uint8_t dta4) +{ + ssd1680_select(priv, true); + ssd1680_cmddata(priv, true); + SPI_SEND(priv->spi, cmd); + ssd1680_cmddata(priv, false); + SPI_SEND(priv->spi, dta1); + SPI_SEND(priv->spi, dta2); + SPI_SEND(priv->spi, dta3); + SPI_SEND(priv->spi, dta4); + ssd1680_select(priv, false); +} + +static void ssd1680_snd_cmd_with_data(FAR struct ssd1680_dev_s *priv, + uint8_t cmd, const uint8_t *dta, int dta_len) +{ + ssd1680_select(priv, true); + ssd1680_cmddata(priv, true); + SPI_SEND(priv->spi, cmd); + ssd1680_cmddata(priv, false); + SPI_SNDBLOCK(priv->spi, dta, dta_len); + ssd1680_select(priv, false); +} + +#if !defined(CONFIG_LCD_PORTRAIT) && !defined(CONFIG_LCD_RPORTRAIT) + +#if SSD1680_DEV_BPP == 1 +static void ssd1680_snd_cmd_with_data_bitstrip( + FAR struct ssd1680_dev_s *priv, uint8_t cmd, const uint8_t *dta, + int nopix, int strip_len) +{ + int i; + int j; + uint8_t bytes[8]; + uint8_t val; + +#if defined(CONFIG_LCD_LANDSCAPE) + dta += (strip_len - 1); +#endif + + ssd1680_select(priv, true); + ssd1680_cmddata(priv, true); + SPI_SEND(priv->spi, cmd); + ssd1680_cmddata(priv, false); + while (nopix > 0) + { + for (j = 0; j < 8; j++) + { + bytes[j] = *(dta + j * strip_len); + } + + for (i = 0; i < 8; i++) + { + val = 0; + for (j = 0; j < 8; j++) + { +#if defined(CONFIG_LCD_LANDSCAPE) + val |= ((bytes[j] << (7 - j)) & (1 << (7 - j))); + bytes[j] = bytes[j] >> 1; +#elif + val |= ((bytes[j] >> j) & (1 << (7 - j))); + bytes[j] = bytes[j] << 1; +#endif + } + + SPI_SEND(priv->spi, val); + nopix--; + if (nopix == 0) + { + break; + } + } + +#if defined(CONFIG_LCD_LANDSCAPE) + dta--; +#elif + dta++; +#endif + } + + ssd1680_select(priv, false); +} +#else +static void ssd1680_snd_cmd_with_data_even_bits_bitstrip( + FAR struct ssd1680_dev_s *priv, uint8_t cmd, const uint8_t *dta, + int nopix, int strip_len) +{ + int i; + int j; + uint8_t rows[8]; + uint16_t tmp[8]; + uint8_t val; + +#if defined(CONFIG_LCD_LANDSCAPE) + dta += (strip_len - 1); +#endif + + ssd1680_select(priv, true); + ssd1680_cmddata(priv, true); + SPI_SEND(priv->spi, cmd); + ssd1680_cmddata(priv, false); + while (nopix > 0) + { + for (j = 0; j < 8; j++) + { +#if defined(CONFIG_LCD_LANDSCAPE) + tmp = *(dta + j * strip_len) + + ((*(dta + j * strip_len + 1)) << 8); +#else + tmp = *(dta + j * strip_len + 1) + + ((*(dta + j * strip_len)) << 8); +#endif + rows[j] = + (tmp & 0x01) | ((tmp >> 1) & 0x02) + | ((tmp >> 2) & 0x04) | ((tmp >> 3) & 0x08) + | ((tmp >> 4) & 0x10) | ((tmp >> 5) & 0x20) + | ((tmp >> 6) & 0x40) | ((tmp >> 7) & 0x80); + } + + for (i = 0; i < 8; i++) + { + val = 0; + for (j = 0; j < 8; j++) + { +#if defined(CONFIG_LCD_LANDSCAPE) + val |= ((rows[j] << (7 - j)) & (1 << (7 - j))); + rows[j] = rows[j] >> 1; +#elif + val |= ((rows[j] >> (j)) & (1 << (7 - j))); + rows[j] = rows[j] << 1; +#endif + } + + SPI_SEND(priv->spi, val); + nopix--; + if (nopix == 0) + { + break; + } + } + +#if defined(CONFIG_LCD_LANDSCAPE) + dta--; +#elif + dta++; +#endif + } + + ssd1680_select(priv, false); +} + +static void ssd1680_snd_cmd_with_data_odd_bits_bitstrip( + FAR struct ssd1680_dev_s *priv, uint8_t cmd, const uint8_t *dta, + int nopix, int strip_len) +{ + int i; + int j; + uint16_t rows[8]; + uint8_t val; + +#if defined(CONFIG_LCD_LANDSCAPE) + dta += (strip_len - 1); +#endif + + ssd1680_select(priv, true); + ssd1680_cmddata(priv, true); + SPI_SEND(priv->spi, cmd); + ssd1680_cmddata(priv, false); + while (nopix > 0) + { + for (j = 0; j < 8; j++) + { +#if defined(CONFIG_LCD_LANDSCAPE) + rows[j] = *(dta + j * strip_len) + + ((*(dta + j * strip_len + 1)) << 8); +#else + rows[j] = *(dta + j * strip_len + 1) + + ((*(dta + j * strip_len)) << 8); +#endif + } + + for (i = 0; i < 8; i++) + { + val = 0; + for (j = 0; j < 8; j++) + { +#if defined(CONFIG_LCD_LANDSCAPE) + val |= ((rows[j] << (7 - j)) & (1 << (7 - j))); + rows[j] = rows[j] >> 2; +#elif + val |= ((rows[j] >> (j)) & (1 << (7 - j))); + rows[j] = rows[j] << 2; +#endif + } + + SPI_SEND(priv->spi, val); + nopix--; + if (nopix == 0) + { + break; + } + } + +#if defined(CONFIG_LCD_LANDSCAPE) + dta--; +#elif + dta++; +#endif + } + + ssd1680_select(priv, false); +} +#endif +#endif + +#if SSD1680_DEV_BPP == 2 +static void ssd1680_snd_cmd_with_data_even_bits( + FAR struct ssd1680_dev_s *priv, uint8_t cmd, const uint8_t *dta, + int dta_len) +{ + int i; + uint8_t dta_byte; + uint8_t src1; + uint8_t src2; + ssd1680_select(priv, true); + ssd1680_cmddata(priv, true); + SPI_SEND(priv->spi, cmd); + ssd1680_cmddata(priv, false); + + for (i = 0; i < dta_len; i++) + { + src1 = *(dta++); + src2 = *(dta++); + dta_byte = (src1 & 0x01) | ((src1 >> 1) & 0x02) + | ((src1 >> 2) & 0x04) | ((src1 >> 3) & 0x08) + | ((src2 << 4) & 0x10) | ((src2 << 3) & 0x20) + | ((src2 << 2) & 0x40) | ((src2 << 1) & 0x80); + SPI_SEND(priv->spi, dta_byte); + } + + ssd1680_select(priv, false); +} + +static void ssd1680_snd_cmd_with_data_odd_bits( + FAR struct ssd1680_dev_s *priv, uint8_t cmd, const uint8_t *dta, + int dta_len) +{ + int i; + uint8_t dta_byte; + uint8_t src1; + uint8_t src2; + ssd1680_select(priv, true); + ssd1680_cmddata(priv, true); + SPI_SEND(priv->spi, cmd); + ssd1680_cmddata(priv, false); + + for (i = 0; i < dta_len; i++) + { + src1 = *(dta++); + src2 = *(dta++); + dta_byte = ((src1 >> 1 & 0x01)) | ((src1 >> 2) & 0x02) + | ((src1 >> 3) & 0x04) | ((src1 >> 4) & 0x08) + | ((src2 << 3) & 0x10) | ((src2 << 2) & 0x20) + | ((src2 << 1) & 0x40) | (src2 & 0x80); + SPI_SEND(priv->spi, dta_byte); + } + + ssd1680_select(priv, false); +} +#endif + +/**************************************************************************** + * Name: ssd1680_cmddata + * + * Description: + * Select Command/Data mode for SSD1680 + * + ****************************************************************************/ + +inline static void ssd1680_cmddata(FAR struct ssd1680_dev_s *priv, bool cmd) +{ + SPI_CMDDATA(priv->spi, SPIDEV_DISPLAY(0), cmd); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ssd1680_initialize + * + * Description: + * Initialize the video hardware. The initial state of the OLED is + * fully initialized, display memory cleared, and the OLED ready + * to use, but with the power setting at 0 (full off == sleep mode). + * + * Input Parameters: + * + * dev - A reference to the SPI driver instance. + * board_priv - Board specific structure. + * + * Returned Value: + * + * On success, this function returns a reference to the LCD object for + * the specified OLED. NULL is returned on any failure. + * + ****************************************************************************/ + +FAR struct lcd_dev_s *ssd1680_initialize(FAR struct spi_dev_s *dev, + FAR const struct ssd1680_priv_s *board_priv) +{ + FAR struct ssd1680_dev_s *priv = &g_epaperdev; + DEBUGASSERT(dev); + + priv->dev = g_lcd_epaper_dev; + priv->on = false; + priv->is_conf = false; + + /* Register board specific functions */ + + priv->board_priv = board_priv; + priv->spi = dev; + + /* Configure the SPI */ + + ssd1680_configspi(priv->spi); + + /* Initialize the framebuffer */ + + memset(priv->shadow_fb, SSD1680_Y1_BLACK & 1 ? + 0xff : 0x00, SSD1680_DEV_FBSIZE); + + /* Power on and configure display */ + + ssd1680_setpower(&priv->dev, true); + + return &priv->dev; +} + +FAR void *bitscpy_ds(FAR void *dest, int dest_offset, FAR const void *src, + size_t nbits) +{ + FAR unsigned char *pout = (FAR unsigned char *)dest; + FAR unsigned char *pin = (FAR unsigned char *)src; + uint8_t val; + + /* Copy block of bytes */ + + while (nbits >= 8) + { + /* Read. MSB is first */ + + val = *pin++; + + /* Write */ + + if (dest_offset == 0) + { + *pout++ = val; + } + else + { + *pout &= (~(0xff >> dest_offset)); + *pout |= (val >> dest_offset); + pout++; + *pout &= ~(0xff << (8 - dest_offset)); + *pout |= (val << (8 - dest_offset)); + } + + nbits -= 8; + } + + /* Copy last bits 1-7 */ + + if (nbits > 0) + { + val = *pin; + + if (nbits + dest_offset <= 8) + { + *pout &= (~((0xff << (8 - nbits)) >> dest_offset)); + *pout |= (val & (0xff << (8 - nbits)) >> dest_offset); + } + else + { + *pout &= (~(0xff >> dest_offset)); + *pout |= (val >> dest_offset); + pout++; + + nbits -= (8 - dest_offset); + *pout &= ~(0xff << (8 - nbits)); + *pout |= ((val << (8 - dest_offset)) & (0xff << (8 - nbits))); + } + } + + return dest; +} + +FAR void *bitscpy_ss(FAR void *dest, FAR const void *src, int src_offset, + size_t nbits) +{ + FAR unsigned char *pout = (FAR unsigned char *)dest; + FAR unsigned char *pin = (FAR unsigned char *)src; + uint8_t val; + + /* Copy block of bytes */ + + while (nbits >= 8) + { + /* Read. MSB is first */ + + if (src_offset == 0) + { + val = *pin++; + } + else + { + val = ((*pin) << src_offset); + pin++; + val |= ((*pin) >> (8 - src_offset)); + } + + /* Write */ + + *pout++ = val; + nbits -= 8; + } + + /* Copy last bits 1-7 */ + + if (nbits > 0) + { + val = ((*pin) << src_offset); + if (nbits + src_offset > 8) + { + pin++; + val |= ((*pin & (~(0xff >> (nbits + src_offset - 8)) + >> (8 - src_offset)))); + } + + *pout &= (~((0xff << (8 - nbits)))); + *pout |= (val & (0xff << (8 - nbits))); + } + + return dest; +} + +#endif /* CONFIG_LCD_SSD1680 */ diff --git a/drivers/lcd/ssd1680.h b/drivers/lcd/ssd1680.h new file mode 100644 index 0000000000..6f8b9491da --- /dev/null +++ b/drivers/lcd/ssd1680.h @@ -0,0 +1,197 @@ +/**************************************************************************** + * drivers/lcd/ssd1680.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __DRIVERS_LCD_SSD1680_H +#define __DRIVERS_LCD_SSD1680_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdint.h> +#include <stdbool.h> + +#include <nuttx/lcd/lcd.h> +#include <nuttx/lcd/ssd1680.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* Limitations of the current configuration that I hope to fix someday */ + +#if !defined(CONFIG_LCD_SSD1680_2_13) && !defined(CONFIG_LCD_SSD1680_2_9) +# error "Unknown and unsupported SSD16800 LCD" +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef CONFIG_NX_BGCOLOR +# define CONFIG_NX_BGCOLOR SSD1680_Y1_BLACK +#endif + +/* SSD1680 Commands *********************************************************/ + +#define SSD1680_DRIVER_CONTROL 0x01 +#define SSD1680_GATE_VOLTAGE 0x03 +#define SSD1680_SOURCE_VOLTAGE 0x04 +#define SSD1680_PROGOTP_INITIAL 0x08 +#define SSD1680_PROGREG_INITIAL 0x09 +#define SSD1680_READREG_INITIAL 0x0A +#define SSD1680_BOOST_SOFTSTART 0x0C +#define SSD1680_GATE_SCAN_POSITION 0x0F +#define SSD1680_DEEP_SLEEP 0x10 +#define SSD1680_DATA_MODE 0x11 +#define SSD1680_SW_RESET 0x12 +#define SSD1680_TEMP_CONTROL 0x18 +#define SSD1680_TEMP_WRITE 0x1A + +/* Execute sequence written in Control Register 2 + * Wait till SSD1680 is not busy + */ + +#define SSD1680_MASTER_ACTIVATE 0x20 + +#define SSD1680_DISP_CTRL1 0x21 + +/* Set control register 2. + * 1 byte of data with following bits: + * 0x80 - enable clock signal + * 0x40 - enable analog + * 0x20 - load temperature value (endless busy state. Don't use) + * 0x10 - Load LUT (endless busy state. Probably OTP wasn't prepared) + * 0x08 - Display Mode 2 + * 0x04 - disable OSC + * 0x01 - disable clock signal + * 0x02 - disable analog + */ + +#define SSD1680_DISP_CTRL2 0x22 + +#define SSD1680_WRITE_RAM1 0x24 +#define SSD1680_WRITE_RAM2 0x26 +#define SSD1680_WRITE_VCOM 0x2C +#define SSD1680_READ_OTP 0x2D +#define SSD1680_READ_STATUS 0x2F +#define SSD1680_WRITE_LUT 0x32 +#define SSD1680_WRITE_BORDER 0x3C +#define SSD1680_SET_RAMXPOS 0x44 +#define SSD1680_SET_RAMYPOS 0x45 +#define SSD1680_SET_RAMXCOUNT 0x4E +#define SSD1680_SET_RAMYCOUNT 0x4F +#define SSD1680_NOP 0x7F + +/* Color Properties *********************************************************/ +#if defined(CONFIG_LCD_SSD1680_2_13) || defined(CONFIG_LCD_SSD1680_2_9) +/* 1 bit per pixel */ +# define SSD1680_DEV_COLORFMT FB_FMT_Y1 +# define SSD1680_DEV_BPP 1 +# define SSD1680_NO_OF_PLANES 1 +#elif defined (CONFIG_LCD_SSD1680_2_13_RED) || (CONFIG_LCD_SSD1680_2_9_RED) +/* 2 bits per pixel, it is not agrayscale, but indexed colours */ +# define SSD1680_DEV_COLORFMT FB_FMT_Y2 +# define SSD1680_DEV_BPP 2 +# define SSD1680_NO_OF_PLANES 1 +#else +#error "Can't prepare macros for not supported display" +#endif + +#if SSD1680_DEV_BPP == 1 +# define SSD1680_PDF 3 +# define SSD1680_PDV 8 +#elif SSD1680_DEV_BPP == 2 +# define SSD1680_PDF 2 +# define SSD1680_PDV 4 +#else +# error "SSD1680: Bits per pixel not defined" +#endif + +/* Display Resolution + * + * The SSD1680 display controller can handle a resolution of 176/296. + * The portrait mode is default. + * Display size vs its resolution: + * 2.13 : 122 / 250 + * 2.9 : 128 / 296 + */ + +#if defined(CONFIG_LCD_SSD1680_2_13) || defined(CONFIG_LCD_SSD1680_2_13_RED) +# define SSD1680_DEV_GATE_LAYOUT 0x00 +# define SSD1680_DEV_NATIVE_XRES 122 +# define SSD1680_DEV_NATIVE_YRES 250 +#elif defined(CONFIG_LCD_SSD1680_2_9) || defined(CONFIG_LCD_SSD1680_2_9_RED) +# define SSD1680_DEV_GATE_LAYOUT 0x00 +# define SSD1680_DEV_NATIVE_XRES 128 +# define SSD1680_DEV_NATIVE_YRES 296 +#endif + +/* SSD1680 memory write algorithm */ +#if defined(CONFIG_LCD_PORTRAIT) +# define SSD1680_VAL_DATA_MODE 0x03 +#elif defined(CONFIG_LCD_RPORTRAIT) +# define SSD1680_VAL_DATA_MODE 0x02 +#elif defined(CONFIG_LCD_LANDSCAPE) +# define SSD1680_VAL_DATA_MODE 0x07 +#elif defined(CONFIG_LCD_RLANDSCAPE) +# define SSD1680_VAL_DATA_MODE 0x07 +#endif + +/* Rotating screen if the orientation is changed */ +#if defined(CONFIG_LCD_PORTRAIT) || defined(CONFIG_LCD_RPORTRAIT) + +/* Calculate number of bytes per row. + * In case of tri-color display there are two 1-bit data arrays + * In order to simplify communication with controller, we need to provide + * a number that is multiplication of 8 (not 4 even if we have 2 bpp) + */ + +# define SSD1680_DEV_ROWSIZE \ + ((SSD1680_DEV_NATIVE_XRES + SSD1680_PDV - 1) >> SSD1680_PDF) +# define SSD1680_DEV_FB_XRES SSD1680_DEV_NATIVE_XRES +# define SSD1680_DEV_FB_YRES SSD1680_DEV_NATIVE_YRES +#else /* Landstape */ +# define SSD1680_DEV_ROWSIZE \ + ((SSD1680_DEV_NATIVE_YRES + SSD1680_PDV - 1) >> SSD1680_PDF) +# define SSD1680_DEV_FB_XRES SSD1680_DEV_NATIVE_YRES +# define SSD1680_DEV_FB_YRES SSD1680_DEV_NATIVE_XRES +#endif /* Landstape */ + +#define SSD1680_DEV_X_ROUND_UP \ +(((SSD1680_DEV_NATIVE_XRES + SSD1680_PDV - 1) >> SSD1680_PDF) << SSD1680_PDF) +#define SSD1680_DEV_Y_ROUND_UP \ +(((SSD1680_DEV_NATIVE_YRES + SSD1680_PDV - 1) >> SSD1680_PDF) << SSD1680_PDF) + +#define SSD1680_DEV_FBSIZE ((SSD1680_DEV_ROWSIZE) * (SSD1680_DEV_FB_YRES)) + +/**************************************************************************** + * Public Type Definition + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#endif /* __DRIVERS_LCD_SSD1680_H */ diff --git a/include/nuttx/lcd/ssd1680.h b/include/nuttx/lcd/ssd1680.h new file mode 100644 index 0000000000..9fcdf37d87 --- /dev/null +++ b/include/nuttx/lcd/ssd1680.h @@ -0,0 +1,193 @@ +/**************************************************************************** + * include/nuttx/lcd/ssd1680.h + * + * Driver for Solomon Systech SSD1680 e-paper controller + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_LCD_SSD1680_H +#define __INCLUDE_NUTTX_LCD_SSD1680_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdbool.h> + +#include <nuttx/arch.h> + +#ifdef CONFIG_LCD_SSD1680 +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* SSD1680 configuration settings: + * + * CONFIG_SSD1680_SPIMODE - Controls the SPI mode + * CONFIG_SSD1680_FREQUENCY - Define to use a different bus frequency + * + * Required LCD driver settings: + * + * CONFIG_LCD_MAXCONTRAST should be 255, but any value >0 and <=255 + * will be accepted. + * CONFIG_LCD_MAXPOWER must be 1 + * + * Optional LCD driver settings: + * CONFIG_LCD_LANDSCAPE, CONFIG_LCD_PORTRAIT, CONFIG_LCD_RLANDSCAPE, and + * CONFIG_LCD_RPORTRAIT - Display orientation. + * + * Required SPI driver settings: + * CONFIG_SPI_CMDDATA - Include support for cmd/data selection. + */ + +/* SPI Interface + * + * "The serial interface consists of serial clock SCL, serial data SI, CS and + * CMD/!DTA. SI is shifted into an 8-bit shift register on every rising edge + * of SCL in the order of D7, D6, ... and D0. CMD/!DTA is sampled on every + * eighth clock and the data byte in the shift register is written to the + * display data RAM or command register in the same clock." + * + * "This module determines whether the input data is interpreted as data or + * command. When CMD/!DTA = "H," the inputs at D7 - D0 are interpreted as + * data and be written to display RAM. When CMD/!DTA = "L", the inputs at + * D7 - D0 are interpreted as command, they will be decoded and be written + * to the corresponding command registers." + */ + +#ifndef CONFIG_SPI_CMDDATA +# error "CONFIG_SPI_CMDDATA must be defined in your NuttX configuration" +#endif + +/* Check contrast selection */ + +#if !defined(CONFIG_LCD_MAXCONTRAST) +# define CONFIG_LCD_MAXCONTRAST 255 +#endif + +#if CONFIG_LCD_MAXCONTRAST <= 0 || CONFIG_LCD_MAXCONTRAST > 255 +# error "CONFIG_LCD_MAXCONTRAST exceeds supported maximum" +#endif + +/* Check power setting */ + +#if !defined(CONFIG_LCD_MAXPOWER) +# define CONFIG_LCD_MAXPOWER 1 +#endif + +#if CONFIG_LCD_MAXPOWER != 1 +# warning "CONFIG_LCD_MAXPOWER exceeds supported maximum" +# undef CONFIG_LCD_MAXPOWER +# define CONFIG_LCD_MAXPOWER 1 +#endif + +/* Color is 1bpp monochrome with leftmost column contained in bits 0 */ + +#ifdef CONFIG_NX_DISABLE_1BPP +# warning "1 bit-per-pixel support needed" +#endif + +/* Orientation */ + +#if defined(CONFIG_LCD_LANDSCAPE) +# undef CONFIG_LCD_PORTRAIT +# undef CONFIG_LCD_RLANDSCAPE +# undef CONFIG_LCD_RPORTRAIT +#elif defined(CONFIG_LCD_PORTRAIT) +# undef CONFIG_LCD_LANDSCAPE +# undef CONFIG_LCD_RLANDSCAPE +# undef CONFIG_LCD_RPORTRAIT +#elif defined(CONFIG_LCD_RLANDSCAPE) +# undef CONFIG_LCD_LANDSCAPE +# undef CONFIG_LCD_PORTRAIT +# undef CONFIG_LCD_RPORTRAIT +#elif defined(CONFIG_LCD_RPORTRAIT) +# undef CONFIG_LCD_LANDSCAPE +# undef CONFIG_LCD_PORTRAIT +# undef CONFIG_LCD_RLANDSCAPE +#else +# define CONFIG_LCD_LANDSCAPE 1 +# warning "Assuming landscape orientation" +#endif + +/* Some important "colors" */ + +#define SSD1680_Y1_BLACK 0 +#define SSD1680_Y1_WHITE 1 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct ssd1680_priv_s +{ + bool (*set_vcc) (bool on); /* Allow board to control display power. Return + * true if request state set successfully. */ + bool (*set_rst) (bool on); /* Hardware reset support */ + bool (*check_busy) (void); /* Checks the state of busy pin */ +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: ssd1680initialize + * + * Description: + * Initialize the video hardware. The initial state of the OLED is + * fully initialized, display memory cleared, and the OLED ready to + * use, but with the power setting at 0 (full off == sleep mode). + * + * Input Parameters: + * + * dev - A reference to the SPI driver instance. + * board_priv - Board specific structure. + * + * Returned Value: + * + * On success, this function returns a reference to the LCD object for + * the specified OLED. NULL is returned on any failure. + * + ****************************************************************************/ + +struct lcd_dev_s; /* See include/nuttx/lcd/lcd.h */ +struct spi_dev_s; /* See include/nuttx/spi/spi.h */ + +FAR struct lcd_dev_s *ssd1680_initialize(FAR struct spi_dev_s *dev, + FAR const struct ssd1680_priv_s *board_priv); + +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_LCD_SSD1680 */ +#endif /* __INCLUDE_NUTTX_LCD_SSD1680_H */