Below is the full code. Can be a bit messy in places, but I followed some code by the manufacturer and staple stitched stuff to make it actually display. Upon reviewing it again, I realized I had forgotten to include the lv_disp_flush_ready call in the flush_ready_cb, but now it just reboots with the following trace upon shorting the pin:
pressed
Guru Meditation Error: Core 0 panic'ed (StoreProhibited). Exception was unhandled.
Core 0 register dump:
PC : 0x40381b22 PS : 0x00060133 A0 : 0x803814f8 A1 : 0x3fcabc90
A2 : 0x3fcca548 A3 : 0x0001c800 A4 : 0x80376a5c A5 : 0x00000001
A6 : 0x3fc981ec A7 : 0x3fca8cb4 A8 : 0x3fca85c4 A9 : 0xf206f206
A10 : 0x0000000a A11 : 0x0000001e A12 : 0xf206f206 A13 : 0x40000000
A14 : 0x3fcabe50 A15 : 0x0000cdcd SAR : 0x00000007 EXCCAUSE: 0x0000001d
EXCVADDR: 0xf206f212 LBEG : 0x400570e8 LEND : 0x400570f3 LCOUNT : 0x00000000
Backtrace: 0x40381b1f:0x3fcabc90 0x403814f5:0x3fcabcb0 0x40377b8d:0x3fcabcd0 0x40377ca5:0x3fcabcf0 0x40377cd5:0x3fcabd20 0x40377ec0:0x3fcabd40 0x42001ece:0x3fcabd60 0x42041081:0x3fcabd90 0x4200893f:0x3fcabdc0 0x42008ed9:0x3fcabde0 0x4200902f:0x3fcabe50 0x4200913c:0x3fcabe80 0x420092e2:0x3fcabea0 0x4200e959:0x3fcabed0 0x4200ea03:0x3fcabef0 0x42002238:0x3fcabf10 0x42044b2f:0x3fcac010 0x4037bf4d:0x3fcac040
ELF file SHA256: 1f6dbc4aa
Rebooting...
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_sh8601.h"
#include "lvgl.h"
#include "esp_timer.h"
#include "esp_task_wdt.h"
#include "rtc_wdt.h"
#define LCD_HOST SPI2_HOST
#define LCD_H_RES 240
#define LCD_V_RES 240
#define LCD_DMA_LINE (LCD_V_RES / 2)
#define PIN_NUM_MOSI 41
#define PIN_NUM_CLK 40
#define PIN_NUM_CS 39
#define PIN_NUM_RST 42
#define PIN_NUM_DC 38
#define PIN_NUM_BL 4
#define LVGL_TICK_PERIOD_MS 2
#define BUTTON_PIN 3
lv_obj_t *container;
static const sh8601_lcd_init_cmd_t lcd_init_cmds[] = {
{0x36, (uint8_t[]){0xC0}, 1, 0},
{0x3A, (uint8_t[]){0x05}, 1, 0},
{0xB2, (uint8_t[]){0x0C, 0x0C, 0x00, 0x33, 0x33}, 5, 0},
{0xB4, (uint8_t[]){0x01}, 1, 0},
{0xC0, (uint8_t[]){0x2C, 0x2D}, 2, 0},
{0xC5, (uint8_t[]){0x2E}, 1, 0},
{0x21, NULL, 0, 0},
{0x2A, (uint8_t[]){0x00, 0x00, 0x00, 0xEF}, 4, 0},
{0x2B, (uint8_t[]){0x00, 0x00, 0x00, 0xEF}, 4, 0},
{0x11, NULL, 0, 120},
{0x29, NULL, 0, 0},
};
static bool flush_ready_cb(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx)
{
lv_disp_drv_t *disp_drv = (lv_disp_drv_t *)user_ctx;
lv_disp_flush_ready(disp_drv);
return false;
}
static void lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
{
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)drv->user_data;
int x1 = area->x1;
int x2 = area->x2;
int y1 = area->y1 + 80;
int y2 = area->y2 + 80;
size_t width = x2 - x1 + 1;
size_t height = y2 - y1 + 1;
size_t px_count = width * height;
uint16_t *swap_buf = heap_caps_malloc(px_count * sizeof(uint16_t), MALLOC_CAP_DMA);
if (!swap_buf)
{
ESP_LOGE("LVGL", "Failed to allocate swap buffer!");
lv_disp_flush_ready(drv);
return;
}
for (size_t i = 0; i < px_count; i++)
{
uint16_t color = color_map[i].full;
swap_buf[i] = (color >> 8) | (color << 8);
}
esp_lcd_panel_draw_bitmap(panel_handle, x1, y1, x2 + 1, y2 + 1, swap_buf);
heap_caps_free(swap_buf);
}
static void lv_tick_cb(void *arg)
{
lv_tick_inc(LVGL_TICK_PERIOD_MS);
}
void ui_init(void)
{
lv_obj_t *scr = lv_scr_act();
container = lv_obj_create(scr);
lv_obj_set_style_pad_all(container, 0, 0);
lv_obj_set_style_pad_row(container, 0, 0);
lv_obj_set_style_pad_column(container, 0, 0);
lv_obj_set_size(container, LCD_H_RES, LCD_V_RES);
lv_obj_set_layout(container, LV_LAYOUT_FLEX);
lv_obj_set_flex_flow(container, LV_FLEX_FLOW_COLUMN);
lv_obj_set_scroll_dir(container, LV_DIR_VER);
for (int i = 0; i < 5; i++)
{
lv_obj_t *rect = lv_obj_create(container);
lv_obj_set_size(rect, LCD_H_RES, 80);
lv_obj_set_style_bg_color(rect, lv_palette_main(LV_PALETTE_RED), 0);
}
}
void app_main(void)
{
ESP_LOGI("App", "Starting app_main");
static lv_disp_draw_buf_t draw_buf;
static lv_disp_drv_t disp_drv;
gpio_config_t gpio_conf = {
.intr_type = GPIO_INTR_DISABLE,
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = (1ULL << PIN_NUM_RST) | (1ULL << PIN_NUM_BL),
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.pull_up_en = GPIO_PULLUP_ENABLE,
};
ESP_ERROR_CHECK(gpio_config(&gpio_conf));
gpio_set_level(PIN_NUM_BL, 1);
gpio_config_t button_conf = {
.pin_bit_mask = (1ULL << BUTTON_PIN),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_NEGEDGE};
ESP_ERROR_CHECK(gpio_config(&button_conf));
spi_bus_config_t bus_conf = {
.mosi_io_num = PIN_NUM_MOSI,
.miso_io_num = -1,
.sclk_io_num = PIN_NUM_CLK,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = LCD_H_RES * LCD_DMA_LINE * sizeof(uint16_t),
};
ESP_ERROR_CHECK(spi_bus_initialize(LCD_HOST, &bus_conf, SPI_DMA_CH_AUTO));
esp_lcd_panel_io_handle_t io_handle = NULL;
esp_lcd_panel_io_spi_config_t io_config = {
.dc_gpio_num = PIN_NUM_DC,
.cs_gpio_num = PIN_NUM_CS,
.pclk_hz = 20 * 1000 * 1000,
.lcd_cmd_bits = 8,
.lcd_param_bits = 8,
.spi_mode = 0,
.trans_queue_depth = 10,
.on_color_trans_done = flush_ready_cb,
.user_ctx = &disp_drv};
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(LCD_HOST, &io_config, &io_handle));
sh8601_vendor_config_t vendor_config = {
.init_cmds = lcd_init_cmds,
.init_cmds_size = sizeof(lcd_init_cmds) / sizeof(lcd_init_cmds[0]),
};
esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = PIN_NUM_RST,
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
.bits_per_pixel = 16,
.data_endian = LCD_RGB_DATA_ENDIAN_BIG,
.vendor_config = &vendor_config,
};
esp_lcd_panel_handle_t panel_handle = NULL;
ESP_ERROR_CHECK(esp_lcd_new_panel_sh8601(io_handle, &panel_config, &panel_handle));
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
ESP_LOGI("Panel", "Panel initialized");
lv_init();
static lv_color_t *buf1 = NULL, *buf2 = NULL;
buf1 = heap_caps_malloc(LCD_H_RES * LCD_DMA_LINE * sizeof(lv_color_t), MALLOC_CAP_DMA);
buf2 = heap_caps_malloc(LCD_H_RES * LCD_DMA_LINE * sizeof(lv_color_t), MALLOC_CAP_DMA);
assert(buf1 && buf2);
lv_disp_draw_buf_init(&draw_buf, buf1, buf2, LCD_H_RES * LCD_V_RES);
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = LCD_H_RES;
disp_drv.ver_res = LCD_V_RES;
disp_drv.flush_cb = lvgl_flush_cb;
disp_drv.draw_buf = &draw_buf;
disp_drv.user_data = panel_handle;
lv_disp_drv_register(&disp_drv);
// Start LVGL tick
const esp_timer_create_args_t tick_timer_args = {
.callback = lv_tick_cb,
.name = "lvgl_tick",
};
esp_timer_handle_t tick_timer;
ESP_ERROR_CHECK(esp_timer_create(&tick_timer_args, &tick_timer));
ESP_ERROR_CHECK(esp_timer_start_periodic(tick_timer, LVGL_TICK_PERIOD_MS * 1000));
ui_init();
int prev = 1;
while (1)
{
lv_timer_handler();
int level = gpio_get_level(BUTTON_PIN);
if (level == 0 && prev == 1)
{
printf("pressed\n");
lv_obj_scroll_by(container, 0, 80, LV_ANIM_OFF);
}
prev = level;
vTaskDelay(pdMS_TO_TICKS(10));
}
}