diff --git a/CMakeLists.txt b/CMakeLists.txt index 78870dd..407802a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,8 @@ elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_GC9A01) list(APPEND SOURCES "lvgl_tft/GC9A01.c") elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9163C) list(APPEND SOURCES "lvgl_tft/ili9163c.c") +elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_PCD8544) + list(APPEND SOURCES "lvgl_tft/pcd8544.c") else() message(WARNING "LVGL ESP32 drivers: Display controller not defined.") endif() diff --git a/lvgl_helpers.h b/lvgl_helpers.h index 5fd6f09..c010174 100644 --- a/lvgl_helpers.h +++ b/lvgl_helpers.h @@ -76,6 +76,8 @@ extern "C" { #define DISP_BUF_SIZE ((LV_VER_RES_MAX * LV_VER_RES_MAX) / 8) // 2888 bytes #elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9163C #define DISP_BUF_SIZE (LV_HOR_RES_MAX * 40) +#elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_PCD8544) +#define DISP_BUF_SIZE (LV_HOR_RES_MAX * (LV_VER_RES_MAX / 8)) #else #error "No display controller selected" #endif diff --git a/lvgl_spi_conf.h b/lvgl_spi_conf.h index 240090d..2c62c94 100644 --- a/lvgl_spi_conf.h +++ b/lvgl_spi_conf.h @@ -166,6 +166,8 @@ extern "C" { #define SPI_TFT_CLOCK_SPEED_HZ (40 * 1000 * 1000) #elif defined(CONFIG_LV_TFT_DISPLAY_CONTROLLER_FT81X) #define SPI_TFT_CLOCK_SPEED_HZ (32*1000*1000) +#elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_PCD8544) +#define SPI_TFT_CLOCK_SPEED_HZ (4*1000*1000) #else #define SPI_TFT_CLOCK_SPEED_HZ (40*1000*1000) #endif diff --git a/lvgl_tft/Kconfig b/lvgl_tft/Kconfig index 140c173..b700894 100644 --- a/lvgl_tft/Kconfig +++ b/lvgl_tft/Kconfig @@ -174,6 +174,10 @@ menu "LVGL TFT Display controller" help ILI9163C display controller. + config LV_TFT_DISPLAY_CONTROLLER_PCD8544 + bool + help + PCD8544 display controller (Nokia 3110/5110) # Display controller communication protocol # # This symbols define the communication protocol used by the @@ -340,6 +344,11 @@ menu "LVGL TFT Display controller" bool "ILI9163C" select LV_TFT_DISPLAY_CONTROLLER_ILI9163C select LV_TFT_DISPLAY_PROTOCOL_SPI + config LV_TFT_DISPLAY_USER_CONTROLLER_PCD8544 + bool "PCD8544" + select LV_TFT_DISPLAY_CONTROLLER_PCD8544 + select LV_TFT_DISPLAY_PROTOCOL_SPI + select LV_TFT_DISPLAY_MONOCHROME endchoice config CUSTOM_DISPLAY_BUFFER_SIZE diff --git a/lvgl_tft/disp_driver.c b/lvgl_tft/disp_driver.c index f62a947..153ca31 100644 --- a/lvgl_tft/disp_driver.c +++ b/lvgl_tft/disp_driver.c @@ -43,6 +43,8 @@ void *disp_driver_init(void) uc8151d_init(); #elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9163C ili9163c_init(); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_PCD8544 + pcd8544_init(); #endif // We still use menuconfig for these settings @@ -107,6 +109,8 @@ void disp_driver_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * uc8151d_lv_fb_flush(drv, area, color_map); #elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9163C ili9163c_flush(drv, area, color_map); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_PCD8544 + pcd8544_flush(drv, area, color_map); #endif } @@ -122,6 +126,8 @@ void disp_driver_rounder(lv_disp_drv_t * disp_drv, lv_area_t * area) jd79653a_lv_rounder_cb(disp_drv, area); #elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D uc8151d_lv_rounder_cb(disp_drv, area); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_PCD8544 + pcd8544_rounder(disp_drv, area); #endif } @@ -138,5 +144,7 @@ void disp_driver_set_px(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_ jd79653a_lv_set_fb_cb(disp_drv, buf, buf_w, x, y, color, opa); #elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D uc8151d_lv_set_fb_cb(disp_drv, buf, buf_w, x, y, color, opa); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_PCD8544 + pcd8544_set_px_cb(disp_drv, buf, buf_w, x, y, color, opa); #endif } diff --git a/lvgl_tft/disp_driver.h b/lvgl_tft/disp_driver.h index 8c70fda..2f5bcdf 100644 --- a/lvgl_tft/disp_driver.h +++ b/lvgl_tft/disp_driver.h @@ -52,6 +52,8 @@ extern "C" { #include "uc8151d.h" #elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9163C #include "ili9163c.h" +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_PCD8544 +#include "pcd8544.h" #endif /********************* diff --git a/lvgl_tft/pcd8544.c b/lvgl_tft/pcd8544.c new file mode 100644 index 0000000..b2a7bed --- /dev/null +++ b/lvgl_tft/pcd8544.c @@ -0,0 +1,146 @@ +/** + * @file pcd8544.c + * + * Roughly based on: + * https://github.com/adafruit/Adafruit-PCD8544-Nokia-5110-LCD-library + * https://github.com/olikraus/u8g2 + */ + +#include "disp_spi.h" +#include "driver/gpio.h" + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "pcd8544.h" + +#define TAG "lv_pcd8544" + +/********************** + * MACROS + **********************/ + +#define BIT_SET(a,b) ((a) |= (1U<<(b))) +#define BIT_CLEAR(a,b) ((a) &= ~(1U<<(b))) + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void pcd8544_send_cmd(uint8_t cmd) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(PCD8544_DC, 0); /*Command mode*/ + disp_spi_send_data(&cmd, 1); +} + +static void pcd8544_send_data(void * data, uint16_t length) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(PCD8544_DC, 1); /*Data mode*/ + disp_spi_send_data(data, length); +} + +static void pcd8544_send_colors(void * data, uint16_t length) +{ + gpio_set_level(PCD8544_DC, 1); /*Data mode*/ + disp_spi_send_colors(data, length); +} + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void pcd8544_init(void){ + + // TODO: orientation + + // Initialize non-SPI GPIOs + gpio_pad_select_gpio(PCD8544_DC); + gpio_set_direction(PCD8544_DC, GPIO_MODE_OUTPUT); + gpio_pad_select_gpio(PCD8544_RST); + gpio_set_direction(PCD8544_RST, GPIO_MODE_OUTPUT); + + // Reset the display + gpio_set_level(PCD8544_RST, 0); + vTaskDelay(100 / portTICK_RATE_MS); + gpio_set_level(PCD8544_RST, 1); + vTaskDelay(100 / portTICK_RATE_MS); + + pcd8544_send_cmd(0x21); /* activate chip (PD=0), horizontal increment (V=0), enter extended command set (H=1) */ + pcd8544_send_cmd(0x06); /* temp. control: b10 = 2 */ + pcd8544_send_cmd(0x13); /* bias system 1:48 */ + pcd8544_send_cmd(0xc0); /* medium Vop = Contrast 0x40 = 64 */ + + pcd8544_send_cmd(0x20); /* activate chip (PD=0), horizontal increment (V=0), enter extended command set (H=0) */ + pcd8544_send_cmd(0x0c); /* display mode normal */ +} + +void pcd8544_set_contrast (uint8_t contrast){ + if (contrast > 0x7f){ + contrast = 0x7f; + } + pcd8544_send_cmd(0x21); /* activate chip (PD=0), horizontal increment (V=0), enter extended command set (H=1) */ + pcd8544_send_cmd(0x80 | contrast); /* medium Vop = Contrast */ +} + +void pcd8544_rounder(lv_disp_drv_t * disp_drv, lv_area_t *area){ + uint8_t hor_max = disp_drv->hor_res; + uint8_t ver_max = disp_drv->ver_res; + + area->x1 = 0; + area->y1 = 0; + area->x2 = hor_max - 1; + area->y2 = ver_max - 1; +} + +void pcd8544_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){ + + uint8_t set = (color.full == 0) && (LV_OPA_TRANSP != opa); + + uint16_t byte_index = x + (( y>>3 ) * buf_w); + uint8_t bit_index = y & 0x7; + + if (set) { + BIT_SET(buf[byte_index], bit_index); + } else { + BIT_CLEAR(buf[byte_index], bit_index); + } +} + +void pcd8544_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_map){ + + pcd8544_send_cmd(0x20); /* activate chip (PD=0), horizontal increment (V=0), enter extended command set (H=0) */ + + uint8_t * buf = (uint8_t *) color_map; + + if ((area->x1 == 0) && (area->y1 == 0) && (area->x2 == (disp_drv->hor_res - 1)) && (area->y2 == (disp_drv->ver_res - 1))){ + + // optimize flush of complete frame buffer in a single SPI transaction + + pcd8544_send_cmd(0x40); /* set Y address */ + pcd8544_send_cmd(0x80); /* set X address */ + pcd8544_send_colors(buf, disp_drv->hor_res * disp_drv->ver_res / 8); + + } else { + + // send horizontal tiles + + uint16_t bank_start = area->y1 / 8; + uint16_t bank_end = area->y2 / 8; + + uint16_t bank; + uint16_t cols_to_update = area->x2 - area->x1 + 1; + for (bank = bank_start ; bank <= bank_end ; bank++ ){ + pcd8544_send_cmd(0x40 | bank ); /* set Y address */ + pcd8544_send_cmd(0x80 | area->x1 ); /* set X address */ + uint16_t offset = bank * disp_drv->hor_res + area->x1; + pcd8544_send_data(&buf[offset], cols_to_update); + } + + lv_disp_flush_ready(disp_drv); + + } +} diff --git a/lvgl_tft/pcd8544.h b/lvgl_tft/pcd8544.h new file mode 100644 index 0000000..b9357fe --- /dev/null +++ b/lvgl_tft/pcd8544.h @@ -0,0 +1,57 @@ + +/** + * @file pcd8544.h + * + */ + +#ifndef PCD8544_H +#define PCD8544_H + +#ifdef __cplusplus +extern "C" { +#endif + + /********************* + * INCLUDES + *********************/ +#include + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + + /********************* + * DEFINES + *********************/ + +#define PCD8544_DC CONFIG_LV_DISP_PIN_DC +#define PCD8544_RST CONFIG_LV_DISP_PIN_RST +#define PCD8544_BCKL CONFIG_LV_DISP_PIN_BCKL + + /********************** + * TYPEDEFS + **********************/ + + /********************** + * GLOBAL PROTOTYPES + **********************/ + + void pcd8544_init(void); + void pcd8544_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map); + void pcd8544_rounder(lv_disp_drv_t * disp_drv, lv_area_t *area); + void pcd8544_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 pcd8544_set_contrast(uint8_t contrast); + + /********************** + * MACROS + **********************/ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*PCD8544_H*/