////////////////////////////////////////////////////////////////////////////// // Includes ////////////////////////////////////////////////////////////////////////////// #include "../include/common.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" //#include "lvgl/src/lv_hal/lv_hal_disp.h" #include "espidf.h" #include "esp_lcd_panel_io.h" #include "esp_lcd_panel_rgb.h" #include "esp_lcd_panel_vendor.h" #include "esp_lcd_panel_ops.h" #include "esp_lcd_panel_interface.h" #include "esp_lcd_panel_rgb.h" #include "esp_private/gdma.h" #include "esp_pm.h" #include "hal/dma_types.h" #include "hal/lcd_hal.h" #include "esp_lcd_types.h" #include "hal/lcd_types.h" //#include "hal/lcd_ll.h" #include "lvgl/lvgl.h" ////////////////////////////////////////////////////////////////////////////// // makerfabs requires specific lv_conf resolution and color depth ////////////////////////////////////////////////////////////////////////////// #if LV_COLOR_DEPTH != 16 #error "modmakerfabs: LV_COLOR_DEPTH must be set to 16!" #endif ////////////////////////////////////////////////////////////////////////////// // Makerfabs Module definitions ////////////////////////////////////////////////////////////////////////////// typedef struct { esp_lcd_panel_t base; // Base class of generic lcd panel int panel_id; // LCD panel ID lcd_hal_context_t hal; // Hal layer object size_t data_width; // Number of data lines (e.g. for RGB565, the data width is 16) size_t sram_trans_align; // Alignment for framebuffer that allocated in SRAM size_t psram_trans_align; // Alignment for framebuffer that allocated in PSRAM int disp_gpio_num; // Display control GPIO, which is used to perform action like "disp_off" intr_handle_t intr; // LCD peripheral interrupt handle esp_pm_lock_handle_t pm_lock; // Power management lock size_t num_dma_nodes; // Number of DMA descriptors that used to carry the frame buffer uint8_t *fb; // Frame buffer size_t fb_size; // Size of frame buffer int data_gpio_nums[SOC_LCD_RGB_DATA_WIDTH]; // GPIOs used for data lines, we keep these GPIOs for action like "invert_color" uint32_t src_clk_hz; // Peripheral source clock resolution esp_lcd_rgb_timing_t timings; // RGB timing parameters (e.g. pclk, sync pulse, porch width) gdma_channel_handle_t dma_chan; // DMA channel handle esp_lcd_rgb_panel_frame_trans_done_cb_t on_frame_trans_done; // Callback, invoked after frame trans done void *user_ctx; // Reserved user's data of callback functions int x_gap; // Extra gap in x coordinate, it's used when calculate the flush window int y_gap; // Extra gap in y coordinate, it's used when calculate the flush window int lcd_clk_flags; // LCD clock calculation flags struct { unsigned int disp_en_level: 1; // The level which can turn on the screen by `disp_gpio_num` unsigned int stream_mode: 1; // If set, the LCD transfers data continuously, otherwise, it stops refreshing the LCD when transaction done unsigned int fb_in_psram: 1; // Whether the frame buffer is in PSRAM } flags; dma_descriptor_t dma_nodes[]; // DMA descriptor pool of size `num_dma_nodes` } esp_rgb_panel_t; typedef struct { mp_obj_base_t base; uint8_t de; uint8_t colormode; uint8_t vsync; uint8_t hsync; uint8_t pclk; uint8_t d0; uint8_t d1; uint8_t d2; uint8_t d3; uint8_t d4; uint8_t d5; uint8_t d6; uint8_t d7; int8_t d8; int8_t d9; int8_t d10; int8_t d11; int8_t d12; int8_t d13; int8_t d14; int8_t d15; uint32_t speed; uint16_t width; uint16_t height; uint8_t hsync_pulse_width; uint8_t hsync_back_porch; uint8_t hsync_front_porch; uint8_t hsync_polarity; uint8_t vsync_pulse_width; uint8_t vsync_back_porch; uint8_t vsync_front_porch; uint8_t vsync_polarity; uint8_t pclk_active_neg; esp_lcd_panel_handle_t panel_handle; esp_rgb_panel_t *rgb_panel; esp_lcd_rgb_panel_config_t *panel_config; lv_disp_draw_buf_t *draw_buf; lv_color_t *disp_draw_buf; lv_disp_drv_t *disp_drv; } makerfabs_t; // Unfortunately, lvgl doesnt pass user_data to callbacks, so we use this global. // This means we can have only one active display driver instance, pointed by this global. STATIC makerfabs_t *g_makerfabs = NULL; STATIC mp_obj_t makerfabs_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); STATIC mp_obj_t mp_init_makerfabs(mp_obj_t self_in); STATIC mp_obj_t mp_activate_makerfabs(mp_obj_t self_in) { makerfabs_t *self = MP_OBJ_TO_PTR(self_in); g_makerfabs = self; return mp_const_none; } STATIC void makerfabs_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p); STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_init_makerfabs_obj, mp_init_makerfabs); //STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_activate_makerfabs_obj, mp_activate_makerfabs); DEFINE_PTR_OBJ(makerfabs_flush); STATIC const mp_rom_map_elem_t makerfabs_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&mp_init_makerfabs_obj) }, //{ MP_ROM_QSTR(MP_QSTR_activate), MP_ROM_PTR(&mp_activate_makerfabs_obj) }, // init calls activate. { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&PTR_OBJ(makerfabs_flush)) }, }; STATIC MP_DEFINE_CONST_DICT(makerfabs_locals_dict, makerfabs_locals_dict_table); STATIC const mp_obj_type_t makerfabs_type = { { &mp_type_type }, .name = MP_QSTR_Makerfabs, .make_new = makerfabs_make_new, .locals_dict = (mp_obj_dict_t*)&makerfabs_locals_dict, }; STATIC mp_obj_t makerfabs_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { enum{ ARG_width, ARG_height, ARG_de, ARG_vsync, ARG_hsync, ARG_pclk, ARG_d0, ARG_d1, ARG_d2, ARG_d3, ARG_d4, ARG_d5, ARG_d6, ARG_d7, ARG_d8, ARG_d9, ARG_d10, ARG_d11, ARG_d12, ARG_d13, ARG_d14, ARG_d15, ARG_speed, ARG_hsync_pulse_width, ARG_hsync_back_porch, ARG_hsync_front_porch, ARG_hsync_polarity, ARG_vsync_pulse_width, ARG_vsync_back_porch, ARG_vsync_front_porch, ARG_vsync_polarity, ARG_pclk_active_neg }; static const mp_arg_t allowed_args[] = { { MP_QSTR_width,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_height,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_de,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_vsync,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_hsync,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_pclk,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_d0,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_d1,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_d2,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_d3,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_d4,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_d5,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_d6,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_d7,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_d8,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_d9,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_d10,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_d11,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_d12,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_d13,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_d14,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_d15,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_speed,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_hsync_pulse_width,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_hsync_back_porch,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_hsync_front_porch,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_hsync_polarity,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_vsync_pulse_width,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_vsync_back_porch,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_vsync_front_porch,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_vsync_polarity,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, { MP_QSTR_pclk_active_neg,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int=-1}}, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); makerfabs_t *self = m_new_obj(makerfabs_t); self->base.type = type; self->de = args[ARG_de].u_int; self->vsync = args[ARG_vsync].u_int; self->hsync = args[ARG_hsync].u_int; self->pclk = args[ARG_pclk].u_int; self->d0 = args[ARG_d0].u_int; self->d1 = args[ARG_d1].u_int; self->d2 = args[ARG_d2].u_int; self->d3 = args[ARG_d3].u_int; self->d4 = args[ARG_d4].u_int; self->d5 = args[ARG_d5].u_int; self->d6 = args[ARG_d6].u_int; self->d7 = args[ARG_d7].u_int; self->d8 = args[ARG_d8].u_int; self->d9 = args[ARG_d9].u_int; self->d10 = args[ARG_d10].u_int; self->d11 = args[ARG_d11].u_int; self->d12 = args[ARG_d12].u_int; self->d13 = args[ARG_d13].u_int; self->d14 = args[ARG_d14].u_int; self->d15 = args[ARG_d15].u_int; self->speed = args[ARG_speed].u_int; self->width = args[ARG_width].u_int; self->height = args[ARG_height].u_int; self->hsync_pulse_width = args[ARG_hsync_pulse_width].u_int; self->hsync_back_porch = args[ARG_hsync_back_porch].u_int; self->hsync_front_porch = args[ARG_hsync_front_porch].u_int; self->hsync_polarity = args[ARG_hsync_polarity].u_int; self->vsync_pulse_width = args[ARG_vsync_pulse_width].u_int; self->vsync_back_porch = args[ARG_vsync_back_porch].u_int; self->vsync_front_porch = args[ARG_vsync_front_porch].u_int; self->vsync_polarity = args[ARG_vsync_polarity].u_int; self->pclk_active_neg = args[ARG_pclk_active_neg].u_int; return MP_OBJ_FROM_PTR(self); } STATIC const mp_rom_map_elem_t makerfabs_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_makerfabs) }, { MP_ROM_QSTR(MP_QSTR_display), (mp_obj_t)&makerfabs_type}, }; STATIC MP_DEFINE_CONST_DICT ( mp_module_makerfabs_globals, makerfabs_globals_table ); const mp_obj_module_t mp_module_makerfabs = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_makerfabs_globals }; MP_REGISTER_MODULE(MP_QSTR_Makerfabs, mp_module_makerfabs); ////////////////////////////////////////////////////////////////////////////// // parallel RGB driver implementation ////////////////////////////////////////////////////////////////////////////// STATIC void disp_init(makerfabs_t *self) { esp_err_t ret; esp_lcd_rgb_panel_config_t *panel_config = (esp_lcd_rgb_panel_config_t *)heap_caps_calloc(1, sizeof(esp_lcd_rgb_panel_config_t), MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL); if (self->d15 != -1) { panel_config->data_width = 16; panel_config->sram_trans_align = 16; panel_config->data_gpio_nums[0] = self->d0; panel_config->data_gpio_nums[1] = self->d1; panel_config->data_gpio_nums[2] = self->d2; panel_config->data_gpio_nums[3] = self->d3; panel_config->data_gpio_nums[4] = self->d4; panel_config->data_gpio_nums[5] = self->d5; panel_config->data_gpio_nums[6] = self->d6; panel_config->data_gpio_nums[7] = self->d7; panel_config->data_gpio_nums[8] = self->d8; panel_config->data_gpio_nums[9] = self->d9; panel_config->data_gpio_nums[10] = self->d10; panel_config->data_gpio_nums[11] = self->d11; panel_config->data_gpio_nums[12] = self->d12; panel_config->data_gpio_nums[13] = self->d13; panel_config->data_gpio_nums[14] = self->d13; panel_config->data_gpio_nums[15] = self->d15; } else { panel_config->data_width = 8; panel_config->sram_trans_align = 8; panel_config->data_gpio_nums[0] = self->d0; panel_config->data_gpio_nums[1] = self->d1; panel_config->data_gpio_nums[2] = self->d2; panel_config->data_gpio_nums[3] = self->d3; panel_config->data_gpio_nums[4] = self->d4; panel_config->data_gpio_nums[5] = self->d5; panel_config->data_gpio_nums[6] = self->d6; panel_config->data_gpio_nums[7] = self->d7; } panel_config->clk_src = LCD_CLK_SRC_PLL160M; panel_config->psram_trans_align = 64; panel_config->hsync_gpio_num = self->hsync; panel_config->vsync_gpio_num = self->vsync; panel_config->de_gpio_num = self->de; panel_config->pclk_gpio_num = self->pclk; panel_config->disp_gpio_num = GPIO_NUM_NC; panel_config->flags.disp_active_low = 0; panel_config->flags.relax_on_idle = 0; panel_config->flags.fb_in_psram = 1; panel_config->timings.pclk_hz = self->speed; panel_config->timings.h_res = self->width; panel_config->timings.v_res = self->height; panel_config->timings.hsync_back_porch = self->hsync_back_porch; panel_config->timings.hsync_front_porch = self->hsync_front_porch; panel_config->timings.hsync_pulse_width = self->hsync_pulse_width; panel_config->timings.vsync_back_porch = self->vsync_back_porch; panel_config->timings.vsync_front_porch = self->vsync_front_porch; panel_config->timings.vsync_pulse_width = self->vsync_pulse_width; panel_config->timings.flags.hsync_idle_low = (self->hsync_polarity == 0) ? 1 : 0; panel_config->timings.flags.vsync_idle_low = (self->vsync_polarity == 0) ? 1 : 0; panel_config->timings.flags.de_idle_high = 0; panel_config->timings.flags.pclk_active_neg = self->pclk_active_neg; panel_config->timings.flags.pclk_idle_high = 0; ret = esp_lcd_new_rgb_panel(panel_config, &self->panel_handle); if (ret != ESP_OK) { nlr_raise(mp_obj_new_exception_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Failed creating display panel"))); } ret = esp_lcd_panel_reset(self->panel_handle); if (ret != ESP_OK) { nlr_raise(mp_obj_new_exception_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Failed resetting display panel"))); } ret = esp_lcd_panel_init(self->panel_handle); if (ret != ESP_OK) { nlr_raise(mp_obj_new_exception_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Failed initializing display panel"))); } uint16_t color = 0xb967; ret = esp_lcd_panel_draw_bitmap(self->panel_handle, 0, 0, 1, 1, &color); if (ret != ESP_OK) { nlr_raise(mp_obj_new_exception_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Failed rendering to display panel"))); } uint16_t color2 = 0x967b; ret = esp_lcd_panel_draw_bitmap(self->panel_handle, 10, 10, 11, 11, &color2); if (ret != ESP_OK) { nlr_raise(mp_obj_new_exception_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Failed rendering to display panel 2"))); } uint16_t color3 = 0x1672; ret = esp_lcd_panel_draw_bitmap(self->panel_handle, 20, 20, 21, 21, &color3); if (ret != ESP_OK) { nlr_raise(mp_obj_new_exception_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Failed rendering to display panel 3"))); } self->panel_config = panel_config; self->rgb_panel = __containerof(self->panel_handle, esp_rgb_panel_t, base); /* lv_init(); #ifdef ESP32 self->disp_draw_buf = (lv_color_t *)heap_caps_malloc(sizeof(lv_color_t) * self->width * 10, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); #else self->disp_draw_buf = (lv_color_t *)malloc(sizeof(lv_color_t) * self->width * 10); #endif if (!self->disp_draw_buf) { nlr_raise(mp_obj_new_exception_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("LVGL disp_draw_buf allocate failed!"))); } lv_disp_draw_buf_init(self->draw_buf, self->disp_draw_buf, NULL, self->width * 10); // Crashes with StoreProhibited, EXCVADDR:0x0 lv_disp_drv_init(self->disp_drv); self->disp_drv->hor_res = self->width; self->disp_drv->ver_res = self->height; self->disp_drv->flush_cb = makerfabs_flush; self->disp_drv->draw_buf = self->draw_buf; lv_disp_drv_register(self->disp_drv); */ } STATIC mp_obj_t mp_init_makerfabs(mp_obj_t self_in) { makerfabs_t *self = MP_OBJ_TO_PTR(self_in); mp_activate_makerfabs(self_in); disp_init(self); return mp_const_none; } /* #include "esp32s3/rom/cache.h" // This function is located in ROM (also see esp_rom/${target}/ld/${target}.rom.ld) extern int Cache_WriteBack_Addr(uint32_t addr, uint32_t size); STATIC void makerfabs_draw16bitRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, int16_t w, int16_t h) { makerfabs_t *self = g_makerfabs; if ( ((x + w - 1) < 0) || // Outside left ((y + h - 1) < 0) || // Outside top (x > self->width) || // Outside right (y > self->height) // Outside bottom ) { return; } else { int16_t xskip = 0; if ((y + h - 1) > self->height) { h -= (y + h - 1) - self->height; } if (y < 0) { bitmap -= y * w; h += y; y = 0; } if ((x + w - 1) > self->width) { xskip = (x + w - 1) - self->width; w -= xskip; } if (x < 0) { bitmap -= x; xskip -= x; w += x; x = 0; } uint16_t *row = (uint16_t *)self->rgb_panel->fb; row += y * self->width; uint32_t cachePos = (uint32_t)row; row += x; if (((self->width & 1) == 0) && ((xskip & 1) == 0) && ((w & 1) == 0)) { uint32_t *row2 = (uint32_t *)row; uint32_t *bitmap2 = (uint32_t *)bitmap; int16_t _width2 = self->width >> 1; int16_t xskip2 = xskip >> 1; int16_t w2 = w >> 1; for (int16_t j = 0; j < h; j++) { for (int16_t i = 0; i < w2; i++) { row2[i] = *bitmap2++; } bitmap2 += xskip2; row2 += _width2; } } else { for (int j = 0; j < h; j++) { for (int i = 0; i < w; i++) { row[i] = *bitmap++; } bitmap += xskip; row += self->width; } } Cache_WriteBack_Addr(cachePos, self->width * h * 2); } } */ STATIC void makerfabs_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) { makerfabs_t *self = g_makerfabs; esp_err_t ret; ret = esp_lcd_panel_draw_bitmap(self->panel_handle, area->x1, area->y1, area->x2, area->y2, color_p); if (ret != ESP_OK) { nlr_raise(mp_obj_new_exception_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Failed rendering, flush"))); } //nlr_raise(mp_obj_new_exception_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Flush called"))); //makerfabs_draw16bitRGBBitmap(area->x1, area->y1, &color_p, area->x2-area->x1, area->y2-area->y1); lv_disp_flush_ready(disp_drv); }