Add support for SSD1680 e-paper controller

The driver is based on the construction of IL3820 (SSD1608), which is very similar from command point of view.
It is tested on ESP32-S2 MCU and GoodDisplay GDEY029T94.
This commit is contained in:
AVartanyan 2022-09-26 23:49:24 +03:00 committed by GitHub
parent 26fe6e7703
commit f4fd82bd7e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 522 additions and 4 deletions

View file

@ -32,6 +32,8 @@ elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_FT81X)
list(APPEND SOURCES "lvgl_tft/FT81x.c") list(APPEND SOURCES "lvgl_tft/FT81x.c")
elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820) elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820)
list(APPEND SOURCES "lvgl_tft/il3820.c") list(APPEND SOURCES "lvgl_tft/il3820.c")
elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1680)
list(APPEND SOURCES "lvgl_tft/ssd1680.c")
elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_JD79653A) elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_JD79653A)
list(APPEND SOURCES "lvgl_tft/jd79653a.c") list(APPEND SOURCES "lvgl_tft/jd79653a.c")
elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D) elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D)

View file

@ -34,6 +34,7 @@ swap of RGB565 color on the LVGL configuration menuconfig (it's not handled auto
| IL3820 | e-Paper | SPI | 1: 1byte per pixel | No | | IL3820 | e-Paper | SPI | 1: 1byte per pixel | No |
| UC8151D/ GoodDisplay GDEW0154M10 DES | e-Paper | SPI | 1: 1byte per pixel | No | | UC8151D/ GoodDisplay GDEW0154M10 DES | e-Paper | SPI | 1: 1byte per pixel | No |
| FitiPower JD79653A/ GoodDisplay GDEW0154M09 | e-Paper | SPI | 1: 1byte per pixel | No | | FitiPower JD79653A/ GoodDisplay GDEW0154M09 | e-Paper | SPI | 1: 1byte per pixel | No |
| SSD1680 / GoodDisplay GDEY029T94 | e-Paper | SPI | 1: 1byte per pixel | No |
## Supported indev controllers ## Supported indev controllers

View file

@ -25,6 +25,7 @@ $(call compile_only_if,$(CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1306),lvgl_tft/ssd1
$(call compile_only_if,$(CONFIG_LV_TFT_DISPLAY_CONTROLLER_FT81X),lvgl_tft/EVE_commands.o) $(call compile_only_if,$(CONFIG_LV_TFT_DISPLAY_CONTROLLER_FT81X),lvgl_tft/EVE_commands.o)
$(call compile_only_if,$(CONFIG_LV_TFT_DISPLAY_CONTROLLER_FT81X),lvgl_tft/FT81x.o) $(call compile_only_if,$(CONFIG_LV_TFT_DISPLAY_CONTROLLER_FT81X),lvgl_tft/FT81x.o)
$(call compile_only_if,$(CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820),lvgl_tft/il3820.o) $(call compile_only_if,$(CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820),lvgl_tft/il3820.o)
$(call compile_only_if,$(CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1306),lvgl_tft/ssd1680.o)
$(call compile_only_if,$(CONFIG_LV_TFT_DISPLAY_CONTROLLER_JD79653A),lvgl_tft/jd79653a.o) $(call compile_only_if,$(CONFIG_LV_TFT_DISPLAY_CONTROLLER_JD79653A),lvgl_tft/jd79653a.o)
$(call compile_only_if,$(CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D),lvgl_tft/uc8151d.o) $(call compile_only_if,$(CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D),lvgl_tft/uc8151d.o)
$(call compile_only_if,$(CONFIG_LV_TFT_DISPLAY_CONTROLLER_RA8875),lvgl_tft/ra8875.o) $(call compile_only_if,$(CONFIG_LV_TFT_DISPLAY_CONTROLLER_RA8875),lvgl_tft/ra8875.o)

View file

@ -154,7 +154,7 @@ bool lvgl_spi_driver_init(int host,
int dma_channel, int dma_channel,
int quadwp_pin, int quadhd_pin) int quadwp_pin, int quadhd_pin)
{ {
assert((0 <= host) && (SPI_HOST_MAX > host)); //assert((0 <= host) && (SPI_HOST_MAX > host));
const char *spi_names[] = { const char *spi_names[] = {
"SPI1_HOST", "SPI2_HOST", "SPI3_HOST" "SPI1_HOST", "SPI2_HOST", "SPI3_HOST"
}; };

View file

@ -66,6 +66,8 @@ extern "C" {
#define DISP_BUF_SIZE (LV_HOR_RES_MAX * DISP_BUF_LINES) #define DISP_BUF_SIZE (LV_HOR_RES_MAX * DISP_BUF_LINES)
#elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820) #elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820)
#define DISP_BUF_SIZE (LV_VER_RES_MAX * IL3820_COLUMNS) #define DISP_BUF_SIZE (LV_VER_RES_MAX * IL3820_COLUMNS)
#elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1680)
#define DISP_BUF_SIZE (LV_VER_RES_MAX * SSD1680_COLUMNS)
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_RA8875 #elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_RA8875
#define DISP_BUF_SIZE (LV_HOR_RES_MAX * 40) #define DISP_BUF_SIZE (LV_HOR_RES_MAX * 40)
#elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_GC9A01) #elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_GC9A01)

View file

@ -126,6 +126,7 @@ extern "C" {
defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_SH1107) || \ defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_SH1107) || \
defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_FT81X) || \ defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_FT81X) || \
defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820) || \ defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820) || \
defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1680) || \
defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_JD79653A) || \ defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_JD79653A) || \
defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9163C) defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9163C)

View file

@ -149,6 +149,14 @@ menu "LVGL TFT Display controller"
help help
IL3820 epaper display controller. IL3820 epaper display controller.
config LV_TFT_DISPLAY_CONTROLLER_SSD1680
bool
help
SSD1680 e-paper display controller for GoodDisplay GDEY029T94.
The resolution must be set as per a portrait mode:
LV_HOR_RES_MAX -> 128 and LV_VER_RES_MAX -> 296
And the actual use mode currently is Landskape only.
config LV_TFT_DISPLAY_CONTROLLER_JD79653A config LV_TFT_DISPLAY_CONTROLLER_JD79653A
bool bool
help help
@ -326,6 +334,12 @@ menu "LVGL TFT Display controller"
select LV_TFT_DISPLAY_CONTROLLER_IL3820 select LV_TFT_DISPLAY_CONTROLLER_IL3820
select LV_TFT_DISPLAY_PROTOCOL_SPI select LV_TFT_DISPLAY_PROTOCOL_SPI
select LV_TFT_DISPLAY_MONOCHROME select LV_TFT_DISPLAY_MONOCHROME
config LV_TFT_DISPLAY_USER_CONTROLLER_SSD1680
bool "SSD1680"
select LV_TFT_DISPLAY_CONTROLLER_SSD1680
select LV_TFT_DISPLAY_PROTOCOL_SPI
select LV_TFT_DISPLAY_MONOCHROME
select LV_TFT_USE_CUSTOM_SPI_CLK_DIVIDER
config LV_TFT_DISPLAY_USER_CONTROLLER_JD79653A config LV_TFT_DISPLAY_USER_CONTROLLER_JD79653A
bool "JD79653A" bool "JD79653A"
select LV_TFT_DISPLAY_CONTROLLER_JD79653A select LV_TFT_DISPLAY_CONTROLLER_JD79653A
@ -521,7 +535,7 @@ menu "LVGL TFT Display controller"
default LV_TFT_SPI_CLK_DIVIDER_5 if LV_TFT_DISPLAY_CONTROLLER_ILI9481 default LV_TFT_SPI_CLK_DIVIDER_5 if LV_TFT_DISPLAY_CONTROLLER_ILI9481
default LV_TFT_SPI_CLK_DIVIDER_3 if LV_TFT_DISPLAY_CONTROLLER_HX8357 default LV_TFT_SPI_CLK_DIVIDER_3 if LV_TFT_DISPLAY_CONTROLLER_HX8357
default LV_TFT_SPI_CLK_DIVIDER_10 if LV_TFT_DISPLAY_CONTROLLER_SH1107 default LV_TFT_SPI_CLK_DIVIDER_10 if LV_TFT_DISPLAY_CONTROLLER_SH1107
default LV_TFT_SPI_CLK_DIVIDER_16 if LV_TFT_DISPLAY_CONTROLLER_JD79653A || LV_TFT_DISPLAY_CONTROLLER_UC8151D default LV_TFT_SPI_CLK_DIVIDER_16 if LV_TFT_DISPLAY_CONTROLLER_JD79653A || LV_TFT_DISPLAY_CONTROLLER_UC8151D || LV_TFT_DISPLAY_CONTROLLER_SSD1680
default LV_TFT_SPI_CLK_DIVIDER_2 default LV_TFT_SPI_CLK_DIVIDER_2
config LV_TFT_SPI_CLK_DIVIDER_1 config LV_TFT_SPI_CLK_DIVIDER_1
@ -907,8 +921,8 @@ menu "LVGL TFT Display controller"
Configure the display Reset pin here. Configure the display Reset pin here.
config LV_DISP_PIN_BUSY config LV_DISP_PIN_BUSY
int "GPIO for Busy" if LV_TFT_DISPLAY_CONTROLLER_IL3820 || LV_TFT_DISPLAY_CONTROLLER_JD79653A || LV_TFT_DISPLAY_CONTROLLER_UC8151D int "GPIO for Busy" if LV_TFT_DISPLAY_CONTROLLER_IL3820 || LV_TFT_DISPLAY_CONTROLLER_JD79653A || LV_TFT_DISPLAY_CONTROLLER_UC8151D || LV_TFT_DISPLAY_CONTROLLER_SSD1680
default 35 if LV_TFT_DISPLAY_CONTROLLER_IL3820 || LV_TFT_DISPLAY_CONTROLLER_JD79653A || LV_TFT_DISPLAY_CONTROLLER_UC8151D default 35 if LV_TFT_DISPLAY_CONTROLLER_IL3820 || LV_TFT_DISPLAY_CONTROLLER_JD79653A || LV_TFT_DISPLAY_CONTROLLER_UC8151D || LV_TFT_DISPLAY_CONTROLLER_SSD1680
default 35 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 default 35 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2
default 21 if IDF_TARGET_ESP32C3 default 21 if IDF_TARGET_ESP32C3

View file

@ -33,6 +33,8 @@ void *disp_driver_init(void)
FT81x_init(); FT81x_init();
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820 #elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820
il3820_init(); il3820_init();
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1680
ssd1680_init();
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_RA8875 #elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_RA8875
ra8875_init(); ra8875_init();
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_GC9A01 #elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_GC9A01
@ -99,6 +101,8 @@ void disp_driver_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t *
FT81x_flush(drv, area, color_map); FT81x_flush(drv, area, color_map);
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820 #elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820
il3820_flush(drv, area, color_map); il3820_flush(drv, area, color_map);
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1680
ssd1680_flush(drv, area, color_map);
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_RA8875 #elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_RA8875
ra8875_flush(drv, area, color_map); ra8875_flush(drv, area, color_map);
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_GC9A01 #elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_GC9A01
@ -122,6 +126,8 @@ void disp_driver_rounder(lv_disp_drv_t * disp_drv, lv_area_t * area)
sh1107_rounder(disp_drv, area); sh1107_rounder(disp_drv, area);
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820 #elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820
il3820_rounder(disp_drv, area); il3820_rounder(disp_drv, area);
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1680
ssd1680_rounder(disp_drv, area);
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_JD79653A #elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_JD79653A
jd79653a_lv_rounder_cb(disp_drv, area); jd79653a_lv_rounder_cb(disp_drv, area);
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D #elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D
@ -140,6 +146,8 @@ void disp_driver_set_px(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_
sh1107_set_px_cb(disp_drv, buf, buf_w, x, y, color, opa); sh1107_set_px_cb(disp_drv, buf, buf_w, x, y, color, opa);
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820 #elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820
il3820_set_px_cb(disp_drv, buf, buf_w, x, y, color, opa); il3820_set_px_cb(disp_drv, buf, buf_w, x, y, color, opa);
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1680
ssd1680_set_px_cb(disp_drv, buf, buf_w, x, y, color, opa);
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_JD79653A #elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_JD79653A
jd79653a_lv_set_fb_cb(disp_drv, buf, buf_w, x, y, color, opa); jd79653a_lv_set_fb_cb(disp_drv, buf, buf_w, x, y, color, opa);
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D #elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D

View file

@ -42,6 +42,8 @@ extern "C" {
#include "FT81x.h" #include "FT81x.h"
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820 #elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820
#include "il3820.h" #include "il3820.h"
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1680
#include "ssd1680.h"
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_RA8875 #elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_RA8875
#include "ra8875.h" #include "ra8875.h"
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_GC9A01 #elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_GC9A01

373
lvgl_tft/ssd1680.c Normal file
View file

@ -0,0 +1,373 @@
/**
@file ssd1680.c
@brief Tested with GoodDisplay GDEY029T94 e-paper 2.9" monochrome display
@version 1.0
@date 2022-09-20
@author Aram Vartanyan, based on the il3820 driver by Juergen Kienhoefer
*/
/*********************
* INCLUDES
*********************/
#include "disp_spi.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "ssd1680.h"
/*********************
* DEFINES
*********************/
#define TAG "SSD1680"
/**
* ssd1680 compatible EPD controller driver.
*/
#define BIT_SET(a,b) ((a) |= (1U<<(b)))
#define BIT_CLEAR(a,b) ((a) &= ~(1U<<(b)))
/* Number of pixels? */
#define SSD1680_PIXEL (LV_HOR_RES_MAX * LV_VER_RES_MAX)
#define EPD_PANEL_NUMOF_COLUMS EPD_PANEL_WIDTH
#define EPD_PANEL_NUMOF_ROWS_PER_PAGE 8
/* Are pages the number of bytes to represent the panel width? in bytes */
#define EPD_PANEL_NUMOF_PAGES (EPD_PANEL_HEIGHT / EPD_PANEL_NUMOF_ROWS_PER_PAGE)
#define SSD1680_PANEL_FIRST_PAGE 0
#define SSD1680_PANEL_LAST_PAGE (EPD_PANEL_NUMOF_PAGES - 1)
#define SSD1680_PANEL_FIRST_GATE 0
#define SSD1680_PANEL_LAST_GATE (EPD_PANEL_NUMOF_COLUMS - 1)
#define SSD1680_PIXELS_PER_BYTE 8
#define EPD_PARTIAL_CNT 5
//uint8_t ssd1680_scan_mode = SSD1680_DATA_ENTRY_XIYDX; //another approach
uint8_t ssd1680_scan_mode = SSD1680_DATA_ENTRY_XIYIY; //as per the il3820 driver
static uint8_t partial_counter = 0;
static uint8_t ssd1680_border_init[] = {0x05}; //init
static uint8_t ssd1680_border_part[] = {0x80}; //partial update
//A2 - 1 Follow LUT; A1:A0 - 01 LUT1;
//static uint8_t ssd1680_border[] = {0x03}; //old LUT3
/* Static functions */
static void ssd1680_update_display(bool isPartial);
static inline void ssd1680_set_window( uint16_t sx, uint16_t ex, uint16_t ys, uint16_t ye);
static inline void ssd1680_set_cursor(uint16_t sx, uint16_t ys);
static inline void ssd1680_waitbusy(int wait_ms);
static inline void ssd1680_hw_reset(void);
static inline void ssd1680_command_mode(void);
static inline void ssd1680_data_mode(void);
static inline void ssd1680_write_cmd(uint8_t cmd, uint8_t *data, size_t len);
static inline void ssd1680_send_cmd(uint8_t cmd);
static inline void ssd1680_send_data(uint8_t *data, uint16_t length);
/* Required by LVGL */
void ssd1680_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
{
/* Each byte holds the data of 8 pixels, linelen is the number of bytes
* we need to cover a line of the display. */
size_t linelen = EPD_PANEL_WIDTH / 8; //SSD1680_COLUMNS = (EPD_PANEL_WIDTH / 8)
uint8_t *buffer = (uint8_t *) color_map;
uint16_t x_addr_counter = 0;
uint16_t y_addr_counter = 0;
/* Set the cursor at the beginning of the graphic RAM */
#if defined (CONFIG_LV_DISPLAY_ORIENTATION_PORTRAIT)
x_addr_counter = EPD_PANEL_WIDTH - 1;
y_addr_counter = EPD_PANEL_HEIGHT - 1;
#endif
ssd1680_init();
if (!partial_counter) {
ESP_LOGD(TAG, "Refreshing in FULL");
ssd1680_send_cmd(SSD1680_CMD_WRITE1_RAM);
for(size_t row = 0; row <= (EPD_PANEL_HEIGHT - 1); row++){
ssd1680_send_data(buffer, linelen);
buffer += SSD1680_COLUMNS;
}
ssd1680_send_cmd(SSD1680_CMD_WRITE2_RAM);
for(size_t row = 0; row <= (EPD_PANEL_HEIGHT - 1); row++){
ssd1680_send_data(buffer, linelen);
buffer += SSD1680_COLUMNS;
}
ssd1680_update_display(false);
partial_counter = EPD_PARTIAL_CNT;
} else {
//update partial
ssd1680_hw_reset();
ssd1680_write_cmd(SSD1680_CMD_BWF_CTRL, ssd1680_border_part, 1);
ssd1680_set_window(area->x1, area->x2, area->y1, area->y2);
ssd1680_set_cursor(x_addr_counter, y_addr_counter);
ssd1680_send_cmd(SSD1680_CMD_WRITE1_RAM);
for(size_t row = 0; row <= (EPD_PANEL_HEIGHT - 1); row++) {
ssd1680_send_data(buffer, linelen);
buffer += SSD1680_COLUMNS; //(128/8)x296 = 4736
}
ssd1680_update_display(true);
partial_counter--;
}
ssd1680_deep_sleep();
/* IMPORTANT!!!
* Inform the graphics library that you are ready with the flushing */
lv_disp_flush_ready(drv);
}
/* Specify the start/end positions of the window address in the X and Y
* directions by an address unit.
*
* @param sx: X Start position.
* @param ex: X End position.
* @param ys: Y Start position.
* @param ye: Y End position.
*/
static inline void ssd1680_set_window( uint16_t sx, uint16_t ex, uint16_t ys, uint16_t ye)
{
uint8_t tmp[4] = {0};
tmp[0] = sx / 8;
tmp[1] = ex / 8; //0x0F-->(15+1)*8=128 -> ex = EPD_PANEL_WIDTH
//tmp[1] = (ex / 8) - 1;
/* Set X address start/end */
ssd1680_write_cmd(SSD1680_CMD_RAM_XPOS_CTRL, tmp, 2);
//0x0127-->(295+1)=296
//a % b = a - (a/b)*b
tmp[0] = ys % 256;
tmp[1] = ys / 256;
tmp[2] = ye % 256;
tmp[3] = ye / 256;
/* Set Y address start/end */
ssd1680_write_cmd(SSD1680_CMD_RAM_YPOS_CTRL, tmp, 4);
}
/* Make initial settings for the RAM X and Y addresses in the address counter
* (AC).
*
* @param sx: RAM X address counter.
* @param ys: RAM Y address counter.
*/
static inline void ssd1680_set_cursor(uint16_t sx, uint16_t ys)
{
uint8_t tmp[2] = {0};
tmp[0] = sx / 8;
ssd1680_write_cmd(SSD1680_CMD_RAM_XPOS_CNTR, tmp, 1);
tmp[0] = ys % 256;
tmp[1] = ys / 256;
ssd1680_write_cmd(SSD1680_CMD_RAM_YPOS_CNTR, tmp, 2);
}
/* After sending the RAM content we need to send the commands:
* - Display Update Control 2
* - Master Activation
*/
static void ssd1680_update_display(bool isPartial)
{
uint8_t tmp = 0;
if(isPartial) {
tmp = 0xFF; //Display mode 2 - Partial update
} else {
tmp = 0xF7; //Display mode 1 - Full update
}
/* Display Update Control */
ssd1680_write_cmd(SSD1680_CMD_UPDATE_CTRL2, &tmp, 1);
/* Activate Display Update Sequence */
ssd1680_write_cmd(SSD1680_CMD_MASTER_ACTIVATION, NULL, 0);
/* Poll BUSY signal. */
ssd1680_waitbusy(SSD1680_WAIT);
}
/* Rotate the display by "software" when using PORTRAIT orientation.
* BIT_SET(byte_index, bit_index) clears the bit_index pixel at byte_index of
* the display buffer.
* BIT_CLEAR(byte_index, bit_index) sets the bit_index pixel at the byte_index
* of the display buffer. */
void ssd1680_set_px_cb(lv_disp_drv_t * disp_drv, uint8_t* buf,
lv_coord_t buf_w, lv_coord_t x, lv_coord_t y,
lv_color_t color, lv_opa_t opa)
{
uint16_t byte_index = 0;
uint8_t bit_index = 0;
#if defined (CONFIG_LV_DISPLAY_ORIENTATION_PORTRAIT)
//This part doesn't work for now. Display prints random dots.
byte_index = x + ((y >> 3) * EPD_PANEL_HEIGHT);
bit_index = y & 0x7;
if (color.full) {
BIT_SET(buf[byte_index], 7 - bit_index);
} else {
uint16_t mirrored_idx = (EPD_PANEL_HEIGHT - x) + ((y >> 3) * EPD_PANEL_HEIGHT);
BIT_CLEAR(buf[mirrored_idx], 7 - bit_index);
}
#elif defined (CONFIG_LV_DISPLAY_ORIENTATION_LANDSCAPE)
byte_index = y + ((x >> 3) * EPD_PANEL_HEIGHT);
bit_index = x & 0x7;
if (color.full) {
BIT_SET(buf[byte_index], 7 - bit_index);
} else {
BIT_CLEAR(buf[byte_index], 7 - bit_index);
}
#else
#error "Unsupported orientation used"
#endif
}
/* Required by LVGL */
void ssd1680_rounder(lv_disp_drv_t * disp_drv, lv_area_t *area)
{
area->x1 = area->x1 & ~(0x7);
area->x2 = area->x2 | (0x7);
/* Update the areas as needed.
* For example it makes the area to start only on 8th rows and have Nx8 pixel height.*/
}
/* main initialization routine */
void ssd1680_init(void)
{
uint8_t tmp[3] = {0};
/* Initialize non-SPI GPIOs */
gpio_pad_select_gpio(SSD1680_DC_PIN);
gpio_set_direction(SSD1680_DC_PIN, GPIO_MODE_OUTPUT);
gpio_pad_select_gpio(SSD1680_BUSY_PIN);
gpio_set_direction(SSD1680_BUSY_PIN, GPIO_MODE_INPUT);
#if SSD1680_USE_RST
gpio_pad_select_gpio(SSD1680_RST_PIN);
gpio_set_direction(SSD1680_RST_PIN, GPIO_MODE_OUTPUT);
/* Harware reset */
ssd1680_hw_reset();
#endif
/* Busy wait for the BUSY signal to go low */
ssd1680_waitbusy(SSD1680_WAIT);
/* Software reset */
ssd1680_write_cmd(SSD1680_CMD_SW_RESET, NULL, 0);
ssd1680_waitbusy(SSD1680_WAIT);
/* Driver output control */
tmp[0] = (EPD_PANEL_HEIGHT - 1) & 0xFF; //0x27
tmp[1] = (EPD_PANEL_HEIGHT >> 8 ); //0x01
tmp[2] = 0x00; // GD = 0; SM = 0; TB = 0; //0x00
ssd1680_write_cmd(SSD1680_CMD_GDO_CTRL, tmp, 3);
/* Configure entry mode */
ssd1680_write_cmd(SSD1680_CMD_ENTRY_MODE, &ssd1680_scan_mode, 1);
/* Configure the window */
ssd1680_set_window(0, EPD_PANEL_WIDTH - 1, 0, EPD_PANEL_HEIGHT - 1);
/* Select border waveform for VBD */
ssd1680_write_cmd(SSD1680_CMD_BWF_CTRL, ssd1680_border_init, 1);
/* Display update control 1 */
tmp[0] = 0x00; //A7:0 Normal RAM content option
tmp[1] = 0x80; //B7 Source Output Mode: Available source from S8 to S167
tmp[2] = 0x00; //do not send
ssd1680_write_cmd(SSD1680_CMD_UPDATE_CTRL1, tmp, 2);
/* Read Build-in Temperature sensor */
tmp[0] = 0x80; //A7:0 Internal sensor
tmp[1] = 0x00; //do not send
ssd1680_write_cmd(SSD1680_CMD_READ_INT_TEMP, tmp, 1);
/*set RAM x (0) and y (295) address count */
ssd1680_set_cursor(0, EPD_PANEL_HEIGHT - 1);
ssd1680_waitbusy(SSD1680_WAIT);
}
/* Enter deep sleep mode */
void ssd1680_deep_sleep(void)
{
uint8_t data[] = {0x01};
/* Wait for the BUSY signal to go low */
ssd1680_waitbusy(SSD1680_WAIT);
ssd1680_write_cmd(SSD1680_CMD_SLEEP_MODE1, data, 1);
vTaskDelay(100 / portTICK_RATE_MS); // 100ms delay
}
static inline void ssd1680_waitbusy(int wait_ms)
{
int i = 0;
vTaskDelay(10 / portTICK_RATE_MS); // 10ms delay
for(i = 0; i < (wait_ms * 10); i++) {
if(gpio_get_level(SSD1680_BUSY_PIN) != SSD1680_BUSY_LEVEL) {
return;
}
vTaskDelay(10 / portTICK_RATE_MS);
}
ESP_LOGE( TAG, "busy exceeded %dms", i*10 );
}
/* Set HWRESET */
static inline void ssd1680_hw_reset(void)
{
gpio_set_level(SSD1680_RST_PIN, 0);
vTaskDelay(SSD1680_RESET_DELAY / portTICK_RATE_MS);
gpio_set_level(SSD1680_RST_PIN, 1);
vTaskDelay(SSD1680_RESET_DELAY / portTICK_RATE_MS);
}
/* Set DC signal to command mode */
static inline void ssd1680_command_mode(void)
{
gpio_set_level(SSD1680_DC_PIN, 0);
}
/* Set DC signal to data mode */
static inline void ssd1680_data_mode(void)
{
gpio_set_level(SSD1680_DC_PIN, 1);
}
static inline void ssd1680_write_cmd(uint8_t cmd, uint8_t *data, size_t len)
{
disp_wait_for_pending_transactions();
ssd1680_command_mode();
disp_spi_send_data(&cmd, 1);
if (data != NULL) {
ssd1680_data_mode();
disp_spi_send_data(data, len);
}
}
/* Send cmd to the display */
static inline void ssd1680_send_cmd(uint8_t cmd)
{
disp_wait_for_pending_transactions();
ssd1680_command_mode();
disp_spi_send_data(&cmd, 1);
}
/* Send length bytes of data to the display */
static inline void ssd1680_send_data(uint8_t *data, uint16_t length)
{
disp_wait_for_pending_transactions();
ssd1680_data_mode();
disp_spi_send_colors(data, length);
}

114
lvgl_tft/ssd1680.h Normal file
View file

@ -0,0 +1,114 @@
/**
* @file ssd1680.h
*
*/
#ifndef SSD1680_H
#define SSD1680_H
#ifdef __cplusplus
extern "C"
{
#endif
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
#include "lvgl.h"
#else
#include "lvgl/lvgl.h"
#endif
#include "sdkconfig.h"
#define EPD_PANEL_WIDTH LV_HOR_RES_MAX /* 128 */
#define EPD_PANEL_HEIGHT LV_VER_RES_MAX /* 296 */
/* 128 = panel width */
#define SSD1680_COLUMNS (EPD_PANEL_WIDTH / 8)
#define SSD1680_DC_PIN CONFIG_LV_DISP_PIN_DC
#define SSD1680_RST_PIN CONFIG_LV_DISP_PIN_RST
#define SSD1680_USE_RST CONFIG_LV_DISP_USE_RST
#define SSD1680_BUSY_PIN CONFIG_LV_DISP_PIN_BUSY
#define SSD1680_BUSY_LEVEL 1 //chip is busy if the pin level is high
/* SSD1680 commands */
#define SSD1680_CMD_GDO_CTRL 0x01 //Driver output control
#define SSD1680_CMD_GDV_CTRL 0x03
#define SSD1680_CMD_SDV_CTRL 0x04
#define SSD1680_CMD_SOFTSTART 0x0c
#define SSD1680_CMD_GSCAN_START 0x0f
#define SSD1680_CMD_SLEEP_MODE1 0x10 //enter deep sleep 1 - retain RAM
#define SSD1680_CMD_SLEEP_MODE2 0x11 //enter deep sleep 2 - cannot retain RAM
//After entering Deep sleep Mode, BUSY will stay high.
//To Exit Deep Sleep a HWRESET should be sent.
#define SSD1680_CMD_ENTRY_MODE 0x11 //Data entry mode
#define SSD1680_CMD_SW_RESET 0x12 //SWRESET
#define SSD1680_CMD_TSENS_CTRL 0x1a
#define SSD1680_CMD_READ_INT_TEMP 0x18 //Read built-in temperature sensor
#define SSD1680_CMD_MASTER_ACTIVATION 0x20 //Activate Display Update Sequence
#define SSD1680_CMD_UPDATE_CTRL1 0x21 //Display update control
#define SSD1680_CMD_UPDATE_CTRL2 0x22 //Display Update Control
#define SSD1680_CMD_WRITE1_RAM 0x24 //Write Black (0) and Wite (1) image to RAM
#define SSD1680_CMD_WRITE2_RAM 0x26 //Write RED (1) and NonRED (0) image to RAM
#define SSD1680_CMD_VCOM_SENSE 0x28
#define SSD1680_CMD_VCOM_SENSE_DURATON 0x29
#define SSD1680_CMD_PRGM_VCOM_OTP 0x2a
#define SSD1680_CMD_VCOM_VOLTAGE 0x2c
#define SSD1680_CMD_PRGM_WS_OTP 0x30
#define SSD1680_CMD_UPDATE_LUT 0x32
#define SSD1680_CMD_PRGM_OTP_SELECTION 0x36
#define SSD1680_CMD_WRITE_DISPL_OPT 0x37
#define SSD1680_CMD_BWF_CTRL 0x3c //BorderWavefrom
#define SSD1680_CMD_RAM_XPOS_CTRL 0x44 //set Ram-X address start/end position
#define SSD1680_CMD_RAM_YPOS_CTRL 0x45 //set Ram-Y address start/end position
#define SSD1680_CMD_RAM_XPOS_CNTR 0x4e //set RAM x address count to 0;
#define SSD1680_CMD_RAM_YPOS_CNTR 0x4f //set RAM y address count to 0X199;
/* Data entry sequence modes */
#define SSD1680_DATA_ENTRY_MASK 0x07
#define SSD1680_DATA_ENTRY_XDYDX 0x00
#define SSD1680_DATA_ENTRY_XIYDX 0x01
#define SSD1680_DATA_ENTRY_XDYIX 0x02
#define SSD1680_DATA_ENTRY_XIYIX 0x03
#define SSD1680_DATA_ENTRY_XDYDY 0x04
#define SSD1680_DATA_ENTRY_XIYDY 0x05
#define SSD1680_DATA_ENTRY_XDYIY 0x06
#define SSD1680_DATA_ENTRY_XIYIY 0x07
/* Options for display update */
#define SSD1680_CTRL1_INITIAL_UPDATE_LL 0x00
#define SSD1680_CTRL1_INITIAL_UPDATE_LH 0x01
#define SSD1680_CTRL1_INITIAL_UPDATE_HL 0x02
#define SSD1680_CTRL1_INITIAL_UPDATE_HH 0x03
/* Options for display update sequence */
#define SSD1680_CTRL2_ENABLE_CLK 0x80
#define SSD1680_CTRL2_ENABLE_ANALOG 0x40
#define SSD1680_CTRL2_TO_INITIAL 0x08
#define SSD1680_CTRL2_TO_PATTERN 0x04
#define SSD1680_CTRL2_DISABLE_ANALOG 0x02
#define SSD1680_CTRL2_DISABLE_CLK 0x01
#define SSD1680_SLEEP_MODE_DSM 0x01
#define SSD1680_SLEEP_MODE_PON 0x00
/* time constants in ms */
#define SSD1680_RESET_DELAY 20 //At least 10ms delay
#define SSD1680_BUSY_DELAY 1
// normal wait time max 20 times x 10ms
#define SSD1680_WAIT 20
void ssd1680_init(void);
void ssd1680_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map);
void ssd1680_rounder(lv_disp_drv_t * disp_drv, lv_area_t *area);
void ssd1680_set_px_cb(lv_disp_drv_t * disp_drv, uint8_t* buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa);
void ssd1680_deep_sleep(void);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* __SSD1680_REGS_H__ */