diff --git a/.github/workflows/upload_component.yml b/.github/workflows/upload_component.yml index a1c661ef..3f58e80a 100644 --- a/.github/workflows/upload_component.yml +++ b/.github/workflows/upload_component.yml @@ -18,7 +18,7 @@ jobs: esp32_azure_iot_kit;esp32_s2_kaluga_kit;esp_wrover_kit;esp-box;esp32_s3_usb_otg;esp32_s3_eye;esp32_s3_lcd_ev_board;esp32_s3_korvo_2;esp-box-lite;esp32_lyrat;esp32_c3_lcdkit;esp-box-3; components/bh1750;components/ds18b20;components/es8311;components/es7210;components/fbm320;components/hts221;components/mag3110;components/mpu6050;components/ssd1306;components/esp_lvgl_port; components/lcd_touch/esp_lcd_touch;components/lcd_touch/esp_lcd_touch_ft5x06;components/lcd_touch/esp_lcd_touch_gt911;components/lcd_touch/esp_lcd_touch_tt21100;components/lcd_touch/esp_lcd_touch_gt1151;components/lcd_touch/esp_lcd_touch_cst816s; - components/lcd/esp_lcd_gc9a01;components/lcd/esp_lcd_ili9341;components/lcd/esp_lcd_ra8875;components/lcd_touch/esp_lcd_touch_stmpe610;components/lcd/esp_lcd_sh1107;components/lcd/esp_lcd_st7796;components/lcd/esp_lcd_gc9503; + components/lcd/esp_lcd_gc9a01;components/lcd/esp_lcd_ili9341;components/lcd/esp_lcd_ra8875;components/lcd_touch/esp_lcd_touch_stmpe610;components/lcd/esp_lcd_sh1107;components/lcd/esp_lcd_st7796;components/lcd/esp_lcd_gc9503;components/lcd/esp_lcd_ssd1681; components/io_expander/esp_io_expander;components/io_expander/esp_io_expander_tca9554;components/io_expander/esp_io_expander_tca95xx_16bit;components/io_expander/esp_io_expander_ht8574; namespace: "espressif" api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }} diff --git a/components/lcd/esp_lcd_ssd1681/README.md b/components/lcd/esp_lcd_ssd1681/README.md index 580f459f..25d5e7f4 100644 --- a/components/lcd/esp_lcd_ssd1681/README.md +++ b/components/lcd/esp_lcd_ssd1681/README.md @@ -2,6 +2,12 @@ [![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_ssd1681/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_ssd1681) +Implementation of the SSD1681 e-Paper controller with esp_lcd component. + +| LCD controller | Communication interface | Component name | Link to datasheet | +| :------------: | :---------------------: | :------------: | :---------------: | +| SSD1681 | SPI | esp_lcd_ssd1681| [Specification](https://cdn-learn.adafruit.com/assets/assets/000/099/573/original/SSD1681.pdf) | + ## Add to project Packages from this repository are uploaded to [Espressif's component service](https://components.espressif.com/). @@ -35,7 +41,7 @@ Original function prototype: */ esp_err_t esp_lcd_panel_mirror(esp_lcd_panel_handle_t panel, bool mirror_x, bool mirror_y); ``` -Please note that `y_axis` has to be false if you enabled the `non_copy_mode` when constructing the panel, otherwise the function will return `ESP_ERR_INVALID_ARG`. +Please note that `mirror_y` has to be false if you enabled the `non_copy_mode` when constructing the panel, otherwise the function will return `ESP_ERR_INVALID_ARG`. ### `esp_lcd_panel_swap_xy` @@ -82,19 +88,19 @@ Please note that this function will draw the bitmap but do not refresh the panel Original function prototype: ```c /** - * @brief Turn off the display + * @brief Turn on or off the display * * @param[in] panel LCD panel handle, which is created by other factory API like `esp_lcd_new_panel_st7789()` - * @param[in] off Whether to turn off the screen + * @param[in] on_off True to turns on display, False to turns off display * @return * - ESP_OK on success * - ESP_ERR_NOT_SUPPORTED if this function is not supported by the panel */ -esp_err_t esp_lcd_panel_disp_off(esp_lcd_panel_handle_t panel, bool off) +esp_err_t esp_lcd_panel_disp_on_off(esp_lcd_panel_handle_t panel, bool on_off); ``` -Call with parameter `off` set to false will have the e-paper panel enter sleep mode. BUSY pin will stay HIGH in sleep mode and a `esp_lcd_panel_init()` call is needed to resume the panel. Call with parameter `off` set to true will load the panel built-in waveform LUT, it is useful if you had set a custom waveform LUT. +Call with parameter `on_off` set to false will have the e-paper panel enter sleep mode. BUSY pin will stay HIGH in sleep mode and a `esp_lcd_panel_init()` call is needed to resume the panel. Call with parameter `on_off` set to true will load the panel built-in waveform LUT, it is useful if you had set a custom waveform LUT. ## Service Life Optimization - The screen should not be powered on for extended periods of time. Please use the `disp_on_off` API to put the screen into sleep mode or cut down the power when the screen is not refreshing. -- Do not refresh the screen at maximum speed for a long time. E-Paper panels are not made to show dynamic contents. The refresh interval should be at least 3 minutes if you want to refresh the screen continuously. \ No newline at end of file +- Do not refresh the screen at maximum speed for a long time. E-Paper panels are not made to show dynamic contents quickly. The refresh interval should be at least 3 minutes if you want to refresh the screen continuously. \ No newline at end of file diff --git a/components/lcd/esp_lcd_ssd1681/esp_lcd_panel_ssd1681.c b/components/lcd/esp_lcd_ssd1681/esp_lcd_panel_ssd1681.c index 3b8e7ff5..2cec32e0 100644 --- a/components/lcd/esp_lcd_ssd1681/esp_lcd_panel_ssd1681.c +++ b/components/lcd/esp_lcd_ssd1681/esp_lcd_panel_ssd1681.c @@ -59,7 +59,7 @@ typedef struct { bool _swap_xy; // --- Other private fields bool _mirror_x; - uint8_t* _framebuffer; + uint8_t *_framebuffer; bool _invert_color; } epaper_panel_t; @@ -68,12 +68,12 @@ static inline uint8_t byte_reverse(uint8_t data); static esp_err_t process_bitmap(esp_lcd_panel_t *panel, int len_x, int len_y, int buffer_size, const void *color_data); static esp_err_t panel_epaper_wait_busy(esp_lcd_panel_t *panel); // --- Callback functions & ISRs -static void epaper_driver_gpio_isr_handler(void* arg); +static void epaper_driver_gpio_isr_handler(void *arg); // --- IO wrapper functions, simply send command/param/buffer -static esp_err_t epaper_set_lut(esp_lcd_panel_io_handle_t io, const uint8_t* lut); +static esp_err_t epaper_set_lut(esp_lcd_panel_io_handle_t io, const uint8_t *lut); static esp_err_t epaper_set_cursor(esp_lcd_panel_io_handle_t io, uint32_t cur_x, uint32_t cur_y); static esp_err_t epaper_set_area(esp_lcd_panel_io_handle_t io, uint32_t start_x, uint32_t start_y, uint32_t end_x, uint32_t end_y); -static esp_err_t panel_epaper_set_vram(esp_lcd_panel_io_handle_t io, uint8_t* bw_bitmap, uint8_t* red_bitmap, size_t size); +static esp_err_t panel_epaper_set_vram(esp_lcd_panel_io_handle_t io, uint8_t *bw_bitmap, uint8_t *red_bitmap, size_t size); // --- SSD1681 specific functions, exported to user in public header file // extern esp_err_t esp_lcd_new_panel_ssd1681(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, // esp_lcd_panel_handle_t *ret_panel); @@ -93,19 +93,20 @@ static esp_err_t epaper_panel_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_g static esp_err_t epaper_panel_disp_on_off(esp_lcd_panel_t *panel, bool on_off); -static void epaper_driver_gpio_isr_handler(void* arg) +static void epaper_driver_gpio_isr_handler(void *arg) { epaper_panel_t *epaper_panel = arg; // --- Disable ISR handling gpio_intr_disable(epaper_panel->busy_gpio_num); // --- Call user callback func - if(epaper_panel->epaper_refresh_done_isr_callback.callback_ptr) { + if (epaper_panel->epaper_refresh_done_isr_callback.callback_ptr) { (epaper_panel->epaper_refresh_done_isr_callback.callback_ptr)(&(epaper_panel->base), NULL, epaper_panel->epaper_refresh_done_isr_callback.args); } } -esp_err_t epaper_panel_register_event_callbacks(esp_lcd_panel_t *panel, epaper_panel_callbacks_t* cbs, void* user_ctx) { +esp_err_t epaper_panel_register_event_callbacks(esp_lcd_panel_t *panel, epaper_panel_callbacks_t *cbs, void *user_ctx) +{ ESP_RETURN_ON_FALSE(panel, ESP_ERR_INVALID_ARG, TAG, "panel handler is NULL"); ESP_RETURN_ON_FALSE(cbs, ESP_ERR_INVALID_ARG, TAG, "cbs is NULL"); epaper_panel_t *epaper_panel = __containerof(panel, epaper_panel_t, base); @@ -114,7 +115,8 @@ esp_err_t epaper_panel_register_event_callbacks(esp_lcd_panel_t *panel, epaper_p return ESP_OK; } -esp_err_t epaper_panel_set_custom_lut(esp_lcd_panel_t *panel, uint8_t *lut, size_t size) { +esp_err_t epaper_panel_set_custom_lut(esp_lcd_panel_t *panel, uint8_t *lut, size_t size) +{ ESP_RETURN_ON_FALSE(panel, ESP_ERR_INVALID_ARG, TAG, "panel handler is NULL"); ESP_RETURN_ON_FALSE(lut, ESP_ERR_INVALID_ARG, TAG, "lut is NULL"); ESP_RETURN_ON_FALSE(size == SSD1681_LUT_SIZE, ESP_ERR_INVALID_ARG, TAG, "Invalid lut size"); @@ -128,74 +130,78 @@ static esp_err_t epaper_set_lut(esp_lcd_panel_io_handle_t io, const uint8_t *lut ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, SSD1681_CMD_SET_LUT_REG, lut, 153), TAG, "SSD1681_CMD_OUTPUT_CTRL err"); ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, SSD1681_CMD_SET_DISP_UPDATE_CTRL, (uint8_t[]) { - lut[153] + lut[153] }, 1), TAG, "SSD1681_CMD_SET_END_OPTION err"); ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, SSD1681_CMD_SET_GATE_DRIVING_VOLTAGE, (uint8_t[]) { - lut[154] + lut[154] }, 1), TAG, "SSD1681_CMD_SET_END_OPTION err"); ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, SSD1681_CMD_SET_SRC_DRIVING_VOLTAGE, (uint8_t[]) { - lut[155], lut[156], lut[157] + lut[155], lut[156], lut[157] }, 3), TAG, "SSD1681_CMD_SET_SRC_DRIVING_VOLTAGE err"); ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, SSD1681_CMD_SET_VCOM_REG, (uint8_t[]) { - lut[158] + lut[158] }, 1), TAG, "SSD1681_CMD_SET_VCOM_REG err"); return ESP_OK; } -static esp_err_t epaper_set_cursor(esp_lcd_panel_io_handle_t io, uint32_t cur_x, uint32_t cur_y) { +static esp_err_t epaper_set_cursor(esp_lcd_panel_io_handle_t io, uint32_t cur_x, uint32_t cur_y) +{ ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, SSD1681_CMD_SET_INIT_X_ADDR_COUNTER, (uint8_t[]) { - (uint8_t)((cur_x>>3) & 0xff) + (uint8_t)((cur_x >> 3) & 0xff) }, 1), TAG, "SSD1681_CMD_SET_INIT_X_ADDR_COUNTER err"); ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, SSD1681_CMD_SET_INIT_Y_ADDR_COUNTER, (uint8_t[]) { - (uint8_t)(cur_y & 0xff), // cur_y[7:0] - (uint8_t)((cur_y>>8) & 0xff) // cur_y[8] + (uint8_t)(cur_y & 0xff), // cur_y[7:0] + (uint8_t)((cur_y >> 8) & 0xff) // cur_y[8] }, 2), TAG, "SSD1681_CMD_SET_INIT_Y_ADDR_COUNTER err"); return ESP_OK; } -static esp_err_t epaper_set_area(esp_lcd_panel_io_handle_t io, uint32_t start_x, uint32_t start_y, uint32_t end_x, uint32_t end_y) { +static esp_err_t epaper_set_area(esp_lcd_panel_io_handle_t io, uint32_t start_x, uint32_t start_y, uint32_t end_x, uint32_t end_y) +{ // --- Set RAMX Start/End Position ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, SSD1681_CMD_SET_RAMX_START_END_POS, (uint8_t[]) { - (start_x >> 3) & 0xff, // start_x - (end_x >> 3) & 0xff // end_x + (start_x >> 3) & 0xff, // start_x + (end_x >> 3) & 0xff // end_x }, 2), TAG, "SSD1681_CMD_SET_RAMX_START_END_POS err"); // --- Set RAMY Start/End Position ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, SSD1681_CMD_SET_RAMY_START_END_POS, (uint8_t[]) { - (start_y) & 0xff, // start_y[7:0] - (start_y >> 8) & 0xff, // start_y[8] - end_y & 0xff, // end_y[7:0] - (end_y >> 8) & 0xff // end_y[8] + (start_y) & 0xff, // start_y[7:0] + (start_y >> 8) & 0xff, // start_y[8] + end_y & 0xff, // end_y[7:0] + (end_y >> 8) & 0xff // end_y[8] }, 4), TAG, "SSD1681_CMD_SET_RAMX_START_END_POS err"); return ESP_OK; } -static esp_err_t panel_epaper_wait_busy(esp_lcd_panel_t *panel) { +static esp_err_t panel_epaper_wait_busy(esp_lcd_panel_t *panel) +{ epaper_panel_t *epaper_panel = __containerof(panel, epaper_panel_t, base); - while(gpio_get_level(epaper_panel->busy_gpio_num)) { + while (gpio_get_level(epaper_panel->busy_gpio_num)) { vTaskDelay(pdMS_TO_TICKS(15)); } return ESP_OK; } -esp_err_t panel_epaper_set_vram(esp_lcd_panel_io_handle_t io, uint8_t* bw_bitmap, uint8_t* red_bitmap, size_t size) { +esp_err_t panel_epaper_set_vram(esp_lcd_panel_io_handle_t io, uint8_t *bw_bitmap, uint8_t *red_bitmap, size_t size) +{ // Note: the screen region to be used to draw bitmap had been defined // The region of BLACK VRAM and RED VRAM are set by the same series of command, the two bitmaps will be drawn at // the same region, so the two bitmaps can share a same size. // --- Validate and Transfer data - if(bw_bitmap && (size > 0)) { + if (bw_bitmap && (size > 0)) { // tx WHITE/BLACK bitmap ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_color(io, SSD1681_CMD_WRITE_BLACK_VRAM, bw_bitmap, size), TAG, "data bw_bitmap err"); } - if(red_bitmap && (size > 0)) { + if (red_bitmap && (size > 0)) { // tx RED bitmap ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_color(io, SSD1681_CMD_WRITE_RED_VRAM, red_bitmap, size), TAG, "data red_bitmap err"); @@ -203,27 +209,27 @@ esp_err_t panel_epaper_set_vram(esp_lcd_panel_io_handle_t io, uint8_t* bw_bitmap return ESP_OK; } -esp_err_t epaper_panel_refresh_screen(esp_lcd_panel_t *panel) { +esp_err_t epaper_panel_refresh_screen(esp_lcd_panel_t *panel) +{ ESP_RETURN_ON_FALSE(panel, ESP_ERR_INVALID_ARG, TAG, "panel handler is NULL"); epaper_panel_t *epaper_panel = __containerof(panel, epaper_panel_t, base); // --- Set color invert uint8_t duc_flag = 0x00; - if(!(epaper_panel->_invert_color)) { + if (!(epaper_panel->_invert_color)) { duc_flag |= SSD1681_PARAM_COLOR_BW_INVERSE_BIT; duc_flag &= (~SSD1681_PARAM_COLOR_RW_INVERSE_BIT); - } - else { + } else { duc_flag &= (~SSD1681_PARAM_COLOR_BW_INVERSE_BIT); duc_flag |= SSD1681_PARAM_COLOR_RW_INVERSE_BIT; } ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(epaper_panel->io, SSD1681_CMD_DISP_UPDATE_CTRL, (uint8_t[]) { - duc_flag // Color invert flag + duc_flag // Color invert flag }, 1), TAG, "SSD1681_CMD_DISP_UPDATE_CTRL err"); // --- Enable refresh done handler isr gpio_intr_enable(epaper_panel->busy_gpio_num); // --- Send refresh command ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(epaper_panel->io, SSD1681_CMD_SET_DISP_UPDATE_CTRL, (uint8_t[]) { - SSD1681_PARAM_DISP_WITH_MODE_2 + SSD1681_PARAM_DISP_WITH_MODE_2 }, 1), TAG, "SSD1681_CMD_SET_DISP_UPDATE_CTRL err"); ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(epaper_panel->io, SSD1681_CMD_ACTIVE_DISP_UPDATE_SEQ, NULL, 0), TAG, @@ -233,14 +239,14 @@ esp_err_t epaper_panel_refresh_screen(esp_lcd_panel_t *panel) { } esp_err_t -esp_lcd_new_panel_ssd1681(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t * const panel_dev_config, - esp_lcd_panel_handle_t * const ret_panel) +esp_lcd_new_panel_ssd1681(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *const panel_dev_config, + esp_lcd_panel_handle_t *const ret_panel) { #if CONFIG_LCD_ENABLE_DEBUG_LOG esp_log_level_set(TAG, ESP_LOG_DEBUG); #endif ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "1 or more args is NULL"); - esp_lcd_ssd1681_config_t* epaper_ssd1681_conf = panel_dev_config->vendor_config; + esp_lcd_ssd1681_config_t *epaper_ssd1681_conf = panel_dev_config->vendor_config; esp_err_t ret = ESP_OK; // --- Allocate epaper_panel memory on HEAP epaper_panel_t *epaper_panel = NULL; @@ -276,27 +282,26 @@ esp_lcd_new_panel_ssd1681(const esp_lcd_panel_io_handle_t io, const esp_lcd_pane epaper_panel->base.disp_on_off = epaper_panel_disp_on_off; *ret_panel = &(epaper_panel->base); // --- Init framebuffer - if(!(epaper_panel->_non_copy_mode)) { + if (!(epaper_panel->_non_copy_mode)) { epaper_panel->_framebuffer = heap_caps_malloc(SSD1681_EPD_1IN54_V2_WIDTH * SSD1681_EPD_1IN54_V2_HEIGHT / 8, - MALLOC_CAP_DMA); + MALLOC_CAP_DMA); ESP_RETURN_ON_FALSE(epaper_panel->_framebuffer, ESP_ERR_NO_MEM, TAG, "epaper_panel_draw_bitmap allocating buffer memory err"); } // --- Init GPIO // init RST GPIO if (epaper_panel->reset_gpio_num >= 0) { gpio_config_t io_conf = { - .mode = GPIO_MODE_OUTPUT, - .pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num, + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num, }; ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line err"); } // init BUSY GPIO - if(epaper_panel->busy_gpio_num >= 0) { + if (epaper_panel->busy_gpio_num >= 0) { gpio_config_t io_conf = { - .mode = GPIO_MODE_INPUT, - .pull_down_en = 0x01, - .pin_bit_mask = 1ULL << epaper_panel->busy_gpio_num, - + .mode = GPIO_MODE_INPUT, + .pull_down_en = 0x01, + .pin_bit_mask = 1ULL << epaper_panel->busy_gpio_num, }; io_conf.intr_type = GPIO_INTR_NEGEDGE; ESP_LOGI(TAG, "Add handler for GPIO %d", epaper_panel->busy_gpio_num); @@ -313,7 +318,7 @@ esp_lcd_new_panel_ssd1681(const esp_lcd_panel_io_handle_t io, const esp_lcd_pane if (panel_dev_config->reset_gpio_num >= 0) { gpio_reset_pin(panel_dev_config->reset_gpio_num); } - if(epaper_ssd1681_conf->busy_gpio_num >= 0) { + if (epaper_ssd1681_conf->busy_gpio_num >= 0) { gpio_reset_pin(epaper_ssd1681_conf->busy_gpio_num); } free(epaper_panel); @@ -330,7 +335,7 @@ static esp_err_t epaper_panel_del(esp_lcd_panel_t *panel) } gpio_reset_pin(epaper_panel->busy_gpio_num); // --- Free allocated RAM - if((epaper_panel->_framebuffer) && (!(epaper_panel->_non_copy_mode))) { + if ((epaper_panel->_framebuffer) && (!(epaper_panel->_non_copy_mode))) { // Should not free if buffer is not allocated by driver free(epaper_panel->_framebuffer); } @@ -360,7 +365,8 @@ static esp_err_t epaper_panel_reset(esp_lcd_panel_t *panel) return ESP_OK; } -esp_err_t epaper_panel_set_bitmap_color(esp_lcd_panel_t* panel, esp_lcd_ssd1681_bitmap_color_t color) { +esp_err_t epaper_panel_set_bitmap_color(esp_lcd_panel_t *panel, esp_lcd_ssd1681_bitmap_color_t color) +{ ESP_RETURN_ON_FALSE(panel, ESP_ERR_INVALID_ARG, TAG, "panel handler is NULL"); epaper_panel_t *epaper_panel = __containerof(panel, epaper_panel_t, base); epaper_panel->bitmap_color = color; @@ -375,30 +381,30 @@ static esp_err_t epaper_panel_init(esp_lcd_panel_t *panel) ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, SSD1681_CMD_SWRST, NULL, 0), TAG, "param SSD1681_CMD_SWRST err"); panel_epaper_wait_busy(panel); - // --- Driver Output Control + // --- Driver Output Control ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(epaper_panel->io, SSD1681_CMD_OUTPUT_CTRL, - SSD1681_PARAM_OUTPUT_CTRL, 3), TAG, "SSD1681_CMD_OUTPUT_CTRL err"); + SSD1681_PARAM_OUTPUT_CTRL, 3), TAG, "SSD1681_CMD_OUTPUT_CTRL err"); // --- Border Waveform Control ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(epaper_panel->io, SSD1681_CMD_SET_BORDER_WAVEFORM, (uint8_t[]) { - SSD1681_PARAM_BORDER_WAVEFORM + SSD1681_PARAM_BORDER_WAVEFORM }, 1), TAG, "SSD1681_CMD_SET_BORDER_WAVEFORM err"); // --- Temperature Sensor Control ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(epaper_panel->io, SSD1681_CMD_SET_TEMP_SENSOR, (uint8_t[]) { - SSD1681_PARAM_TEMP_SENSOR + SSD1681_PARAM_TEMP_SENSOR }, 1), TAG, "SSD1681_CMD_SET_TEMP_SENSOR err"); // --- Load built-in waveform LUT ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(epaper_panel->io, SSD1681_CMD_SET_DISP_UPDATE_CTRL, (uint8_t[]) { - SSD1681_PARAM_DISP_UPDATE_MODE_1 + SSD1681_PARAM_DISP_UPDATE_MODE_1 }, 1), TAG, "SSD1681_CMD_SET_DISP_UPDATE_CTRL err"); // --- Display end option ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(epaper_panel->io, SSD1681_CMD_SET_END_OPTION, (uint8_t[]) { - SSD1681_PARAM_END_OPTION_KEEP + SSD1681_PARAM_END_OPTION_KEEP }, 1), TAG, "SSD1681_CMD_SET_END_OPTION err"); // --- Active Display Update Sequence ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, SSD1681_CMD_ACTIVE_DISP_UPDATE_SEQ, NULL, 0), TAG, - "param SSD1681_CMD_SET_DISP_UPDATE_CTRL err"); + "param SSD1681_CMD_SET_DISP_UPDATE_CTRL err"); panel_epaper_wait_busy(panel); return ESP_OK; @@ -407,7 +413,10 @@ static esp_err_t epaper_panel_init(esp_lcd_panel_t *panel) static esp_err_t epaper_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data) { - epaper_panel_t * epaper_panel = __containerof(panel, epaper_panel_t, base); + epaper_panel_t *epaper_panel = __containerof(panel, epaper_panel_t, base); + if (gpio_get_level(epaper_panel->busy_gpio_num)) { + return ESP_ERR_NOT_FINISHED; + } x_start += epaper_panel->gap_x; x_end += epaper_panel->gap_x; y_start += epaper_panel->gap_y; @@ -415,7 +424,7 @@ epaper_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x // --- Assert & check configuration if (epaper_panel->_non_copy_mode) { ESP_RETURN_ON_FALSE(!(epaper_panel->_swap_xy), ESP_ERR_INVALID_ARG, TAG, "swap-xy is unavailable when enabling non-copy mode"); - ESP_RETURN_ON_FALSE(!(epaper_panel->_mirror_y) , ESP_ERR_INVALID_ARG, TAG, "mirror_y is unavailable when enabling non-copy mode"); + ESP_RETURN_ON_FALSE(!(epaper_panel->_mirror_y), ESP_ERR_INVALID_ARG, TAG, "mirror_y is unavailable when enabling non-copy mode"); } ESP_RETURN_ON_FALSE(color_data, ESP_ERR_INVALID_ARG, TAG, "bitmap is null"); ESP_RETURN_ON_FALSE((x_start < x_end) && (y_start < y_end), ESP_ERR_INVALID_ARG, TAG, "start position must be smaller than end position"); @@ -428,8 +437,8 @@ epaper_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x // prepare buffer if (epaper_panel->_non_copy_mode) { // Use user-passed framebuffer - epaper_panel->_framebuffer = (uint8_t*)color_data; - if(!esp_ptr_dma_capable(epaper_panel->_framebuffer)) { + epaper_panel->_framebuffer = (uint8_t *)color_data; + if (!esp_ptr_dma_capable(epaper_panel->_framebuffer)) { ESP_LOGW(TAG, "Bitmap not DMA capable, use DMA capable memory to avoid additional data copy."); } } else { @@ -437,29 +446,29 @@ epaper_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x process_bitmap(panel, len_x, len_y, buffer_size, color_data); } // --- Set cursor & data entry sequence - if((!(epaper_panel->_mirror_x)) && (!(epaper_panel->_mirror_y))) { + if ((!(epaper_panel->_mirror_x)) && (!(epaper_panel->_mirror_y))) { // --- Cursor Settings ESP_RETURN_ON_ERROR(epaper_set_area(epaper_panel->io, x_start, y_start, x_end, y_end), TAG, - "epaper_set_area() error"); + "epaper_set_area() error"); ESP_RETURN_ON_ERROR(epaper_set_cursor(epaper_panel->io, x_start, y_start), TAG, - "epaper_set_cursor() error"); + "epaper_set_cursor() error"); // --- Data Entry Sequence Setting ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(epaper_panel->io, SSD1681_CMD_DATA_ENTRY_MODE, (uint8_t[]) { - SSD1681_PARAM_DATA_ENTRY_MODE_3 + SSD1681_PARAM_DATA_ENTRY_MODE_3 }, 1), TAG, "SSD1681_CMD_DATA_ENTRY_MODE err"); } - if((!(epaper_panel->_mirror_x)) && (epaper_panel->_mirror_y)) { + if ((!(epaper_panel->_mirror_x)) && (epaper_panel->_mirror_y)) { // --- Cursor Settings ESP_RETURN_ON_ERROR(epaper_set_area(epaper_panel->io, x_start, y_start + len_y - 1, x_end, y_end + len_y - 1), TAG, - "epaper_set_area() error"); + "epaper_set_area() error"); ESP_RETURN_ON_ERROR(epaper_set_cursor(epaper_panel->io, x_start, y_start + len_y - 1), TAG, - "epaper_set_cursor() error"); + "epaper_set_cursor() error"); // --- Data Entry Sequence Setting ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(epaper_panel->io, SSD1681_CMD_DATA_ENTRY_MODE, (uint8_t[]) { - SSD1681_PARAM_DATA_ENTRY_MODE_1 + SSD1681_PARAM_DATA_ENTRY_MODE_1 }, 1), TAG, "SSD1681_CMD_DATA_ENTRY_MODE err"); } - if(((epaper_panel->_mirror_x)) && (!(epaper_panel->_mirror_y))) { + if (((epaper_panel->_mirror_x)) && (!(epaper_panel->_mirror_y))) { // --- Cursor Settings ESP_RETURN_ON_ERROR(epaper_set_area(epaper_panel->io, x_start, y_start + len_y - 1, x_end, y_end + len_y - 1), TAG, "epaper_set_area() error"); @@ -467,10 +476,10 @@ epaper_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x "epaper_set_cursor() error"); // --- Data Entry Sequence Setting ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(epaper_panel->io, SSD1681_CMD_DATA_ENTRY_MODE, (uint8_t[]) { - SSD1681_PARAM_DATA_ENTRY_MODE_1 + SSD1681_PARAM_DATA_ENTRY_MODE_1 }, 1), TAG, "SSD1681_CMD_DATA_ENTRY_MODE err"); } - if(((epaper_panel->_mirror_x)) && (epaper_panel->_mirror_y)) { + if (((epaper_panel->_mirror_x)) && (epaper_panel->_mirror_y)) { // --- Cursor Settings ESP_RETURN_ON_ERROR(epaper_set_area(epaper_panel->io, x_start, y_start, x_end, y_end), TAG, "epaper_set_area() error"); @@ -478,18 +487,17 @@ epaper_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x "epaper_set_cursor() error"); // --- Data Entry Sequence Setting ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(epaper_panel->io, SSD1681_CMD_DATA_ENTRY_MODE, (uint8_t[]) { - SSD1681_PARAM_DATA_ENTRY_MODE_3 + SSD1681_PARAM_DATA_ENTRY_MODE_3 }, 1), TAG, "SSD1681_CMD_DATA_ENTRY_MODE err"); } // --- Send bitmap to e-Paper VRAM - if(epaper_panel->bitmap_color == SSD1681_EPAPER_BITMAP_BLACK) { + if (epaper_panel->bitmap_color == SSD1681_EPAPER_BITMAP_BLACK) { ESP_RETURN_ON_ERROR(panel_epaper_set_vram(epaper_panel->io, (uint8_t *) (epaper_panel->_framebuffer), NULL, - (len_x * len_y / 8)), + (len_x * len_y / 8)), TAG, "panel_epaper_set_vram error"); - } - else if(epaper_panel->bitmap_color == SSD1681_EPAPER_BITMAP_RED) { + } else if (epaper_panel->bitmap_color == SSD1681_EPAPER_BITMAP_RED) { ESP_RETURN_ON_ERROR(panel_epaper_set_vram(epaper_panel->io, NULL, (uint8_t *) (epaper_panel->_framebuffer), - (len_x * len_y / 8)), + (len_x * len_y / 8)), TAG, "panel_epaper_set_vram error"); } // --- Refresh the display, show image in VRAM @@ -500,16 +508,16 @@ epaper_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x static esp_err_t epaper_panel_invert_color(esp_lcd_panel_t *panel, bool invert_color_data) { - epaper_panel_t * epaper_panel = __containerof(panel, epaper_panel_t, base); + epaper_panel_t *epaper_panel = __containerof(panel, epaper_panel_t, base); epaper_panel->_invert_color = invert_color_data; return ESP_OK; } static esp_err_t epaper_panel_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y) { - epaper_panel_t * epaper_panel = __containerof(panel, epaper_panel_t, base); - if(mirror_y) { - if(epaper_panel->_non_copy_mode) { + epaper_panel_t *epaper_panel = __containerof(panel, epaper_panel_t, base); + if (mirror_y) { + if (epaper_panel->_non_copy_mode) { ESP_LOGE(TAG, "mirror_y is unavailable when enabling non-copy mode"); return ESP_ERR_INVALID_ARG; } @@ -522,9 +530,9 @@ static esp_err_t epaper_panel_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool static esp_err_t epaper_panel_swap_xy(esp_lcd_panel_t *panel, bool swap_axes) { - epaper_panel_t * epaper_panel = __containerof(panel, epaper_panel_t, base); - if(swap_axes) { - if(epaper_panel->_non_copy_mode) { + epaper_panel_t *epaper_panel = __containerof(panel, epaper_panel_t, base); + if (swap_axes) { + if (epaper_panel->_non_copy_mode) { ESP_LOGE(TAG, "swap_xy is unavailable when enabling non-copy mode"); return ESP_ERR_INVALID_ARG; } @@ -535,7 +543,7 @@ static esp_err_t epaper_panel_swap_xy(esp_lcd_panel_t *panel, bool swap_axes) static esp_err_t epaper_panel_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap) { - epaper_panel_t * epaper_panel = __containerof(panel, epaper_panel_t, base); + epaper_panel_t *epaper_panel = __containerof(panel, epaper_panel_t, base); epaper_panel->gap_x = x_gap; epaper_panel->gap_y = y_gap; return ESP_OK; @@ -543,22 +551,21 @@ static esp_err_t epaper_panel_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_g static esp_err_t epaper_panel_disp_on_off(esp_lcd_panel_t *panel, bool on_off) { - epaper_panel_t * epaper_panel = __containerof(panel, epaper_panel_t, base); + epaper_panel_t *epaper_panel = __containerof(panel, epaper_panel_t, base); esp_lcd_panel_io_handle_t io = epaper_panel->io; - if(on_off) { + if (on_off) { // Turn on display ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(epaper_panel->io, SSD1681_CMD_SET_DISP_UPDATE_CTRL, (uint8_t[]) { - SSD1681_PARAM_DISP_UPDATE_MODE_1 + SSD1681_PARAM_DISP_UPDATE_MODE_1 }, 1), TAG, "SSD1681_CMD_SET_DISP_UPDATE_CTRL err"); ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, SSD1681_CMD_ACTIVE_DISP_UPDATE_SEQ, NULL, 0), TAG, "SSD1681_CMD_ACTIVE_DISP_UPDATE_SEQ err"); panel_epaper_wait_busy(panel); - } - else { + } else { // Sleep mode, BUSY pin will keep HIGH after entering sleep mode // Perform reset and re-run init to resume the display ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(epaper_panel->io, SSD1681_CMD_SLEEP_CTRL, (uint8_t[]) { - SSD1681_PARAM_SLEEP_MODE_1 + SSD1681_PARAM_SLEEP_MODE_1 }, 1), TAG, "SSD1681_CMD_SLEEP_CTRL err"); // BUSY pin will stay HIGH, so do not call panel_epaper_wait_busy() here } @@ -566,12 +573,13 @@ static esp_err_t epaper_panel_disp_on_off(esp_lcd_panel_t *panel, bool on_off) return ESP_OK; } -static esp_err_t process_bitmap(esp_lcd_panel_t *panel, int len_x, int len_y, int buffer_size, const void *color_data) { - epaper_panel_t * epaper_panel = __containerof(panel, epaper_panel_t, base); +static esp_err_t process_bitmap(esp_lcd_panel_t *panel, int len_x, int len_y, int buffer_size, const void *color_data) +{ + epaper_panel_t *epaper_panel = __containerof(panel, epaper_panel_t, base); // --- Convert image according to configuration - if((!(epaper_panel->_mirror_x)) && (!(epaper_panel->_mirror_y))) { + if ((!(epaper_panel->_mirror_x)) && (!(epaper_panel->_mirror_y))) { if (!(epaper_panel->_non_copy_mode)) { - if(epaper_panel->_swap_xy) { + if (epaper_panel->_swap_xy) { memset(epaper_panel->_framebuffer, 0, 200 * 200 / 8); for (int i = 0; i < buffer_size * 8; i++) { uint8_t bitmap_byte = ((uint8_t *) (color_data))[i / 8]; @@ -585,8 +593,8 @@ static esp_err_t process_bitmap(esp_lcd_panel_t *panel, int len_x, int len_y, in } } } - if((!(epaper_panel->_mirror_x)) && (epaper_panel->_mirror_y)) { - if(epaper_panel->_swap_xy) { + if ((!(epaper_panel->_mirror_x)) && (epaper_panel->_mirror_y)) { + if (epaper_panel->_swap_xy) { memset((epaper_panel->_framebuffer), 0, 200 * 200 / 8); for (int i = 0; i < buffer_size * 8; i++) { uint8_t bitmap_byte = ((uint8_t *) (color_data))[i / 8]; @@ -595,13 +603,13 @@ static esp_err_t process_bitmap(esp_lcd_panel_t *panel, int len_x, int len_y, in } } else { for (int i = 0; i < buffer_size; i++) { - (epaper_panel->_framebuffer)[buffer_size - i - 1] = byte_reverse(((uint8_t*)(color_data))[i]); + (epaper_panel->_framebuffer)[buffer_size - i - 1] = byte_reverse(((uint8_t *)(color_data))[i]); } } } - if(((epaper_panel->_mirror_x)) && (!(epaper_panel->_mirror_y))) { + if (((epaper_panel->_mirror_x)) && (!(epaper_panel->_mirror_y))) { if (!(epaper_panel->_non_copy_mode)) { - if(epaper_panel->_swap_xy) { + if (epaper_panel->_swap_xy) { memset((epaper_panel->_framebuffer), 0, 200 * 200 / 8); for (int i = 0; i < buffer_size * 8; i++) { uint8_t bitmap_byte = ((uint8_t *) (color_data))[i / 8]; @@ -615,8 +623,8 @@ static esp_err_t process_bitmap(esp_lcd_panel_t *panel, int len_x, int len_y, in } } } - if(((epaper_panel->_mirror_x)) && (epaper_panel->_mirror_y)) { - if(epaper_panel->_swap_xy) { + if (((epaper_panel->_mirror_x)) && (epaper_panel->_mirror_y)) { + if (epaper_panel->_swap_xy) { memset((epaper_panel->_framebuffer), 0, 200 * 200 / 8); for (int i = 0; i < buffer_size * 8; i++) { uint8_t bitmap_byte = ((uint8_t *) (color_data))[i / 8]; @@ -625,7 +633,7 @@ static esp_err_t process_bitmap(esp_lcd_panel_t *panel, int len_x, int len_y, in } } else { for (int i = 0; i < buffer_size; i++) { - (epaper_panel->_framebuffer)[buffer_size - i - 1] = byte_reverse(((uint8_t*)(color_data))[i]); + (epaper_panel->_framebuffer)[buffer_size - i - 1] = byte_reverse(((uint8_t *)(color_data))[i]); } } } @@ -636,8 +644,8 @@ static esp_err_t process_bitmap(esp_lcd_panel_t *panel, int len_x, int len_y, in static inline uint8_t byte_reverse(uint8_t data) { static uint8_t _4bit_reverse_lut[] = { - 0x00, 0x08, 0x04, 0x0C, 0x02, 0x0A, 0x06, 0x0E, - 0x01, 0x09, 0x05, 0x0D, 0x03, 0x0B, 0x07, 0x0F + 0x00, 0x08, 0x04, 0x0C, 0x02, 0x0A, 0x06, 0x0E, + 0x01, 0x09, 0x05, 0x0D, 0x03, 0x0B, 0x07, 0x0F }; uint8_t result = 0x00; // Reverse low 4 bits diff --git a/components/lcd/esp_lcd_ssd1681/include/esp_lcd_ssd1681_commands.h b/components/lcd/esp_lcd_ssd1681/esp_lcd_ssd1681_commands.h similarity index 100% rename from components/lcd/esp_lcd_ssd1681/include/esp_lcd_ssd1681_commands.h rename to components/lcd/esp_lcd_ssd1681/esp_lcd_ssd1681_commands.h diff --git a/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/CMakeLists.txt b/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/CMakeLists.txt new file mode 100644 index 00000000..8f46648e --- /dev/null +++ b/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(epaper_demo) diff --git a/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/README.md b/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/README.md new file mode 100644 index 00000000..bc117448 --- /dev/null +++ b/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/README.md @@ -0,0 +1,103 @@ +# e-Paper Example + +The SSD1681 e-paper display drive uses [esp_lcd](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/lcd.html) APIs and provides some additional APIs because of the specificity of e-paper panel. + +This example shows how to use SSD1681 e-paper display driver from Component manager and will draw a few bitmaps to the e-paper panel using the SSD1681 e-paper display driver. + +## How to use the example + +### Hardware Required + +* An ESP development board +* An SSD1681 e-paper panel, with SPI interface +* An USB cable for power supply and programming + +### Hardware Connection + +The connection between ESP Board and the LCD is as follows: + +``` + ESP Board SSD1681 e-Paper Panel +┌──────────────────────────────┐ ┌────────────────────┐ +│ GND ├─────────────►│ GND │ +│ │ │ │ +│ 3V3 ├─────────────►│ VCC │ +│ │ │ │ +│ EXAMPLE_PIN_NUM_SCLK ├─────────────►│ CLK │ +│ │ │ │ +│ EXAMPLE_PIN_NUM_MOSI ├─────────────►│ DIN │ +│ │ │ │ +│ EXAMPLE_PIN_NUM_EPD_RST ├─────────────►│ RST │ +│ │ │ │ +│ EXAMPLE_PIN_NUM_EPD_DC ├─────────────►│ DC │ +│ │ │ │ +│ EXAMPLE_PIN_NUM_EPD_CS ├─────────────►│ CS │ +│ │ │ │ +│ EXAMPLE_PIN_NUM_EPD_BUSY│◄─────────────│ BUSY │ +└──────────────────────────────┘ └────────────────────┘ +``` + +The GPIO number used by this example can be changed in [main.c](main/main.c). + +### Software configuration + +- Change all the `EXAMPLE_PIN` macro definition according to your hardware connection. +- If you are not using waveshare 1.54 inch V2 e-paper panel, please use the waveform lut provided by your panel vendor instead of using the demo built-in ones, or just simply comment the `epaper_panel_set_custom_lut` call and use the panel built-in waveform lut. + +### Build and Flash + +Run `idf.py -p PORT build flash monitor` to build, flash and monitor the project. + +The first time you run `idf.py` for the example will cost extra time as the build system needs to address the component dependencies and downloads the missing components from registry into `managed_components` folder. + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects. + +### Example Output + +```bash +... +I (310) epaper_demo_plain: Initializing SPI Bus... +I (320) epaper_demo_plain: Initializing panel IO... +I (330) gpio: GPIO[9]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 +I (330) epaper_demo_plain: Creating SSD1681 panel... +I (340) gpio: GPIO[4]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 +I (350) lcd_panel.epaper: Add handler for GPIO 18 +I (350) gpio: GPIO[18]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 0| Pulldown: 1| Intr:2 +I (360) epaper_demo_plain: Resetting e-Paper display... +I (490) epaper_demo_plain: Initializing e-Paper display... +I (610) epaper_demo_plain: Turning e-Paper display on... +I (720) epaper_demo_plain: Drawing bitmap... +... +``` + +## Show Custom Bitmap + +As you could see from the demo, each bitmap is stored as an array. If you want to display your custom image, you need to convert your image to a bitmap array first. + +Monochrome as the panel is, every bit (not byte) of the array corresponds to a pixel of the e-paper panel. + +You could convert your image to bitmap array by the following steps: +- Resize your image to the size you want. +- Go to the [LVGL Online Image Converter website](https://lvgl.io/tools/imageconverter) +- Upload your resized image. +- Select the Color format to `CF_ALPHA_1_BIT`, select the Output format to `C array`. +- You could select the `Dither images (can improve quality)` to get better (gray-scale like) image quality +- Do not select the `Output in big-endian format` option. +- Click `Convert` and you get a `.c` file containing the bitmap array. + +There are plenty of software alternative with such functionality, please pay attention to the scan mode if you prefer to use them. + +The driver writes bitmap array to the vram in the sequence below by default: + +![scan_mode](scan_mode.svg) + +Please make sure that the scan mode identical to the image above. Otherwise you might have to mirror or rotate the bitmap using software implemented functions. + +## Troubleshooting + +* Why the e-paper is not displaying properly? + * Maybe your GPIO pin num is not correctly set, check in [main.c](main/main.c). + * Maybe your waveform lut is incorrect, try stop using your custom waveform lut. + diff --git a/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/main/CMakeLists.txt b/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/main/CMakeLists.txt new file mode 100644 index 00000000..1af3548a --- /dev/null +++ b/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" "img_bitmap.c" + INCLUDE_DIRS "") diff --git a/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/main/idf_component.yml b/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/main/idf_component.yml new file mode 100644 index 00000000..ff742c7e --- /dev/null +++ b/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/main/idf_component.yml @@ -0,0 +1,7 @@ +dependencies: + idf: ">=5.0" + esp_lcd_ssd1681: + # I am specifying the path of the component because the component + # had not been published to the ESP Component Registry by the time + # I write this example. + path: "../../../" \ No newline at end of file diff --git a/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/main/img_bitmap.c b/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/main/img_bitmap.c new file mode 100644 index 00000000..79fc95ac --- /dev/null +++ b/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/main/img_bitmap.c @@ -0,0 +1,456 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include "img_bitmap.h" +const uint8_t BITMAP_200_200[] = { /* 0X00,0X01,0XC8,0X00,0XC8,0X00, */ + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X3F, 0XFF, 0XFC, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X1F, 0XFF, 0XFF, 0XFF, 0XF8, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XC0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X3F, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XF0, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X7F, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFE, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X80, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X07, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X1F, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XF8, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X7F, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFE, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X07, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XE0, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X1F, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XF8, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X3F, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFC, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, + 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0X0F, 0XFF, 0XF0, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XE0, 0X00, 0X00, 0X00, 0X00, + 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XFF, 0XFF, 0XFF, 0XE0, 0X0F, 0XFF, 0XF0, 0X07, + 0XFF, 0XFF, 0XFF, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X1F, + 0XFF, 0XFF, 0XFF, 0XFE, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X7F, 0XFF, 0XFF, 0XFF, 0XF8, 0X00, 0X00, + 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X7F, 0XFF, 0XFF, 0XFF, 0XF0, 0X00, 0X0F, 0XFF, + 0XF0, 0X00, 0X0F, 0XFF, 0XFF, 0XFF, 0XFE, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, + 0X00, 0XFF, 0XFF, 0XFF, 0XFF, 0X80, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X01, 0XFF, 0XFF, 0XFF, 0XFF, + 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0XFF, 0XFC, 0X00, 0X00, + 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X3F, 0XFF, 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, + 0X00, 0X00, 0X03, 0XFF, 0XFF, 0XFF, 0XF0, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X0F, 0XFF, + 0XFF, 0XFF, 0XC0, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFF, 0XFF, 0XC0, + 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X03, 0XFF, 0XFF, 0XFF, 0XE0, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, + 0X00, 0XFF, 0XFF, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X1F, 0XFF, 0XFF, + 0XFC, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X3F, 0XFF, 0XFF, 0XF8, 0X00, 0X00, + 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X3F, 0XFF, 0XFF, 0XF8, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, + 0X00, 0X00, 0X00, 0X1F, 0XFF, 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X7F, + 0XFF, 0XFF, 0XE0, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFF, 0XFE, + 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X0F, + 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, + 0X01, 0XFF, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0XFF, + 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X03, 0XFF, 0XFF, 0XFE, 0X00, 0X00, 0X00, + 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X7F, 0XFF, 0XFF, 0XC0, 0X00, 0X00, 0X01, 0X80, + 0X00, 0X00, 0X07, 0XFF, 0XFF, 0XF8, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, + 0X00, 0X1F, 0XFF, 0XFF, 0XE0, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X0F, 0XFF, 0XFF, 0XF0, 0X00, + 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XFF, 0XF0, 0X00, 0X00, + 0X01, 0X80, 0X00, 0X00, 0X1F, 0XFF, 0XFF, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, + 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFF, 0XF8, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X1F, 0XFF, 0XFF, + 0XC0, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X03, 0XFF, 0XFF, 0XF8, + 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X3F, 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, + 0XF0, 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0XFC, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X7F, + 0XFF, 0XFE, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X7F, + 0XFF, 0XFE, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0XFF, 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X3F, 0XFF, 0XFF, 0X00, 0X00, 0X01, 0X80, 0X00, + 0X00, 0XFF, 0XFF, 0XF8, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X1F, 0XFF, 0XFF, 0X00, 0X00, 0X01, 0X80, 0X00, 0X01, 0XFF, 0XFF, 0XF0, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XFF, 0X80, 0X00, 0X01, + 0X80, 0X00, 0X03, 0XFF, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XFF, 0XC0, 0X00, 0X01, 0X80, 0X00, 0X03, 0XFF, 0XFF, 0XE0, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFF, 0XC0, + 0X00, 0X01, 0X80, 0X00, 0X07, 0XFF, 0XFF, 0XC0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, 0XFF, 0XFF, 0XE0, 0X00, 0X01, 0X80, 0X00, 0X0F, 0XFF, 0XFF, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF, + 0XFF, 0XF0, 0X00, 0X01, 0X80, 0X00, 0X0F, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, + 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0XF0, 0X00, 0X01, 0X80, 0X00, 0X1F, + 0XFF, 0XFE, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X7F, 0XFF, 0XF8, 0X00, 0X01, 0X80, 0X00, 0X1F, 0XFF, 0XFE, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X7F, 0XFF, 0XF8, 0X00, 0X01, 0X80, + 0X00, 0X3F, 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X3F, 0XFF, 0XFC, 0X00, 0X01, 0X80, 0X00, 0X3F, 0XFF, 0XF8, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X1F, 0XFF, 0XFC, 0X00, + 0X01, 0X80, 0X00, 0X7F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XFE, 0X00, 0X01, 0X80, 0X00, 0X7F, 0XFF, 0XF0, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, + 0XFE, 0X00, 0X01, 0X80, 0X00, 0XFF, 0XFF, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, + 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFF, 0X00, 0X01, 0X80, 0X00, 0XFF, 0XFF, + 0XE0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X07, 0XFF, 0XFF, 0X00, 0X01, 0X80, 0X01, 0XFF, 0XFF, 0XC0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, 0XFF, 0XFF, 0X80, 0X01, 0X80, 0X01, + 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X01, 0XFF, 0XFF, 0X80, 0X01, 0X80, 0X03, 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0XC0, 0X01, + 0X80, 0X03, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0XC0, 0X01, 0X80, 0X07, 0XFF, 0XFF, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, + 0XE0, 0X01, 0X80, 0X07, 0XFF, 0XFE, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X7F, 0XFF, 0XE0, 0X01, 0X80, 0X07, 0XFF, 0XFE, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X7F, 0XFF, 0XE0, 0X01, 0X80, 0X0F, 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, + 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X3F, 0XFF, 0XF0, 0X01, 0X80, 0X0F, 0XFF, + 0XFC, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X3F, 0XFF, 0XF0, 0X01, 0X80, 0X0F, 0XFF, 0XF8, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X1F, 0XFF, 0XF0, 0X01, 0X80, + 0X1F, 0XFF, 0XF8, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X1F, 0XFF, 0XF8, 0X01, 0X80, 0X1F, 0XFF, 0XF8, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X1F, 0XFF, 0XF8, + 0X01, 0X80, 0X1F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF8, 0X01, 0X80, 0X3F, 0XFF, 0XF0, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, + 0XFF, 0XFC, 0X01, 0X80, 0X3F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, + 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XFC, 0X01, 0X80, 0X3F, 0XFF, 0XE0, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X07, 0XFF, 0XFC, 0X01, 0X80, 0X3F, 0XFF, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFC, 0X01, 0X80, 0X7F, + 0XFF, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFE, 0X01, 0X80, 0X7F, 0XFF, 0XC0, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, 0XFF, 0XFE, 0X01, + 0X80, 0X7F, 0XFF, 0XC0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, 0XFF, 0XFE, 0X01, 0X80, 0X7F, 0XFF, 0XC0, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, 0XFF, + 0XFE, 0X01, 0X80, 0X7F, 0XFF, 0XC0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, 0XFF, 0XFE, 0X01, 0X80, 0XFF, 0XFF, 0X80, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X01, 0XFF, 0XFF, 0X01, 0X80, 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, + 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0X01, 0X80, 0XFF, 0XFF, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X01, 0XFF, 0XFF, 0X01, 0X80, 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0X01, 0X80, + 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0X01, 0X80, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, + 0X01, 0X80, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0X01, 0X81, 0XFF, 0XFF, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0XFF, 0XFF, 0X81, 0X81, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, + 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0X81, 0X81, 0XFF, 0XFF, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0XFF, 0XFF, 0X81, 0X81, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0X81, 0X81, 0XFF, + 0XFF, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0X81, 0X81, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0X81, + 0X81, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0X81, 0X81, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X1F, 0XFF, 0XF8, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XFF, + 0XFF, 0X81, 0X81, 0XFF, 0XFE, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X3F, 0XFF, 0XFC, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X7F, 0XFF, 0X81, 0X81, 0XFF, 0XFE, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X7F, 0XFF, 0XFE, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X7F, 0XFF, 0X81, 0X81, 0XFF, 0XFE, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XFF, + 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X7F, 0XFF, 0X81, 0X81, 0XFF, 0XFE, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X7F, 0XFF, 0X81, 0X81, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X03, 0XFF, 0XFF, 0XFF, 0XC0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0X81, 0X81, + 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFF, 0XFF, 0XE0, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0X81, 0X81, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X0F, 0XFF, 0XFF, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, + 0X81, 0X81, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X1F, 0XFF, 0XFF, 0XFF, 0XF8, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0X81, 0X81, 0XFF, 0XFF, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X3F, 0XFF, 0XFF, 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0XFF, 0XFF, 0X81, 0X81, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X7F, 0XFF, 0XFF, + 0XFF, 0XFE, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0X81, 0X81, 0XFF, 0XFF, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0XFF, 0XFF, 0X81, 0X81, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0X81, 0X80, 0XFF, + 0XFF, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XC0, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0X01, 0X80, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X07, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0X01, + 0X80, 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XF0, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0X01, 0X80, 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X1F, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XF8, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF, + 0XFF, 0X01, 0X80, 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X3F, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0X01, 0X80, 0XFF, 0XFF, 0X80, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X7F, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFE, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X01, 0XFF, 0XFF, 0X01, 0X80, 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0X01, 0X80, 0X7F, 0XFF, + 0XC0, 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X80, 0X00, 0X00, + 0X00, 0X00, 0X03, 0XFF, 0XFE, 0X01, 0X80, 0X7F, 0XFF, 0XC0, 0X00, 0X00, 0X00, 0X00, 0X03, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XC0, 0X00, 0X00, 0X00, 0X00, 0X03, 0XFF, 0XFE, 0X01, 0X80, + 0X7F, 0XFF, 0XC0, 0X00, 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XE0, + 0X00, 0X00, 0X00, 0X00, 0X03, 0XFF, 0XFE, 0X01, 0X80, 0X7F, 0XFF, 0XC0, 0X00, 0X00, 0X00, 0X00, + 0X0F, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X03, 0XFF, 0XFE, + 0X01, 0X80, 0X7F, 0XFF, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X1F, 0XFF, 0XFF, 0XCF, 0XFF, 0XF3, 0XFF, + 0XFF, 0XF8, 0X00, 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFE, 0X01, 0X80, 0X3F, 0XFF, 0XE0, 0X00, 0X00, + 0X00, 0X00, 0X3F, 0XFF, 0XFF, 0X8F, 0XFF, 0XF1, 0XFF, 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X00, 0X07, + 0XFF, 0XFC, 0X01, 0X80, 0X3F, 0XFF, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X7F, 0XFF, 0XFF, 0X0F, 0XFF, + 0XF0, 0XFF, 0XFF, 0XFE, 0X00, 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFC, 0X01, 0X80, 0X3F, 0XFF, 0XF0, + 0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0XFE, 0X0F, 0XFF, 0XF0, 0X7F, 0XFF, 0XFF, 0X00, 0X00, 0X00, + 0X00, 0X0F, 0XFF, 0XFC, 0X01, 0X80, 0X3F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0XFC, + 0X0F, 0XFF, 0XF0, 0X3F, 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XFC, 0X01, 0X80, 0X1F, + 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X03, 0XFF, 0XFF, 0XF8, 0X0F, 0XFF, 0XF0, 0X1F, 0XFF, 0XFF, 0XC0, + 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF8, 0X01, 0X80, 0X1F, 0XFF, 0XF8, 0X00, 0X00, 0X00, 0X07, 0XFF, + 0XFF, 0XF0, 0X0F, 0XFF, 0XF0, 0X0F, 0XFF, 0XFF, 0XE0, 0X00, 0X00, 0X00, 0X1F, 0XFF, 0XF8, 0X01, + 0X80, 0X1F, 0XFF, 0XF8, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XFF, 0XE0, 0X0F, 0XFF, 0XF0, 0X07, 0XFF, + 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X1F, 0XFF, 0XF8, 0X01, 0X80, 0X0F, 0XFF, 0XF8, 0X00, 0X00, 0X00, + 0X1F, 0XFF, 0XFF, 0XC0, 0X0F, 0XFF, 0XF0, 0X03, 0XFF, 0XFF, 0XF8, 0X00, 0X00, 0X00, 0X1F, 0XFF, + 0XF0, 0X01, 0X80, 0X0F, 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X3F, 0XFF, 0XFF, 0X80, 0X0F, 0XFF, 0XF0, + 0X01, 0XFF, 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X3F, 0XFF, 0XF0, 0X01, 0X80, 0X0F, 0XFF, 0XFC, 0X00, + 0X00, 0X00, 0X7F, 0XFF, 0XFF, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0XFF, 0XFF, 0XFE, 0X00, 0X00, 0X00, + 0X3F, 0XFF, 0XF0, 0X01, 0X80, 0X07, 0XFF, 0XFE, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0XFE, 0X00, 0X0F, + 0XFF, 0XF0, 0X00, 0X7F, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X7F, 0XFF, 0XE0, 0X01, 0X80, 0X07, 0XFF, + 0XFE, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0XFC, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X3F, 0XFF, 0XFF, 0X80, + 0X00, 0X00, 0X7F, 0XFF, 0XE0, 0X01, 0X80, 0X07, 0XFF, 0XFF, 0X00, 0X00, 0X03, 0XFF, 0XFF, 0XF8, + 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X1F, 0XFF, 0XFF, 0XC0, 0X00, 0X00, 0XFF, 0XFF, 0XE0, 0X01, 0X80, + 0X03, 0XFF, 0XFF, 0X00, 0X00, 0X07, 0XFF, 0XFF, 0XF0, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X0F, 0XFF, + 0XFF, 0XE0, 0X00, 0X00, 0XFF, 0XFF, 0XC0, 0X01, 0X80, 0X03, 0XFF, 0XFF, 0X80, 0X00, 0X0F, 0XFF, + 0XFF, 0XE0, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X07, 0XFF, 0XFF, 0XF0, 0X00, 0X01, 0XFF, 0XFF, 0XC0, + 0X01, 0X80, 0X01, 0XFF, 0XFF, 0X80, 0X00, 0X1F, 0XFF, 0XFF, 0XC0, 0X00, 0X0F, 0XFF, 0XF0, 0X00, + 0X03, 0XFF, 0XFF, 0XF8, 0X00, 0X01, 0XFF, 0XFF, 0X80, 0X01, 0X80, 0X01, 0XFF, 0XFF, 0XC0, 0X00, + 0X3F, 0XFF, 0XFF, 0X80, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X01, 0XFF, 0XFF, 0XFC, 0X00, 0X03, 0XFF, + 0XFF, 0X80, 0X01, 0X80, 0X00, 0XFF, 0XFF, 0XE0, 0X00, 0X7F, 0XFF, 0XFF, 0X00, 0X00, 0X0F, 0XFF, + 0XF0, 0X00, 0X00, 0XFF, 0XFF, 0XFE, 0X00, 0X07, 0XFF, 0XFF, 0X00, 0X01, 0X80, 0X00, 0XFF, 0XFF, + 0XE0, 0X00, 0XFF, 0XFF, 0XFE, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X7F, 0XFF, 0XFF, 0X00, + 0X07, 0XFF, 0XFF, 0X00, 0X01, 0X80, 0X00, 0X7F, 0XFF, 0XF0, 0X01, 0XFF, 0XFF, 0XFC, 0X00, 0X00, + 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X3F, 0XFF, 0XFF, 0X80, 0X0F, 0XFF, 0XFE, 0X00, 0X01, 0X80, 0X00, + 0X7F, 0XFF, 0XF0, 0X03, 0XFF, 0XFF, 0XF8, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X1F, 0XFF, + 0XFF, 0XC0, 0X0F, 0XFF, 0XFE, 0X00, 0X01, 0X80, 0X00, 0X3F, 0XFF, 0XF8, 0X07, 0XFF, 0XFF, 0XF0, + 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X0F, 0XFF, 0XFF, 0XE0, 0X1F, 0XFF, 0XFC, 0X00, 0X01, + 0X80, 0X00, 0X3F, 0XFF, 0XFC, 0X0F, 0XFF, 0XFF, 0XE0, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, + 0X07, 0XFF, 0XFF, 0XF0, 0X3F, 0XFF, 0XFC, 0X00, 0X01, 0X80, 0X00, 0X1F, 0XFF, 0XFE, 0X1F, 0XFF, + 0XFF, 0XC0, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X03, 0XFF, 0XFF, 0XF8, 0X7F, 0XFF, 0XF8, + 0X00, 0X01, 0X80, 0X00, 0X1F, 0XFF, 0XFE, 0X3F, 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X0F, 0XFF, 0XF0, + 0X00, 0X00, 0X01, 0XFF, 0XFF, 0XFC, 0X7F, 0XFF, 0XF8, 0X00, 0X01, 0X80, 0X00, 0X0F, 0XFF, 0XFF, + 0X7F, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0XFE, 0XFF, + 0XFF, 0XF0, 0X00, 0X01, 0X80, 0X00, 0X0F, 0XFF, 0XFF, 0XFF, 0XFF, 0XFE, 0X00, 0X00, 0X00, 0X0F, + 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X7F, 0XFF, 0XFF, 0XFF, 0XFF, 0XF0, 0X00, 0X01, 0X80, 0X00, 0X07, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X3F, 0XFF, + 0XFF, 0XFF, 0XFF, 0XE0, 0X00, 0X01, 0X80, 0X00, 0X03, 0XFF, 0XFF, 0XFF, 0XFF, 0XF8, 0X00, 0X00, + 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X1F, 0XFF, 0XFF, 0XFF, 0XFF, 0XC0, 0X00, 0X01, 0X80, + 0X00, 0X03, 0XFF, 0XFF, 0XFF, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, + 0X0F, 0XFF, 0XFF, 0XFF, 0XFF, 0XC0, 0X00, 0X01, 0X80, 0X00, 0X01, 0XFF, 0XFF, 0XFF, 0XFF, 0XE0, + 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFF, 0XFF, 0XFF, 0X80, 0X00, + 0X01, 0X80, 0X00, 0X00, 0XFF, 0XFF, 0XFF, 0XFF, 0XC0, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, + 0X00, 0X00, 0X03, 0XFF, 0XFF, 0XFF, 0XFF, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0XFF, 0XFF, 0XFF, + 0XFF, 0X80, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0XFF, 0XFF, + 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X7F, 0XFF, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, + 0XF0, 0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0XFF, 0XFE, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X3F, + 0XFF, 0XFF, 0XFE, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X7F, 0XFF, + 0XFF, 0XFC, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X1F, 0XFF, 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X00, + 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X3F, 0XFF, 0XFF, 0XF8, 0X00, 0X00, 0X01, 0X80, 0X00, + 0X00, 0X1F, 0XFF, 0XFF, 0XF8, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, + 0X1F, 0XFF, 0XFF, 0XF8, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X0F, 0XFF, 0XFF, 0XF0, 0X00, 0X00, + 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XFF, 0XF0, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X07, 0XFF, 0XFF, 0XF8, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, + 0X00, 0X00, 0X1F, 0XFF, 0XFF, 0XE0, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X03, 0XFF, 0XFF, 0XFE, + 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X7F, 0XFF, 0XFF, 0XC0, 0X00, + 0X00, 0X01, 0X80, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, + 0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0XFF, + 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0XFF, + 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X7F, 0XFF, 0XFF, 0XE0, 0X00, 0X00, 0X00, 0X0F, + 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFF, 0XFE, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, + 0X00, 0X3F, 0XFF, 0XFF, 0XF8, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X1F, 0XFF, + 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X1F, 0XFF, 0XFF, 0XFC, 0X00, 0X00, + 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X3F, 0XFF, 0XFF, 0XF8, 0X00, 0X00, 0X00, 0X01, 0X80, + 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X00, + 0XFF, 0XFF, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFF, 0XFF, + 0XC0, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X03, 0XFF, 0XFF, 0XFF, 0XE0, 0X00, 0X00, 0X00, + 0X01, 0X80, 0X00, 0X00, 0X00, 0X03, 0XFF, 0XFF, 0XFF, 0XF0, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, + 0X00, 0X0F, 0XFF, 0XFF, 0XFF, 0XC0, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X01, 0XFF, + 0XFF, 0XFF, 0XFC, 0X00, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X00, 0X3F, 0XFF, 0XFF, 0XFF, 0X80, 0X00, + 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0XFF, 0XFF, 0X80, 0X00, 0X0F, 0XFF, + 0XF0, 0X00, 0X01, 0XFF, 0XFF, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, + 0X00, 0X7F, 0XFF, 0XFF, 0XFF, 0XF0, 0X00, 0X0F, 0XFF, 0XF0, 0X00, 0X0F, 0XFF, 0XFF, 0XFF, 0XFE, + 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X1F, 0XFF, 0XFF, 0XFF, 0XFE, 0X00, + 0X0F, 0XFF, 0XF0, 0X00, 0X7F, 0XFF, 0XFF, 0XFF, 0XF8, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, + 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XFF, 0XFF, 0XFF, 0XE0, 0X0F, 0XFF, 0XF0, 0X07, 0XFF, 0XFF, 0XFF, + 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0X0F, 0XFF, 0XF0, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X00, 0X00, 0X00, 0X1F, + 0XC0, 0X01, 0X80, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X3F, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X0C, 0X70, 0X01, 0X80, 0X60, 0X00, 0X00, 0X00, + 0X00, 0X1F, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XF8, 0X00, 0X00, + 0X00, 0X0C, 0X30, 0X01, 0X80, 0X60, 0X00, 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XE0, 0X00, 0X00, 0X00, 0X0C, 0X30, 0X01, 0X80, 0X60, 0X00, + 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X80, + 0X00, 0X00, 0X00, 0X0C, 0X30, 0X01, 0X80, 0X60, 0X00, 0X00, 0X00, 0X00, 0X00, 0X7F, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFE, 0X00, 0X00, 0X00, 0X00, 0X0C, 0X30, 0X01, 0X80, + 0X60, 0X00, 0X00, 0X00, 0X00, 0X00, 0X1F, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XF8, 0X00, 0X00, 0X00, 0X00, 0X0C, 0X60, 0X01, 0X80, 0X60, 0X00, 0X00, 0X00, 0X00, 0X00, 0X07, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XC0, + 0X01, 0X80, 0X60, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X00, 0X0C, 0X60, 0X01, 0X80, 0X60, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X7F, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFE, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X0C, 0X60, 0X01, 0X80, 0X60, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0C, 0X30, 0X01, 0X80, 0X60, 0X80, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X0C, 0X30, 0X01, 0X80, 0X61, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X3F, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0C, 0X18, 0X01, 0X80, 0X61, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XC0, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X1E, 0X1C, 0X01, 0X80, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X1F, 0XFF, 0XFF, 0XFF, 0XF8, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X3F, 0XFF, 0XFC, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, +}; + +const uint8_t BITMAP_128_64[] = { /* 0X01,0X01,0X40,0X00,0X80,0X00, */ + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X08, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X1F, 0XFE, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X1F, 0XFE, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X30, 0X06, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X30, 0X06, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X60, 0X03, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0XE0, 0X03, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X01, 0XC0, 0X03, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0XFF, 0X80, 0X03, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X07, 0XFF, 0X00, 0X03, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X1F, 0XFF, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X3F, 0XFF, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0XFF, 0X0F, 0XC0, 0X01, 0XC0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X01, 0XFF, 0XE3, 0XE0, 0X00, 0XC0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X07, 0XFF, 0XF1, 0XF8, 0X00, 0XC0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X0F, 0XFE, 0X01, 0XFC, 0X00, 0XC0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X1F, 0XF8, 0X00, 0XFF, 0X00, 0XC0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X7F, 0XE0, 0X00, 0X7F, 0X80, 0XC0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X1F, 0XFF, 0XFF, 0X00, 0X00, 0X7F, 0XE1, 0XC0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X81, 0XFF, 0XFF, 0XFC, 0X00, 0X00, 0X3F, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X83, 0XF8, 0X33, 0XC0, 0X00, 0X00, 0X0E, 0X7F, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X87, 0XF1, 0XE1, 0X80, 0X00, 0X00, 0X00, 0X3F, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X8F, 0XF9, 0XC0, 0X80, 0X00, 0X00, 0X00, 0X1E, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X8F, 0XFC, 0X40, 0X40, 0X00, 0X00, 0X00, 0X1E, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X9F, 0XFF, 0X80, 0X70, 0X00, 0X00, 0X00, 0X1E, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X9F, 0XFF, 0X80, 0X60, 0X00, 0X00, 0X00, 0X1E, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X9F, 0XFF, 0X80, 0X60, 0X00, 0X00, 0X00, 0X0E, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X9F, 0XF8, 0X40, 0X40, 0X00, 0X00, 0X00, 0X0E, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X9F, 0XF1, 0XC0, 0XC0, 0X00, 0X00, 0X00, 0X1E, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X9F, 0XF3, 0XE0, 0X80, 0X00, 0X00, 0X00, 0X1E, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X9F, 0XF1, 0XE1, 0X80, 0X00, 0X00, 0X00, 0X1E, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X9F, 0XF8, 0X01, 0X00, 0X00, 0X00, 0X00, 0X1E, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X8F, 0XFC, 0X7F, 0X00, 0X00, 0X00, 0X00, 0X3E, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X8F, 0XFF, 0XFF, 0XC0, 0X00, 0X00, 0X00, 0X7E, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X87, 0XFF, 0XFF, 0XF0, 0X00, 0X00, 0X00, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X83, 0XFF, 0XFF, 0XFE, 0X00, 0X00, 0X3F, 0XFF, 0XC0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0XFF, 0XFF, 0XFF, 0X80, 0X01, 0XFF, 0XC1, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X1F, 0XFF, 0XFF, 0XE0, 0X03, 0XF8, 0X00, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X01, 0XFF, 0XFC, 0X07, 0X80, 0X00, 0X60, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X7F, 0XFF, 0X1F, 0X80, 0X00, 0X60, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X3F, 0XFF, 0XDD, 0XE0, 0X00, 0X60, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X1F, 0XFF, 0XD8, 0X60, 0X00, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X07, 0XFE, 0X08, 0X60, 0X00, 0XC0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X03, 0XFF, 0XFC, 0XE0, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0XC0, 0X03, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X7F, 0XFF, 0XC0, 0X06, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X1F, 0XFF, 0X80, 0X0E, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X07, 0XFF, 0X80, 0X0C, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XC0, 0X18, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0XFC, 0X18, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X3E, 0X30, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X07, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, +}; + +const uint8_t BITMAP_64_128[] = { /* 0X00,0X01,0X40,0X00,0X80,0X00, */ + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X07, 0XF8, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X1F, 0XFE, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X3F, 0XFF, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X7F, 0XFF, 0X80, 0X00, 0X01, 0X80, 0X00, 0X00, 0XFF, 0XFF, 0X80, 0X00, 0X01, + 0X80, 0X00, 0X00, 0XFF, 0XFF, 0XC0, 0X00, 0X01, 0X80, 0X00, 0X00, 0XFF, 0XFF, 0XC0, 0X00, 0X01, + 0X80, 0X00, 0X00, 0XFF, 0XFF, 0XC0, 0X00, 0X01, 0X80, 0X00, 0X01, 0XFF, 0XFF, 0XE0, 0X00, 0X01, + 0X80, 0X00, 0X01, 0XDF, 0X8F, 0XE0, 0X00, 0X01, 0X80, 0X00, 0X01, 0X8F, 0X07, 0XE0, 0X00, 0X01, + 0X80, 0X00, 0X01, 0X87, 0X23, 0XE0, 0X00, 0X01, 0X80, 0X00, 0X01, 0XB7, 0X73, 0XE0, 0X00, 0X01, + 0X80, 0X00, 0X01, 0XB7, 0X73, 0XE0, 0X00, 0X01, 0X80, 0X00, 0X01, 0XB8, 0XF7, 0XE0, 0X00, 0X01, + 0X80, 0X00, 0X01, 0XE0, 0X37, 0XE0, 0X00, 0X01, 0X80, 0X00, 0X01, 0XC0, 0X07, 0XE0, 0X00, 0X01, + 0X80, 0X00, 0X01, 0X80, 0X07, 0XE0, 0X00, 0X01, 0X80, 0X00, 0X01, 0X80, 0X07, 0XE0, 0X00, 0X01, + 0X80, 0X00, 0X01, 0XC0, 0X07, 0XE0, 0X00, 0X01, 0X80, 0X00, 0X01, 0XE0, 0X1F, 0XF0, 0X00, 0X01, + 0X80, 0X00, 0X01, 0XF0, 0X73, 0XF0, 0X00, 0X01, 0X80, 0X00, 0X03, 0XCF, 0XC3, 0XF8, 0X00, 0X01, + 0X80, 0X00, 0X03, 0X87, 0X01, 0XFC, 0X00, 0X01, 0X80, 0X00, 0X07, 0X84, 0X01, 0XFE, 0X00, 0X01, + 0X80, 0X00, 0X0F, 0X80, 0X00, 0XFE, 0X00, 0X01, 0X80, 0X00, 0X1F, 0X80, 0X00, 0XFF, 0X00, 0X01, + 0X80, 0X00, 0X1F, 0X00, 0X00, 0XFF, 0X80, 0X01, 0X80, 0X00, 0X3F, 0X00, 0X00, 0X7F, 0XC0, 0X01, + 0X80, 0X00, 0X7E, 0X00, 0X00, 0X7F, 0XC0, 0X01, 0X80, 0X00, 0X7E, 0X00, 0X00, 0X3F, 0XE0, 0X01, + 0X80, 0X00, 0XFE, 0X00, 0X00, 0X3F, 0XE0, 0X01, 0X80, 0X01, 0XFC, 0X00, 0X00, 0X1F, 0XF0, 0X01, + 0X80, 0X01, 0XFC, 0X00, 0X00, 0X1F, 0XF0, 0X01, 0X80, 0X03, 0XF8, 0X00, 0X00, 0X1F, 0XF8, 0X01, + 0X80, 0X03, 0XF8, 0X00, 0X00, 0X0F, 0XF8, 0X01, 0X80, 0X03, 0XF0, 0X00, 0X00, 0X0E, 0XFC, 0X01, + 0X80, 0X07, 0XB0, 0X00, 0X00, 0X06, 0XFC, 0X01, 0X80, 0X07, 0XB0, 0X00, 0X00, 0X06, 0XFC, 0X01, + 0X80, 0X07, 0XB0, 0X00, 0X00, 0X00, 0XFC, 0X01, 0X80, 0X07, 0X90, 0X00, 0X00, 0X0E, 0XFC, 0X01, + 0X80, 0X07, 0XC0, 0X00, 0X00, 0X0F, 0XFC, 0X01, 0X80, 0X07, 0XC0, 0X00, 0X00, 0X1C, 0XFC, 0X01, + 0X80, 0X07, 0XE0, 0X00, 0X00, 0X38, 0X7C, 0X01, 0X80, 0X0F, 0XF8, 0X00, 0X00, 0X7C, 0X7C, 0X01, + 0X80, 0X1C, 0X7C, 0X00, 0X00, 0X7C, 0XFE, 0X01, 0X80, 0X38, 0X7F, 0X00, 0X00, 0X67, 0XE6, 0X01, + 0X80, 0XF0, 0X3F, 0X80, 0X00, 0XE7, 0X83, 0X01, 0X83, 0XC0, 0X1F, 0X80, 0X00, 0XE0, 0X03, 0X01, + 0X83, 0X00, 0X1F, 0XC0, 0X00, 0XE0, 0X03, 0X01, 0X83, 0X00, 0X0F, 0XC0, 0X00, 0XC0, 0X03, 0X81, + 0X83, 0X00, 0X07, 0XC0, 0X00, 0XC0, 0X01, 0XC1, 0X83, 0X00, 0X07, 0X80, 0X00, 0XC0, 0X00, 0XC1, + 0X83, 0X00, 0X03, 0X80, 0X01, 0XC0, 0X00, 0XC1, 0X83, 0X00, 0X01, 0XC0, 0X03, 0XC0, 0X00, 0XC1, + 0X83, 0X00, 0X01, 0XE0, 0X07, 0X80, 0X01, 0XC1, 0X83, 0X00, 0X00, 0XFE, 0X7F, 0X80, 0X07, 0X81, + 0X87, 0X00, 0X00, 0XFF, 0XFF, 0X80, 0X1E, 0X01, 0X83, 0XC0, 0X00, 0XFF, 0XFF, 0X80, 0X38, 0X01, + 0X83, 0XFE, 0X00, 0XFF, 0XFF, 0X80, 0X70, 0X01, 0X80, 0X3F, 0XC1, 0XE0, 0X01, 0XC0, 0XC0, 0X01, + 0X80, 0X01, 0XFF, 0X80, 0X01, 0XE3, 0XC0, 0X01, 0X80, 0X00, 0X7F, 0X00, 0X00, 0XFF, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X7E, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, + 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, +}; diff --git a/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/main/img_bitmap.h b/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/main/img_bitmap.h new file mode 100644 index 00000000..e51a99ed --- /dev/null +++ b/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/main/img_bitmap.h @@ -0,0 +1,20 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const uint8_t BITMAP_200_200[]; +extern const uint8_t BITMAP_128_64[]; +extern const uint8_t BITMAP_64_128[]; + +#ifdef __cplusplus +} +#endif diff --git a/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/main/main.c b/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/main/main.c new file mode 100644 index 00000000..c2b42fe6 --- /dev/null +++ b/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/main/main.c @@ -0,0 +1,256 @@ +/* + * SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include +#include +#include "sdkconfig.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_vendor.h" +#include "esp_lcd_panel_ssd1681.h" +#include "esp_lcd_panel_ops.h" +#include "driver/spi_common.h" +#include "driver/gpio.h" + +#include "ssd1681_waveshare_1in54_lut.h" +#include "img_bitmap.h" + +// SPI Bus +#define EPD_PANEL_SPI_CLK 1000000 +#define EPD_PANEL_SPI_CMD_BITS 8 +#define EPD_PANEL_SPI_PARAM_BITS 8 +#define EPD_PANEL_SPI_MODE 0 +// e-Paper GPIO +#define EXAMPLE_PIN_NUM_EPD_DC 9 +#define EXAMPLE_PIN_NUM_EPD_RST 4 +#define EXAMPLE_PIN_NUM_EPD_CS 10 +#define EXAMPLE_PIN_NUM_EPD_BUSY 18 +// e-Paper SPI +#define EXAMPLE_PIN_NUM_MOSI 7 +#define EXAMPLE_PIN_NUM_SCLK 6 + +static const char *TAG = "epaper_demo_plain"; + +static bool give_semaphore_in_isr(const esp_lcd_panel_handle_t handle, const void *edata, void *user_data) +{ + SemaphoreHandle_t *epaper_panel_semaphore_ptr = user_data; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + xSemaphoreGiveFromISR(*epaper_panel_semaphore_ptr, &xHigherPriorityTaskWoken); + if (xHigherPriorityTaskWoken == pdTRUE) { + portYIELD_FROM_ISR(); + return true; + } + return false; +} + +void app_main(void) +{ + esp_err_t ret; + // --- Init SPI Bus + ESP_LOGI(TAG, "Initializing SPI Bus..."); + spi_bus_config_t buscfg = { + .sclk_io_num = EXAMPLE_PIN_NUM_SCLK, + .mosi_io_num = EXAMPLE_PIN_NUM_MOSI, + .miso_io_num = -1, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .max_transfer_sz = SOC_SPI_MAXIMUM_BUFFER_SIZE + }; + ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO)); + // --- Init ESP_LCD IO + ESP_LOGI(TAG, "Initializing panel IO..."); + esp_lcd_panel_io_handle_t io_handle = NULL; + esp_lcd_panel_io_spi_config_t io_config = { + .dc_gpio_num = EXAMPLE_PIN_NUM_EPD_DC, + .cs_gpio_num = EXAMPLE_PIN_NUM_EPD_CS, + .pclk_hz = EPD_PANEL_SPI_CLK, + .lcd_cmd_bits = EPD_PANEL_SPI_CMD_BITS, + .lcd_param_bits = EPD_PANEL_SPI_PARAM_BITS, + .spi_mode = EPD_PANEL_SPI_MODE, + .trans_queue_depth = 10, + .on_color_trans_done = NULL + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t) SPI2_HOST, &io_config, &io_handle)); + // --- Create esp_lcd panel + ESP_LOGI(TAG, "Creating SSD1681 panel..."); + esp_lcd_ssd1681_config_t epaper_ssd1681_config = { + .busy_gpio_num = EXAMPLE_PIN_NUM_EPD_BUSY, + // NOTE: Enable this to reduce one buffer copy if you do not use swap-xy, mirror y or invert color + // since those operations are not supported by ssd1681 and are implemented by software + // Better use DMA-capable memory region, to avoid additional data copy + .non_copy_mode = false, + }; + esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = EXAMPLE_PIN_NUM_EPD_RST, + .flags.reset_active_high = false, + .vendor_config = &epaper_ssd1681_config + }; + esp_lcd_panel_handle_t panel_handle = NULL; + // NOTE: Please call gpio_install_isr_service() manually before esp_lcd_new_panel_ssd1681() + // because gpio_isr_handler_add() is called in esp_lcd_new_panel_ssd1681() + gpio_install_isr_service(0); + ret = esp_lcd_new_panel_ssd1681(io_handle, &panel_config, &panel_handle); + ESP_ERROR_CHECK(ret); + // --- Reset the display + ESP_LOGI(TAG, "Resetting e-Paper display..."); + ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); + vTaskDelay(100 / portTICK_PERIOD_MS); + // --- Initialize LCD panel + ESP_LOGI(TAG, "Initializing e-Paper display..."); + ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); + vTaskDelay(100 / portTICK_PERIOD_MS); + // Turn on the screen + ESP_LOGI(TAG, "Turning e-Paper display on..."); + ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true)); + // Set custom lut + // NOTE: Setting custom LUT is not necessary. Panel built-in LUT is used calling after esp_lcd_panel_disp_on_off() + // NOTE: Uncomment code below to see difference between full refresh & fast refresh + // NOTE: epaper_panel_set_custom_lut() must be called AFTER calling esp_lcd_panel_disp_on_off() + // static uint8_t fast_refresh_lut[] = SSD1681_WAVESHARE_1IN54_V2_LUT_FAST_REFRESH_KEEP; + // ESP_ERROR_CHECK(epaper_panel_set_custom_lut(panel_handle, fast_refresh_lut, 159)); + + vTaskDelay(100 / portTICK_PERIOD_MS); + + static SemaphoreHandle_t epaper_panel_semaphore; + epaper_panel_semaphore = xSemaphoreCreateBinary(); + xSemaphoreGive(epaper_panel_semaphore); + // --- Clear the VRAM of RED and BLACK + uint8_t *empty_bitmap = heap_caps_malloc(200 * 200 / 8, MALLOC_CAP_DMA); + memset(empty_bitmap, 0, 200 * 200 / 8); + epaper_panel_set_bitmap_color(panel_handle, SSD1681_EPAPER_BITMAP_RED); + esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, 200, 200, empty_bitmap); + epaper_panel_set_bitmap_color(panel_handle, SSD1681_EPAPER_BITMAP_BLACK); + esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, 200, 200, empty_bitmap); + + // --- Register the e-Paper refresh done callback + // cbs does not have to be static for ssd1681 driver, for the callback ptr is copied, not pointed + epaper_panel_callbacks_t cbs = { + .on_epaper_refresh_done = give_semaphore_in_isr, + }; + epaper_panel_register_event_callbacks(panel_handle, &cbs, &epaper_panel_semaphore); + + // --- Draw full-screen bitmap + ESP_LOGI(TAG, "Drawing bitmap..."); + ESP_LOGI(TAG, "Show image full-screen"); + + xSemaphoreTake(epaper_panel_semaphore, portMAX_DELAY); + ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, false, false)); + ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, false)); + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, false)); + ESP_LOGI(TAG, "Drawing bitmap..."); + ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, 200, 200, BITMAP_200_200)); + ESP_ERROR_CHECK(epaper_panel_refresh_screen(panel_handle)); + + xSemaphoreTake(epaper_panel_semaphore, portMAX_DELAY); + ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, false, true)); + ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, false)); + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, false)); + ESP_LOGI(TAG, "Drawing bitmap..."); + ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, 200, 200, BITMAP_200_200)); + ESP_ERROR_CHECK(epaper_panel_refresh_screen(panel_handle)); + + xSemaphoreTake(epaper_panel_semaphore, portMAX_DELAY); + ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, true, false)); + ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, false)); + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, false)); + ESP_LOGI(TAG, "Drawing bitmap..."); + ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, 200, 200, BITMAP_200_200)); + ESP_ERROR_CHECK(epaper_panel_refresh_screen(panel_handle)); + + xSemaphoreTake(epaper_panel_semaphore, portMAX_DELAY); + ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, true, true)); + ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, false)); + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, false)); + ESP_LOGI(TAG, "Drawing bitmap..."); + ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, 200, 200, BITMAP_200_200)); + ESP_ERROR_CHECK(epaper_panel_refresh_screen(panel_handle)); + + xSemaphoreTake(epaper_panel_semaphore, portMAX_DELAY); + ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, false, false)); + ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, false)); + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, true)); + ESP_LOGI(TAG, "Drawing bitmap..."); + ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, 200, 200, BITMAP_200_200)); + ESP_ERROR_CHECK(epaper_panel_refresh_screen(panel_handle)); + + xSemaphoreTake(epaper_panel_semaphore, portMAX_DELAY); + ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, false, true)); + ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, false)); + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, true)); + ESP_LOGI(TAG, "Drawing bitmap..."); + ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, 200, 200, BITMAP_200_200)); + ESP_ERROR_CHECK(epaper_panel_refresh_screen(panel_handle)); + + xSemaphoreTake(epaper_panel_semaphore, portMAX_DELAY); + ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, true, false)); + ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, false)); + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, true)); + ESP_LOGI(TAG, "Drawing bitmap..."); + ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, 200, 200, BITMAP_200_200)); + ESP_ERROR_CHECK(epaper_panel_refresh_screen(panel_handle)); + + xSemaphoreTake(epaper_panel_semaphore, portMAX_DELAY); + ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, true, true)); + ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, false)); + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, true)); + ESP_LOGI(TAG, "Drawing bitmap..."); + ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, 200, 200, BITMAP_200_200)); + ESP_ERROR_CHECK(epaper_panel_refresh_screen(panel_handle)); + + vTaskDelay(pdMS_TO_TICKS(5000)); + ESP_LOGI(TAG, "Go to sleep mode..."); + esp_lcd_panel_disp_on_off(panel_handle, false); + vTaskDelay(pdMS_TO_TICKS(3000)); + ESP_LOGI(TAG, "e-Paper resuming..."); + esp_lcd_panel_reset(panel_handle); + vTaskDelay(100 / portTICK_PERIOD_MS); + esp_lcd_panel_init(panel_handle); + vTaskDelay(100 / portTICK_PERIOD_MS); + esp_lcd_panel_disp_on_off(panel_handle, true); + vTaskDelay(100 / portTICK_PERIOD_MS); + + // NOTE: If you want to use a custom LUT, you'll have to set it again after resume + // --- Draw partial bitmap + ESP_LOGI(TAG, "Show image partial"); + xSemaphoreTake(epaper_panel_semaphore, portMAX_DELAY); + ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, false, false)); + ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, false)); + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, false)); + ESP_LOGI(TAG, "Drawing bitmap..."); + ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(panel_handle, 16, 0, 80, 128, BITMAP_64_128)); + ESP_ERROR_CHECK(epaper_panel_refresh_screen(panel_handle)); + + xSemaphoreTake(epaper_panel_semaphore, portMAX_DELAY); + ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, false, true)); + ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, false)); + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, false)); + ESP_LOGI(TAG, "Drawing bitmap..."); + ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(panel_handle, 16, 64, 144, 128, BITMAP_128_64)); + ESP_ERROR_CHECK(epaper_panel_refresh_screen(panel_handle)); + + xSemaphoreTake(epaper_panel_semaphore, portMAX_DELAY); + ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, true, false)); + ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, false)); + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, false)); + ESP_LOGI(TAG, "Drawing bitmap..."); + ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(panel_handle, 16, 64, 144, 128, BITMAP_128_64)); + ESP_ERROR_CHECK(epaper_panel_refresh_screen(panel_handle)); + + xSemaphoreTake(epaper_panel_semaphore, portMAX_DELAY); + ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, true, true)); + ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, false)); + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, false)); + ESP_LOGI(TAG, "Drawing bitmap..."); + ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(panel_handle, 16, 0, 144, 64, BITMAP_128_64)); + ESP_ERROR_CHECK(epaper_panel_refresh_screen(panel_handle)); + + vTaskDelay(pdMS_TO_TICKS(5000)); + ESP_LOGI(TAG, "Go to sleep mode..."); + esp_lcd_panel_disp_on_off(panel_handle, false); +} diff --git a/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/main/ssd1681_waveshare_1in54_lut.h b/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/main/ssd1681_waveshare_1in54_lut.h new file mode 100644 index 00000000..66952589 --- /dev/null +++ b/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/main/ssd1681_waveshare_1in54_lut.h @@ -0,0 +1,141 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#pragma once + + +// 0x00 ~ 0xff blink 1 time ~ 256 times to refresh +#define SSD1681_WAVESHARE_1IN54_REFRESH_TIME 0x01 +#define SSD1681_WAVESHARE_1IN54_V2_LUT_FULL_REFRESH ((uint8_t[]) { \ + /* LUT 0 VS Group 0~11 */ \ + 0x80, 0x48, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* LUT 1 VS Group 0~11 */ \ + 0x40, 0x48, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* LUT 2 VS Group 0~11, keep the same as LUT0 for Black-White e-Paper */ \ + 0x80, 0x48, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* LUT 3 VS Group 0~11, keep the same as LUT1 for Black-White e-Paper */ \ + 0x40, 0x48, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* LUT 4 VS Group 0~11, seems useless, just keep all zero */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* --- */ \ + /* Only Group0~2 are used */ \ + /* Group 0 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0xA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 1 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0xA, 0x2, 0x0, 0xA, 0x2, 0x0, SSD1681_WAVESHARE_1IN54_REFRESH_TIME, \ + /* Group 2 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 3 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 4 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 5 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 6 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 7 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 8 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 9 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 10 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 11 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* --- */ \ + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0, \ + /* --- Other register params, do not transfer together with data above */ \ + 0x22, 0x17, 0x41, 0x0, 0x32, 0x20 \ +}) + +#define SSD1681_WAVESHARE_1IN54_V2_LUT_FAST_REFRESH ((uint8_t[]) { \ + /* LUT 0 VS Group 0~11 */ \ + 0x80, 0x48, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* LUT 1 VS Group 0~11 */ \ + 0x40, 0x48, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* LUT 2 VS Group 0~11, keep the same as LUT0 for Black-White e-Paper */ \ + 0x80, 0x48, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* LUT 3 VS Group 0~11, keep the same as LUT1 for Black-White e-Paper */ \ + 0x40, 0x48, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* LUT 4 VS Group 0~11, seems useless, just keep all zero */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* --- */ \ + /* Only Group0~2 are used */ \ + /* Group 0 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 1 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 2 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 3 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 4 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 5 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 6 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 7 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 8 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 9 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 10 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 11 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* --- */ \ + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0, \ + /* --- Other register params, do not transfer together with data above */ \ + 0x22, 0x17, 0x41, 0x0, 0x32, 0x20 \ +}) + +// NOTE: After several refreshes using SSD1681_WAVESHARE_1IN54_V2_LUT_FAST_REFRESH, you may notice the WHITE color +// goes GRAY and contrast decrease a lot. Use the LUT below to avoid that issue. +// NOTE: The LUT below will have the source output "keep previous output before power off", so the service life may be affected. +#define SSD1681_WAVESHARE_1IN54_V2_LUT_FAST_REFRESH_KEEP ((uint8_t[]) { \ + /* LUT 0 VS Group 0~11 */ \ + 0x80, 0x48, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* LUT 1 VS Group 0~11 */ \ + 0x40, 0x48, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* LUT 2 VS Group 0~11, keep the same as LUT0 for Black-White e-Paper */ \ + 0x80, 0x48, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* LUT 3 VS Group 0~11, keep the same as LUT1 for Black-White e-Paper */ \ + 0x40, 0x48, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* LUT 4 VS Group 0~11, seems useless, just keep all zero */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* --- */ \ + /* Only Group0~2 are used */ \ + /* Group 0 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 1 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 2 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 3 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 4 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 5 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 6 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 7 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 8 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 9 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 10 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 11 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* --- */ \ + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0, \ + /* --- Other register params, do not transfer together with data above */ \ + 0x07, 0x17, 0x41, 0x0, 0x32, 0x20 \ +}) diff --git a/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/scan_mode.svg b/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/scan_mode.svg new file mode 100644 index 00000000..4fc73358 --- /dev/null +++ b/components/lcd/esp_lcd_ssd1681/examples/epaper_demo/scan_mode.svg @@ -0,0 +1,4 @@ + + + +
mirror_x false
mirror_y false
swap_xy  false
mirror_x false...
non-copy-mode supported
non-copy-mode supported
Data entry sequence
Data entry sequence
x
x
y
y
1 byte of data
1 byte of data
Text is not SVG - cannot display
diff --git a/components/lcd/esp_lcd_ssd1681/examples/epaper_lvgl_demo/CMakeLists.txt b/components/lcd/esp_lcd_ssd1681/examples/epaper_lvgl_demo/CMakeLists.txt new file mode 100644 index 00000000..c8df0fbd --- /dev/null +++ b/components/lcd/esp_lcd_ssd1681/examples/epaper_lvgl_demo/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(epaper_lvgl_demo) diff --git a/components/lcd/esp_lcd_ssd1681/examples/epaper_lvgl_demo/README.md b/components/lcd/esp_lcd_ssd1681/examples/epaper_lvgl_demo/README.md new file mode 100644 index 00000000..5948d879 --- /dev/null +++ b/components/lcd/esp_lcd_ssd1681/examples/epaper_lvgl_demo/README.md @@ -0,0 +1,88 @@ +# e-Paper Example using LVGL + +[esp_lcd](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/lcd.html) allows user to add their own panel drivers in the project scope (i.e. panel driver can live outside of esp-idf), so that the upper layer code like LVGL porting code can be reused without any modifications, as long as user-implemented panel driver follows the interface defined in the `esp_lcd` component. + +This example shows how to use SSD1681 e-paper display driver from Component manager in esp-idf project. This example will draw a clock widget using the LVGL library. + +## How to use the example + +### Hardware Required + +* An ESP development board +* An SSD1681 e-paper panel, with SPI interface +* An USB cable for power supply and programming + +### Hardware Connection + +The connection between ESP Board and the LCD is as follows: + +``` + ESP Board SSD1681 e-Paper Panel +┌──────────────────────────────┐ ┌────────────────────┐ +│ GND ├─────────────►│ GND │ +│ │ │ │ +│ 3V3 ├─────────────►│ VCC │ +│ │ │ │ +│ EXAMPLE_PIN_NUM_SCLK ├─────────────►│ CLK │ +│ │ │ │ +│ EXAMPLE_PIN_NUM_MOSI ├─────────────►│ DIN │ +│ │ │ │ +│ EXAMPLE_PIN_NUM_EPD_RST ├─────────────►│ RST │ +│ │ │ │ +│ EXAMPLE_PIN_NUM_EPD_DC ├─────────────►│ DC │ +│ │ │ │ +│ EXAMPLE_PIN_NUM_EPD_CS ├─────────────►│ CS │ +│ │ │ │ +│ EXAMPLE_PIN_NUM_EPD_BUSY│◄─────────────│ BUSY │ +└──────────────────────────────┘ └────────────────────┘ +``` + +The GPIO number used by this example can be changed in [main.c](main/main.c). + +### Software Configuration + +- Change all the `EXAMPLE_PIN` macro definition according to your hardware connection. +- If you are not using waveshare 1.54 inch V2 e-paper panel, please use the waveform lut provided by your panel vendor instead of using the demo built-in ones, or just simply comment the `epaper_panel_set_custom_lut` call and use the panel built-in waveform lut. +- You could go to `menuconfig / Component config / LVGL configuration / Feature configuration / Others` and unselect `Show CPU usage and FPS count` to hide the CPU usage and FPS count window. + +### Build and Flash + +Run `idf.py -p PORT build flash monitor` to build, flash and monitor the project. A clock widget will show up on the e-paper as expected. + +The first time you run `idf.py` for the example will cost extra time as the build system needs to address the component dependencies and downloads the missing components from registry into `managed_components` folder. + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects. + +### Example Output + +```bash +... +I (338) example: Initialize SPI bus +I (348) example: Install panel IO +I (348) gpio: GPIO[9]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 +I (358) gpio: GPIO[4]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 +I (368) lcd_panel.epaper: Add handler for GPIO 18 +I (378) gpio: GPIO[18]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 0| Pulldown: 1| Intr:2 +I (378) example: Resetting e-Paper display... +I (508) example: Initializing e-Paper display... +I (628) example: Turning e-Paper display on... +I (748) example: Initialize LVGL library +I (748) example: Register display driver to LVGL +I (748) example: Install LVGL tick timer +I (748) example: Display LVGL Meter Widget +... +``` + +## Performance Notice + +- LVGL library does not refresh the e-paper panel unless the content of the panel changes. However, the panel is kept refreshing when enabling `Show CPU usage and FPS count`, so do not forget to disable it to avoid unnecessary refresh. +- If you want to set a different screen rotation permanently, modifying the buffer conversion code in `example_lvgl_flush_cb` is better than using rotation API provided by LVGL. This is because converting `lv_color_t` to `uint8_t[]` needs a data copy, if using software-based rotation, we'll need another copy. Then convert the traversal sequence while converting `lv_color_t` to `uint8_t[]` might be a better idea. +- You should not set the latency of `vTaskDelay()` in the main loop too short. This demo only refresh the screen every tens of seconds, and during the interval of refreshes, the `lv_timer_handler()` is non-blocking and returns immediately, so please keep a reasonable `vTaskDelay()` latency to yield CPU for other tasks. The `vTaskDelay()` uses a busy-wait if the latency you set is too short and this may cause watchdog timeout error. + +## Troubleshooting + +* Why the e-paper doesn't respond? + * Maybe your GPIO pin num is not correctly set, check in [main.c](main/main.c). + * Maybe your waveform lut is incorrect, try stop using your custom waveform lut. diff --git a/components/lcd/esp_lcd_ssd1681/examples/epaper_lvgl_demo/main/CMakeLists.txt b/components/lcd/esp_lcd_ssd1681/examples/epaper_lvgl_demo/main/CMakeLists.txt new file mode 100644 index 00000000..52fb02e0 --- /dev/null +++ b/components/lcd/esp_lcd_ssd1681/examples/epaper_lvgl_demo/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" "lvgl_demo_ui.c" + INCLUDE_DIRS ".") diff --git a/components/lcd/esp_lcd_ssd1681/examples/epaper_lvgl_demo/main/idf_component.yml b/components/lcd/esp_lcd_ssd1681/examples/epaper_lvgl_demo/main/idf_component.yml new file mode 100644 index 00000000..44eb3690 --- /dev/null +++ b/components/lcd/esp_lcd_ssd1681/examples/epaper_lvgl_demo/main/idf_component.yml @@ -0,0 +1,8 @@ +dependencies: + idf: ">=5.0" + lvgl/lvgl: "~8.3.0" + esp_lcd_ssd1681: + # I am specifying the path of the component because the component + # had not been published to the ESP Component Registry by the time + # I write this example. + path: "../../../" \ No newline at end of file diff --git a/components/lcd/esp_lcd_ssd1681/examples/epaper_lvgl_demo/main/lvgl_demo_ui.c b/components/lcd/esp_lcd_ssd1681/examples/epaper_lvgl_demo/main/lvgl_demo_ui.c new file mode 100644 index 00000000..0b12227b --- /dev/null +++ b/components/lcd/esp_lcd_ssd1681/examples/epaper_lvgl_demo/main/lvgl_demo_ui.c @@ -0,0 +1,57 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +// This demo UI is adapted from LVGL official example: https://docs.lvgl.io/master/widgets/extra/meter.html#simple-meter + +#include "lvgl.h" + +static lv_obj_t *meter; + +static void set_value(void *indic, int32_t v) +{ + lv_meter_set_indicator_end_value(meter, indic, v); +} + + +void example_lvgl_demo_ui(lv_disp_t *disp) +{ + meter = lv_meter_create(lv_scr_act()); + lv_obj_set_size(meter, 200, 200); + lv_obj_center(meter); + + /*Create a scale for the minutes*/ + /*61 ticks in a 360 degrees range (the last and the first line overlaps)*/ + lv_meter_scale_t *scale_min = lv_meter_add_scale(meter); + lv_meter_set_scale_ticks(meter, scale_min, 61, 1, 10, lv_palette_main(LV_PALETTE_BLUE)); + lv_meter_set_scale_range(meter, scale_min, 0, 60, 360, 270); + + /*Create another scale for the hours. It's only visual and contains only major ticks*/ + lv_meter_scale_t *scale_hour = lv_meter_add_scale(meter); + lv_meter_set_scale_ticks(meter, scale_hour, 12, 0, 0, lv_palette_main(LV_PALETTE_BLUE)); /*12 ticks*/ + lv_meter_set_scale_major_ticks(meter, scale_hour, 1, 2, 20, lv_palette_main(LV_PALETTE_BLUE), 10); /*Every tick is major*/ + lv_meter_set_scale_range(meter, scale_hour, 1, 12, 330, 300); /*[1..12] values in an almost full circle*/ + + LV_IMG_DECLARE(img_hand) + + /*Add the hands from images*/ + lv_meter_indicator_t *indic_min = lv_meter_add_needle_line(meter, scale_min, 4, lv_palette_main(LV_PALETTE_RED), -10); + lv_meter_indicator_t *indic_hour = lv_meter_add_needle_img(meter, scale_min, &img_hand, 5, 5); + + /*Create an animation to set the value*/ + lv_anim_t a; + lv_anim_init(&a); + lv_anim_set_exec_cb(&a, set_value); + lv_anim_set_values(&a, 0, 60); + lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE); + lv_anim_set_time(&a, 60000 * 30); /* 60 * 30 sec for 1 turn, 30 sec for 1 move of the minute hand*/ + lv_anim_set_var(&a, indic_min); + lv_anim_start(&a); + + lv_anim_set_var(&a, indic_hour); + lv_anim_set_time(&a, 60000 * 60 * 10); /*36000 sec for 1 turn, 600 sec for 1 move of the hour hand*/ + lv_anim_set_values(&a, 0, 60); + lv_anim_start(&a); +} diff --git a/components/lcd/esp_lcd_ssd1681/examples/epaper_lvgl_demo/main/main.c b/components/lcd/esp_lcd_ssd1681/examples/epaper_lvgl_demo/main/main.c new file mode 100644 index 00000000..1d850d4a --- /dev/null +++ b/components/lcd/esp_lcd_ssd1681/examples/epaper_lvgl_demo/main/main.c @@ -0,0 +1,267 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/task.h" +#include "esp_timer.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_vendor.h" +#include "esp_lcd_panel_ops.h" +#include "driver/gpio.h" +#include "driver/spi_master.h" +#include "esp_err.h" +#include "esp_log.h" +#include "lvgl.h" + +#include "ssd1681_waveshare_1in54_lut.h" + +static const char *TAG = "example"; + +// Using SPI2 in the example +#define LCD_HOST SPI2_HOST + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// Please update the following configuration according to your LCD spec ////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#define EXAMPLE_LCD_PIXEL_CLOCK_HZ (20 * 1000 * 1000) +#define EXAMPLE_PIN_NUM_SCLK 6 +#define EXAMPLE_PIN_NUM_MOSI 7 +#define EXAMPLE_PIN_NUM_MISO (-1) // Unused +#define EXAMPLE_PIN_NUM_EPD_DC 9 +#define EXAMPLE_PIN_NUM_EPD_RST 4 +#define EXAMPLE_PIN_NUM_EPD_CS 10 +#define EXAMPLE_PIN_NUM_EPD_BUSY 18 + +// The pixel number in horizontal and vertical +#define EXAMPLE_LCD_H_RES 200 +#define EXAMPLE_LCD_V_RES 200 + +// Bit number used to represent command and parameter +#define EXAMPLE_LCD_CMD_BITS 8 +#define EXAMPLE_LCD_PARAM_BITS 8 + +#define EXAMPLE_LVGL_TICK_PERIOD_MS 2 + + +static SemaphoreHandle_t panel_refreshing_sem = NULL; + +extern void example_lvgl_demo_ui(lv_disp_t *disp); + +IRAM_ATTR bool epaper_flush_ready_callback(const esp_lcd_panel_handle_t handle, const void *edata, void *user_data) +{ + lv_disp_drv_t *disp_driver = (lv_disp_drv_t *) user_data; + lv_disp_flush_ready(disp_driver); + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + xSemaphoreGiveFromISR(panel_refreshing_sem, &xHigherPriorityTaskWoken); + if (xHigherPriorityTaskWoken == pdTRUE) { + return true; + } + return false; +} + +static uint8_t *converted_buffer_black; +static uint8_t *converted_buffer_red; +static void example_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 offsetx1 = area->x1; + int offsetx2 = area->x2; + int offsety1 = area->y1; + int offsety2 = area->y2; + // Used to vertical traverse lvgl framebuffer + // int len_x = abs(offsetx1 - offsetx2) + 1; + // int len_y = abs(offsety1 - offsety2) + 1; + // --- Convert buffer from color to monochrome bitmap + int len_bits = (abs(offsetx1 - offsetx2) + 1) * (abs(offsety1 - offsety2) + 1); + + memset(converted_buffer_black, 0x00, len_bits / 8); + memset(converted_buffer_red, 0x00, len_bits / 8); + for (int i = 0; i < len_bits; i++) { + // NOTE: Set bits of converted_buffer[] FROM LOW ADDR TO HIGH ADDR, FROM HSB TO LSB + // NOTE: 1 means BLACK/RED, 0 means WHITE + // Horizontal traverse lvgl framebuffer (by row) + converted_buffer_black[i / 8] |= (((lv_color_brightness(color_map[i])) < 251) << (7 - (i % 8))); + converted_buffer_red[i / 8] |= ((((color_map[i].ch.red) > 3) && ((lv_color_brightness(color_map[i])) < 251)) << (7 - (i % 8))); + // Vertical traverse lvgl framebuffer (by column), needs to uncomment len_x and len_y + // NOTE: If your screen rotation requires setting the pixels vertically, you could use the code below + // converted_buffer[i/8] |= (((lv_color_brightness(color_map[((i*len_x)%len_bits) + i/len_y])) > 250) << (7-(i % 8))); + } + // --- Draw bitmap + + ESP_ERROR_CHECK(epaper_panel_set_bitmap_color(panel_handle, SSD1681_EPAPER_BITMAP_BLACK)); + ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, converted_buffer_black)); + ESP_ERROR_CHECK(epaper_panel_set_bitmap_color(panel_handle, SSD1681_EPAPER_BITMAP_RED)); + ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, converted_buffer_red)); + ESP_ERROR_CHECK(epaper_panel_refresh_screen(panel_handle)); +} + +static void example_lvgl_wait_cb(struct _lv_disp_drv_t *disp_drv) +{ + xSemaphoreTake(panel_refreshing_sem, portMAX_DELAY); +} + +/* Rotate display and touch, when rotated screen in LVGL. Called when driver parameters are updated. */ +static void example_lvgl_port_update_callback(lv_disp_drv_t *drv) +{ + esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data; + + switch (drv->rotated) { + case LV_DISP_ROT_NONE: + // Rotate LCD display + esp_lcd_panel_swap_xy(panel_handle, false); + esp_lcd_panel_mirror(panel_handle, false, false); + break; + case LV_DISP_ROT_90: + // Rotate LCD display + esp_lcd_panel_swap_xy(panel_handle, true); + esp_lcd_panel_mirror(panel_handle, false, true); + break; + case LV_DISP_ROT_180: + // Rotate LCD display + esp_lcd_panel_swap_xy(panel_handle, false); + esp_lcd_panel_mirror(panel_handle, true, true); + break; + case LV_DISP_ROT_270: + // Rotate LCD display + esp_lcd_panel_swap_xy(panel_handle, true); + esp_lcd_panel_mirror(panel_handle, true, false); + break; + } +} + + +static void example_increase_lvgl_tick(void *arg) +{ + /* Tell LVGL how many milliseconds has elapsed */ + lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS); +} + +void app_main(void) +{ + static lv_disp_draw_buf_t disp_buf; // contains internal graphic buffer(s) called draw buffer(s) + static lv_disp_drv_t disp_drv; // contains callback functions + + panel_refreshing_sem = xSemaphoreCreateBinary(); + xSemaphoreGive(panel_refreshing_sem); + + ESP_LOGI(TAG, "Initialize SPI bus"); + spi_bus_config_t buscfg = { + .sclk_io_num = EXAMPLE_PIN_NUM_SCLK, + .mosi_io_num = EXAMPLE_PIN_NUM_MOSI, + .miso_io_num = EXAMPLE_PIN_NUM_MISO, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .max_transfer_sz = EXAMPLE_LCD_H_RES * 200 / 8, + }; + ESP_ERROR_CHECK(spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO)); + + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_panel_io_handle_t io_handle = NULL; + esp_lcd_panel_io_spi_config_t io_config = { + .dc_gpio_num = EXAMPLE_PIN_NUM_EPD_DC, + .cs_gpio_num = EXAMPLE_PIN_NUM_EPD_CS, + .pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ, + .lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS, + .lcd_param_bits = EXAMPLE_LCD_PARAM_BITS, + .spi_mode = 0, + .trans_queue_depth = 10, + .on_color_trans_done = NULL, + }; + // --- Attach the LCD to the SPI bus + ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t) LCD_HOST, &io_config, &io_handle)); + esp_lcd_panel_handle_t panel_handle = NULL; + + // --- Create esp_lcd panel + esp_lcd_ssd1681_config_t epaper_ssd1681_config = { + .busy_gpio_num = EXAMPLE_PIN_NUM_EPD_BUSY, + .non_copy_mode = true, + }; + esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = EXAMPLE_PIN_NUM_EPD_RST, + .flags.reset_active_high = false, + .vendor_config = &epaper_ssd1681_config + }; + gpio_install_isr_service(0); + ESP_ERROR_CHECK(esp_lcd_new_panel_ssd1681(io_handle, &panel_config, &panel_handle)); + + // --- Reset the display + ESP_LOGI(TAG, "Resetting e-Paper display..."); + ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); + vTaskDelay(100 / portTICK_PERIOD_MS); + // --- Initialize panel + ESP_LOGI(TAG, "Initializing e-Paper display..."); + ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); + // --- Register the e-Paper refresh done callback + epaper_panel_callbacks_t cbs = { + .on_epaper_refresh_done = epaper_flush_ready_callback + }; + epaper_panel_register_event_callbacks(panel_handle, &cbs, &disp_drv); + vTaskDelay(100 / portTICK_PERIOD_MS); + // --- Turn on display + ESP_LOGI(TAG, "Turning e-Paper display on..."); + ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true)); + vTaskDelay(100 / portTICK_PERIOD_MS); + // --- Configurate the screen + // NOTE: the configurations below are all FALSE by default + ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, false, false)); + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, false)); + esp_lcd_panel_invert_color(panel_handle, false); + // NOTE: Calling esp_lcd_panel_disp_on_off(panel_handle, true) will reset the LUT to the panel built-in one, + // custom LUT will not take effect any more after calling esp_lcd_panel_disp_on_off(panel_handle, true) + ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true)); + + // --- Initialize LVGL + ESP_LOGI(TAG, "Initialize LVGL library"); + lv_init(); + // alloc draw buffers used by LVGL + // it's recommended to choose the size of the draw buffer(s) to be at least 1/10 screen sized + lv_color_t *buf1 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 200 * sizeof(lv_color_t), MALLOC_CAP_DMA); + assert(buf1); + lv_color_t *buf2 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 200 * sizeof(lv_color_t), MALLOC_CAP_DMA); + assert(buf2); + // alloc bitmap buffer to draw + converted_buffer_black = heap_caps_malloc(EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES / 8, MALLOC_CAP_DMA); + converted_buffer_red = heap_caps_malloc(EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES / 8, MALLOC_CAP_DMA); + // initialize LVGL draw buffers + lv_disp_draw_buf_init(&disp_buf, buf1, buf2, EXAMPLE_LCD_H_RES * 200); + // initialize LVGL display driver + lv_disp_drv_init(&disp_drv); + disp_drv.hor_res = EXAMPLE_LCD_H_RES; + disp_drv.ver_res = EXAMPLE_LCD_V_RES; + disp_drv.flush_cb = example_lvgl_flush_cb; + disp_drv.wait_cb = example_lvgl_wait_cb; + disp_drv.drv_update_cb = example_lvgl_port_update_callback; + disp_drv.draw_buf = &disp_buf; + disp_drv.user_data = panel_handle; + // NOTE: The ssd1681 e-paper is monochrome and 1 byte represents 8 pixels + // so full_refresh is MANDATORY because we cannot set position to bitmap at pixel level + disp_drv.full_refresh = true; + ESP_LOGI(TAG, "Register display driver to LVGL"); + lv_disp_t *disp = lv_disp_drv_register(&disp_drv); + // init lvgl tick + ESP_LOGI(TAG, "Install LVGL tick timer"); + // Tick interface for LVGL (using esp_timer to generate 2ms periodic event) + const esp_timer_create_args_t lvgl_tick_timer_args = { + .callback = &example_increase_lvgl_tick, + .name = "lvgl_tick" + }; + esp_timer_handle_t lvgl_tick_timer = NULL; + ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer)); + ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000)); + + ESP_LOGI(TAG, "Display LVGL Meter Widget"); + example_lvgl_demo_ui(disp); + + while (1) { + // raise the task priority of LVGL and/or reduce the handler period can improve the performance + // The task running lv_timer_handler should have lower priority than that running `lv_tick_inc` + lv_timer_handler(); + vTaskDelay(pdMS_TO_TICKS(200)); + } +} diff --git a/components/lcd/esp_lcd_ssd1681/examples/epaper_lvgl_demo/main/ssd1681_waveshare_1in54_lut.h b/components/lcd/esp_lcd_ssd1681/examples/epaper_lvgl_demo/main/ssd1681_waveshare_1in54_lut.h new file mode 100644 index 00000000..66952589 --- /dev/null +++ b/components/lcd/esp_lcd_ssd1681/examples/epaper_lvgl_demo/main/ssd1681_waveshare_1in54_lut.h @@ -0,0 +1,141 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#pragma once + + +// 0x00 ~ 0xff blink 1 time ~ 256 times to refresh +#define SSD1681_WAVESHARE_1IN54_REFRESH_TIME 0x01 +#define SSD1681_WAVESHARE_1IN54_V2_LUT_FULL_REFRESH ((uint8_t[]) { \ + /* LUT 0 VS Group 0~11 */ \ + 0x80, 0x48, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* LUT 1 VS Group 0~11 */ \ + 0x40, 0x48, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* LUT 2 VS Group 0~11, keep the same as LUT0 for Black-White e-Paper */ \ + 0x80, 0x48, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* LUT 3 VS Group 0~11, keep the same as LUT1 for Black-White e-Paper */ \ + 0x40, 0x48, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* LUT 4 VS Group 0~11, seems useless, just keep all zero */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* --- */ \ + /* Only Group0~2 are used */ \ + /* Group 0 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0xA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 1 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0xA, 0x2, 0x0, 0xA, 0x2, 0x0, SSD1681_WAVESHARE_1IN54_REFRESH_TIME, \ + /* Group 2 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 3 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 4 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 5 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 6 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 7 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 8 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 9 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 10 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 11 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* --- */ \ + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0, \ + /* --- Other register params, do not transfer together with data above */ \ + 0x22, 0x17, 0x41, 0x0, 0x32, 0x20 \ +}) + +#define SSD1681_WAVESHARE_1IN54_V2_LUT_FAST_REFRESH ((uint8_t[]) { \ + /* LUT 0 VS Group 0~11 */ \ + 0x80, 0x48, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* LUT 1 VS Group 0~11 */ \ + 0x40, 0x48, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* LUT 2 VS Group 0~11, keep the same as LUT0 for Black-White e-Paper */ \ + 0x80, 0x48, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* LUT 3 VS Group 0~11, keep the same as LUT1 for Black-White e-Paper */ \ + 0x40, 0x48, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* LUT 4 VS Group 0~11, seems useless, just keep all zero */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* --- */ \ + /* Only Group0~2 are used */ \ + /* Group 0 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 1 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 2 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 3 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 4 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 5 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 6 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 7 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 8 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 9 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 10 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 11 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* --- */ \ + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0, \ + /* --- Other register params, do not transfer together with data above */ \ + 0x22, 0x17, 0x41, 0x0, 0x32, 0x20 \ +}) + +// NOTE: After several refreshes using SSD1681_WAVESHARE_1IN54_V2_LUT_FAST_REFRESH, you may notice the WHITE color +// goes GRAY and contrast decrease a lot. Use the LUT below to avoid that issue. +// NOTE: The LUT below will have the source output "keep previous output before power off", so the service life may be affected. +#define SSD1681_WAVESHARE_1IN54_V2_LUT_FAST_REFRESH_KEEP ((uint8_t[]) { \ + /* LUT 0 VS Group 0~11 */ \ + 0x80, 0x48, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* LUT 1 VS Group 0~11 */ \ + 0x40, 0x48, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* LUT 2 VS Group 0~11, keep the same as LUT0 for Black-White e-Paper */ \ + 0x80, 0x48, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* LUT 3 VS Group 0~11, keep the same as LUT1 for Black-White e-Paper */ \ + 0x40, 0x48, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* LUT 4 VS Group 0~11, seems useless, just keep all zero */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* --- */ \ + /* Only Group0~2 are used */ \ + /* Group 0 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 1 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 2 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 3 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 4 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 5 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 6 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 7 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 8 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 9 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 10 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* Group 11 TP[*A] TP[*B] SR[*AB] TP[*C] TP[*D] SR[*CD] RP[*] */ \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, \ + /* --- */ \ + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0, \ + /* --- Other register params, do not transfer together with data above */ \ + 0x07, 0x17, 0x41, 0x0, 0x32, 0x20 \ +}) diff --git a/components/lcd/esp_lcd_ssd1681/examples/epaper_lvgl_demo/sdkconfig.defaults b/components/lcd/esp_lcd_ssd1681/examples/epaper_lvgl_demo/sdkconfig.defaults new file mode 100644 index 00000000..c9ffc649 --- /dev/null +++ b/components/lcd/esp_lcd_ssd1681/examples/epaper_lvgl_demo/sdkconfig.defaults @@ -0,0 +1,2 @@ +CONFIG_LV_USE_USER_DATA=y +CONFIG_LV_COLOR_DEPTH_8=y diff --git a/components/lcd/esp_lcd_ssd1681/idf_component.yml b/components/lcd/esp_lcd_ssd1681/idf_component.yml index c48f4f82..a6c5f3d4 100644 --- a/components/lcd/esp_lcd_ssd1681/idf_component.yml +++ b/components/lcd/esp_lcd_ssd1681/idf_component.yml @@ -1,5 +1,5 @@ version: "0.1.0" -description: ESP LCD SSD1681 +description: ESP LCD SSD1681 e-paper driver url: https://github.com/espressif/esp-bsp/tree/master/components/lcd/esp_lcd_ssd1681 dependencies: idf: ">=5.0" diff --git a/components/lcd/esp_lcd_ssd1681/include/esp_lcd_panel_ssd1681.h b/components/lcd/esp_lcd_ssd1681/include/esp_lcd_panel_ssd1681.h index 60cbfd30..8cc35745 100644 --- a/components/lcd/esp_lcd_ssd1681/include/esp_lcd_panel_ssd1681.h +++ b/components/lcd/esp_lcd_ssd1681/include/esp_lcd_panel_ssd1681.h @@ -23,7 +23,7 @@ extern "C" { * * @return Whether a high priority task is woken up by this function */ -typedef bool (*esp_lcd_epaper_panel_cb_t)(const esp_lcd_panel_handle_t handle, const void* edata, void* user_data); +typedef bool (*esp_lcd_epaper_panel_cb_t)(const esp_lcd_panel_handle_t handle, const void *edata, void *user_data); /** * @brief Type of ssd1681 e-paper callbacks @@ -96,7 +96,7 @@ esp_err_t epaper_panel_refresh_screen(esp_lcd_panel_t *panel); * @param[in] color a enum value, SSD1681_EPAPER_BITMAP_BLACK or SSD1681_EPAPER_BITMAP_RED * @return ESP_OK on success */ -esp_err_t epaper_panel_set_bitmap_color(esp_lcd_panel_t* panel, esp_lcd_ssd1681_bitmap_color_t color); +esp_err_t epaper_panel_set_bitmap_color(esp_lcd_panel_t *panel, esp_lcd_ssd1681_bitmap_color_t color); /** * @brief Set the callback function @@ -112,7 +112,7 @@ esp_err_t epaper_panel_set_bitmap_color(esp_lcd_panel_t* panel, esp_lcd_ssd1681_ * @return ESP_OK on success * ESP_ERR_INVALID_ARG if parameter is invalid */ -esp_err_t epaper_panel_register_event_callbacks(esp_lcd_panel_t *panel, epaper_panel_callbacks_t* cbs, void* user_ctx); +esp_err_t epaper_panel_register_event_callbacks(esp_lcd_panel_t *panel, epaper_panel_callbacks_t *cbs, void *user_ctx); /** * @brief Set a custom waveform lut diff --git a/test_app/CMakeLists.txt b/test_app/CMakeLists.txt index 43cebcd2..9cec9a65 100644 --- a/test_app/CMakeLists.txt +++ b/test_app/CMakeLists.txt @@ -15,7 +15,7 @@ endif() if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_LESS "4.4") set(EXCLUDE_COMPONENTS "es8311" "es7210" "esp_lvgl_port" "ds18b20") elseif("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_LESS "5.0") - set(EXCLUDE_COMPONENTS "esp_lcd_touch_stmpe610" "ds18b20") + set(EXCLUDE_COMPONENTS "esp_lcd_touch_stmpe610" "ds18b20" "esp_lcd_ssd1681") endif() # Test rgb lcd components only in esp32s3