17eb416ef8
* [lvgl_helpers] Cleanup and misc code cleanup Checks for SPI_HOST_MAX symbol before using it. Rename lvgl_driver_init to lvgl_interface_init because it now only initialize the interface bus for display drivers, we still need to remove the indev drivers from here. Use types defined in spi_types.h for spi host (spi_host_device_t) and spi dma channels (spi_dma_chan_t). Also add a couple of symbols to avoid using magic numbers * [lvgl_helpers] Reduce usage of if defined in lvgl_interface_init * [lvgl_helpers] Fix spi dma channel for ESP-IDF versions <= 4.2 * [examples] Update hello_world to call lvgl_interface_init * Add lvgl_get_display_buffer_size helper This helper will allow us to get the calculated display buffer size instead of using a global symbol. * Implement lvgl_get_display_buffer_size This API will be used to get the calculation of display buffer size. * Delete DISP_BUF_SIZE symbols The same functionality is handled by lvgl_get_display_buffer_size * Move SPI max transfer size calculation to helper Use calculate_spi_max_transfer_size to calculate the SPI max transfer size for the SPI master configuration * Remove SPI_BUS_MAX_TRANSFER_SZ definition Same functionality is now handled in calculate_spi_max_transfer_size * Update display buffer size calculation Use lvgl_get_display_buffer_size helper instead of DISP_BUF_SIZE symbol * Update example to LVGL v8 Add comments about changes from: - LVGL v7 to LVGL v8 - Configuration helpers and display drivers * Update lvgl_helpers.c * Update sh1107 driver * Update EVE driver Check for symbols used in previous implementations before trying to use them and add a fallback temporary implementation when not found. The falback implementation isn't tested with hardware. Symbols: - DISP_BUF_SIZE - SPI_TRANSFER_SIZE * Update uc8151d driver * Update jd79653a driver * Update ra8875 driver * Update il3820.h Check for LV_HOR_RES_MAX and LV_VER_RES_MAX before trying to use them * Update lvgl_helpers.c Check for ESP-IDF version before trying to use spi_dma_chan_t type
261 lines
7.5 KiB
C
261 lines
7.5 KiB
C
/**
|
|
@file uc8151d.c
|
|
@brief GoodDisplay GDEW0154M10 DES e-paper display w/ UltraChip UC8151D
|
|
@version 1.0
|
|
@date 2020-08-28
|
|
@author Jackson Ming Hu <huming2207@gmail.com>
|
|
|
|
|
|
@section LICENSE
|
|
|
|
MIT License
|
|
|
|
Copyright (c) 2020 Jackson Ming Hu
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
|
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
|
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
|
|
#include <freertos/FreeRTOS.h>
|
|
#include <freertos/task.h>
|
|
#include <freertos/event_groups.h>
|
|
#include <driver/gpio.h>
|
|
|
|
#include "disp_spi.h"
|
|
#include "disp_driver.h"
|
|
#include "uc8151d.h"
|
|
|
|
#define PIN_DC CONFIG_LV_DISP_PIN_DC
|
|
#define PIN_DC_BIT ((1ULL << (uint8_t)(CONFIG_LV_DISP_PIN_DC)))
|
|
|
|
#if defined CONFIG_LV_DISP_PIN_RST
|
|
#define PIN_RST CONFIG_LV_DISP_PIN_RST
|
|
#define PIN_RST_BIT ((1ULL << (uint8_t)(CONFIG_LV_DISP_PIN_RST)))
|
|
#endif
|
|
|
|
#define PIN_BUSY CONFIG_LV_DISP_PIN_BUSY
|
|
#define PIN_BUSY_BIT ((1ULL << (uint8_t)(CONFIG_LV_DISP_PIN_BUSY)))
|
|
#define EVT_BUSY (1UL << 0UL)
|
|
|
|
#if defined (LV_HOR_RES_MAX)
|
|
#define EPD_WIDTH LV_HOR_RES_MAX
|
|
#else
|
|
/* ToDo Fix, 256 is just a magic number */
|
|
#define EPD_WIDTH 256u
|
|
#endif
|
|
|
|
#if defined (LV_VER_RES_MAX)
|
|
#define EPD_HEIGHT LV_VER_RES_MAX
|
|
#else
|
|
/* ToDo Fix, 128 is just a magic number */
|
|
#define EPD_HEIGHT 128u
|
|
#endif
|
|
|
|
#define EPD_ROW_LEN (EPD_HEIGHT / 8u)
|
|
|
|
#define BIT_SET(a, b) ((a) |= (1U << (b)))
|
|
#define BIT_CLEAR(a, b) ((a) &= ~(1U << (b)))
|
|
|
|
typedef struct
|
|
{
|
|
uint8_t cmd;
|
|
uint8_t data[3];
|
|
size_t len;
|
|
} uc8151d_seq_t;
|
|
|
|
#define EPD_SEQ_LEN(x) ((sizeof(x) / sizeof(uc8151d_seq_t)))
|
|
|
|
static EventGroupHandle_t uc8151d_evts = NULL;
|
|
|
|
static void IRAM_ATTR uc8151d_busy_intr(void *arg)
|
|
{
|
|
BaseType_t xResult;
|
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
|
xResult = xEventGroupSetBitsFromISR(uc8151d_evts, EVT_BUSY, &xHigherPriorityTaskWoken);
|
|
if (xResult == pdPASS) {
|
|
portYIELD_FROM_ISR();
|
|
}
|
|
}
|
|
|
|
static void uc8151d_spi_send_cmd(uint8_t cmd)
|
|
{
|
|
disp_wait_for_pending_transactions();
|
|
gpio_set_level(PIN_DC, 0); // DC = 0 for command
|
|
disp_spi_send_data(&cmd, 1);
|
|
}
|
|
|
|
static void uc8151d_spi_send_data(uint8_t *data, size_t len)
|
|
{
|
|
disp_wait_for_pending_transactions();
|
|
gpio_set_level(PIN_DC, 1); // DC = 1 for data
|
|
disp_spi_send_data(data, len);
|
|
}
|
|
|
|
static void uc8151d_spi_send_data_byte(uint8_t data)
|
|
{
|
|
disp_wait_for_pending_transactions();
|
|
gpio_set_level(PIN_DC, 1); // DC = 1 for data
|
|
disp_spi_send_data(&data, 1);
|
|
}
|
|
|
|
static esp_err_t uc8151d_wait_busy(uint32_t timeout_ms)
|
|
{
|
|
uint32_t wait_ticks = (timeout_ms == 0 ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms));
|
|
EventBits_t bits = xEventGroupWaitBits(uc8151d_evts,
|
|
EVT_BUSY, // Wait for busy bit
|
|
pdTRUE, pdTRUE, // Clear on exit, wait for all
|
|
wait_ticks); // Timeout
|
|
|
|
return ((bits & EVT_BUSY) != 0) ? ESP_OK : ESP_ERR_TIMEOUT;
|
|
}
|
|
|
|
static void uc8151d_sleep(void)
|
|
{
|
|
// Set VCOM to 0xf7
|
|
uc8151d_spi_send_cmd(0x50);
|
|
uc8151d_spi_send_data_byte(0xf7);
|
|
|
|
// Power off
|
|
uc8151d_spi_send_cmd(0x02);
|
|
uc8151d_wait_busy(0);
|
|
|
|
// Go to sleep
|
|
uc8151d_spi_send_cmd(0x07);
|
|
uc8151d_spi_send_data_byte(0xa5);
|
|
}
|
|
|
|
static void uc8151d_reset(void);
|
|
|
|
static void uc8151d_panel_init(void)
|
|
{
|
|
// Hardware reset for 3 times - not sure why but it's from official demo code
|
|
for (uint8_t cnt = 0; cnt < 3; cnt++) {
|
|
uc8151d_reset();
|
|
}
|
|
|
|
// Power up
|
|
uc8151d_spi_send_cmd(0x04);
|
|
uc8151d_wait_busy(0);
|
|
|
|
// Panel settings
|
|
uc8151d_spi_send_cmd(0x00);
|
|
#if defined (CONFIG_LV_DISPLAY_ORIENTATION_PORTRAIT_INVERTED)
|
|
uc8151d_spi_send_data_byte(0x13);
|
|
#elif defined (CONFIG_LV_DISPLAY_ORIENTATION_PORTRAIT)
|
|
uc8151d_spi_send_data_byte(0x1f);
|
|
#endif
|
|
|
|
// VCOM & Data intervals
|
|
uc8151d_spi_send_cmd(0x50);
|
|
uc8151d_spi_send_data_byte(0x97);
|
|
}
|
|
|
|
static void uc8151d_full_update(uint8_t *buf)
|
|
{
|
|
uc8151d_panel_init();
|
|
|
|
uint8_t *buf_ptr = buf;
|
|
uint8_t old_data[EPD_ROW_LEN] = { 0 };
|
|
|
|
// Fill old data
|
|
uc8151d_spi_send_cmd(0x10);
|
|
for (size_t h_idx = 0; h_idx < EPD_HEIGHT; h_idx++) {
|
|
uc8151d_spi_send_data(old_data, EPD_ROW_LEN);
|
|
}
|
|
|
|
// Fill new data
|
|
uc8151d_spi_send_cmd(0x13);
|
|
for (size_t h_idx = 0; h_idx < EPD_HEIGHT; h_idx++) {
|
|
uc8151d_spi_send_data(buf_ptr, EPD_ROW_LEN);
|
|
buf_ptr += EPD_ROW_LEN;
|
|
}
|
|
|
|
// Issue refresh
|
|
uc8151d_spi_send_cmd(0x12);
|
|
vTaskDelay(pdMS_TO_TICKS(10));
|
|
uc8151d_wait_busy(0);
|
|
|
|
uc8151d_sleep();
|
|
}
|
|
|
|
void uc8151d_lv_fb_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
|
|
{
|
|
LV_LOG_INFO("x1: 0x%x, x2: 0x%x, y1: 0x%x, y2: 0x%x", area->x1, area->x2, area->y1, area->y2);
|
|
LV_LOG_INFO("Writing LVGL fb with len: %u", len);
|
|
|
|
uint8_t *buf = (uint8_t *) color_map;
|
|
uc8151d_full_update(buf);
|
|
|
|
lv_disp_flush_ready(drv);
|
|
LV_LOG_INFO("Ready");
|
|
}
|
|
|
|
void uc8151d_lv_set_fb_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 = (x >> 3u) + (y * EPD_ROW_LEN);
|
|
uint8_t bit_index = x & 0x07u;
|
|
|
|
if (color.full) {
|
|
BIT_SET(buf[byte_index], 7 - bit_index);
|
|
} else {
|
|
LV_LOG_INFO("Clear at x: %u, y: %u", x, y);
|
|
BIT_CLEAR(buf[byte_index], 7 - bit_index);
|
|
}
|
|
}
|
|
|
|
void uc8151d_lv_rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area)
|
|
{
|
|
// Always send full framebuffer if it's not in partial mode
|
|
area->x1 = 0;
|
|
area->y1 = 0;
|
|
area->x2 = EPD_WIDTH - 1;
|
|
area->y2 = EPD_HEIGHT - 1;
|
|
}
|
|
|
|
void uc8151d_init(void)
|
|
{
|
|
// Initialise event group
|
|
uc8151d_evts = xEventGroupCreate();
|
|
if (!uc8151d_evts) {
|
|
LV_LOG_ERROR("Failed when initialising event group!");
|
|
return;
|
|
}
|
|
|
|
// Setup input pin, pull-up, input
|
|
gpio_config_t in_io_conf = {
|
|
.intr_type = GPIO_INTR_POSEDGE,
|
|
.mode = GPIO_MODE_INPUT,
|
|
.pin_bit_mask = PIN_BUSY_BIT,
|
|
.pull_down_en = 0,
|
|
.pull_up_en = 1,
|
|
};
|
|
ESP_ERROR_CHECK(gpio_config(&in_io_conf));
|
|
gpio_install_isr_service(0);
|
|
gpio_isr_handler_add(PIN_BUSY, uc8151d_busy_intr, (void *) PIN_BUSY);
|
|
|
|
LV_LOG_INFO("IO init finished");
|
|
uc8151d_panel_init();
|
|
LV_LOG_INFO("Panel initialised");
|
|
}
|
|
|
|
static void uc8151d_reset(void)
|
|
{
|
|
#if defined CONFIG_LV_DISP_USE_RST
|
|
gpio_set_level(PIN_RST, 0);
|
|
// At least 10ms, leave 20ms for now just in case...
|
|
vTaskDelay(pdMS_TO_TICKS(20));
|
|
gpio_set_level(PIN_RST, 1);
|
|
vTaskDelay(pdMS_TO_TICKS(10));
|
|
#endif
|
|
}
|