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:
parent
26fe6e7703
commit
f4fd82bd7e
|
@ -32,6 +32,8 @@ elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_FT81X)
|
|||
list(APPEND SOURCES "lvgl_tft/FT81x.c")
|
||||
elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820)
|
||||
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)
|
||||
list(APPEND SOURCES "lvgl_tft/jd79653a.c")
|
||||
elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D)
|
||||
|
|
|
@ -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 |
|
||||
| UC8151D/ GoodDisplay GDEW0154M10 DES | 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
|
||||
|
||||
|
|
|
@ -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/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_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_UC8151D),lvgl_tft/uc8151d.o)
|
||||
$(call compile_only_if,$(CONFIG_LV_TFT_DISPLAY_CONTROLLER_RA8875),lvgl_tft/ra8875.o)
|
||||
|
|
|
@ -154,7 +154,7 @@ bool lvgl_spi_driver_init(int host,
|
|||
int dma_channel,
|
||||
int quadwp_pin, int quadhd_pin)
|
||||
{
|
||||
assert((0 <= host) && (SPI_HOST_MAX > host));
|
||||
//assert((0 <= host) && (SPI_HOST_MAX > host));
|
||||
const char *spi_names[] = {
|
||||
"SPI1_HOST", "SPI2_HOST", "SPI3_HOST"
|
||||
};
|
||||
|
|
|
@ -66,6 +66,8 @@ extern "C" {
|
|||
#define DISP_BUF_SIZE (LV_HOR_RES_MAX * DISP_BUF_LINES)
|
||||
#elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820)
|
||||
#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
|
||||
#define DISP_BUF_SIZE (LV_HOR_RES_MAX * 40)
|
||||
#elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_GC9A01)
|
||||
|
|
|
@ -126,6 +126,7 @@ extern "C" {
|
|||
defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_SH1107) || \
|
||||
defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_FT81X) || \
|
||||
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_ILI9163C)
|
||||
|
||||
|
|
|
@ -148,6 +148,14 @@ menu "LVGL TFT Display controller"
|
|||
bool
|
||||
help
|
||||
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
|
||||
bool
|
||||
|
@ -326,6 +334,12 @@ menu "LVGL TFT Display controller"
|
|||
select LV_TFT_DISPLAY_CONTROLLER_IL3820
|
||||
select LV_TFT_DISPLAY_PROTOCOL_SPI
|
||||
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
|
||||
bool "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_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_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
|
||||
|
||||
config LV_TFT_SPI_CLK_DIVIDER_1
|
||||
|
@ -907,8 +921,8 @@ menu "LVGL TFT Display controller"
|
|||
Configure the display Reset pin here.
|
||||
|
||||
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
|
||||
default 35 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 || LV_TFT_DISPLAY_CONTROLLER_SSD1680
|
||||
default 35 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2
|
||||
default 21 if IDF_TARGET_ESP32C3
|
||||
|
||||
|
|
|
@ -33,6 +33,8 @@ void *disp_driver_init(void)
|
|||
FT81x_init();
|
||||
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820
|
||||
il3820_init();
|
||||
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1680
|
||||
ssd1680_init();
|
||||
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_RA8875
|
||||
ra8875_init();
|
||||
#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);
|
||||
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820
|
||||
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
|
||||
ra8875_flush(drv, area, color_map);
|
||||
#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);
|
||||
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820
|
||||
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
|
||||
jd79653a_lv_rounder_cb(disp_drv, area);
|
||||
#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);
|
||||
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820
|
||||
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
|
||||
jd79653a_lv_set_fb_cb(disp_drv, buf, buf_w, x, y, color, opa);
|
||||
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D
|
||||
|
|
|
@ -42,6 +42,8 @@ extern "C" {
|
|||
#include "FT81x.h"
|
||||
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820
|
||||
#include "il3820.h"
|
||||
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1680
|
||||
#include "ssd1680.h"
|
||||
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_RA8875
|
||||
#include "ra8875.h"
|
||||
#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_GC9A01
|
||||
|
|
373
lvgl_tft/ssd1680.c
Normal file
373
lvgl_tft/ssd1680.c
Normal 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
114
lvgl_tft/ssd1680.h
Normal 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__ */
|
Loading…
Reference in a new issue