Browse Source

Merge branch 'contrib/github_pr_12370' into 'master'

Support LCD panel sleep mode (GitHub PR)

Closes IDFGH-11203

See merge request espressif/esp-idf!26364
morris 2 năm trước cách đây
mục cha
commit
a21e46dc64

+ 12 - 1
components/esp_lcd/include/esp_lcd_panel_ops.h

@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -133,6 +133,17 @@ esp_err_t esp_lcd_panel_disp_on_off(esp_lcd_panel_handle_t panel, bool on_off);
 esp_err_t esp_lcd_panel_disp_off(esp_lcd_panel_handle_t panel, bool off)
 __attribute__((deprecated("use esp_lcd_panel_disp_on_off instead")));
 
+/**
+ * @brief Enter or exit sleep mode
+ *
+ * @param[in] panel LCD panel handle, which is created by other factory API like `esp_lcd_new_panel_st7789()`
+ * @param[in] sleep True to enter sleep mode, False to wake up
+ * @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_sleep(esp_lcd_panel_handle_t panel, bool sleep);
+
 #ifdef __cplusplus
 }
 #endif

+ 12 - 1
components/esp_lcd/interface/esp_lcd_panel_interface.h

@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -120,6 +120,17 @@ struct esp_lcd_panel_t {
      */
     esp_err_t (*disp_on_off)(esp_lcd_panel_t *panel, bool on_off);
 
+    /**
+     * @brief Enter or exit sleep mode
+     *
+     * @param[in] panel LCD panel handle, which is created by other factory API like `esp_lcd_new_panel_st7789()`
+     * @param[in] sleep True to enter sleep mode, False to wake up
+     * @return
+     *          - ESP_OK on success
+     *          - ESP_ERR_NOT_SUPPORTED if this function is not supported by the panel
+     */
+    esp_err_t (*disp_sleep)(esp_lcd_panel_t *panel, bool sleep);
+
     void *user_data;    /*!< User data, used to store externally customized data */
 };
 

+ 35 - 16
components/esp_lcd/src/esp_lcd_panel_nt35510.c

@@ -37,6 +37,7 @@ static esp_err_t panel_nt35510_mirror(esp_lcd_panel_t *panel, bool mirror_x, boo
 static esp_err_t panel_nt35510_swap_xy(esp_lcd_panel_t *panel, bool swap_axes);
 static esp_err_t panel_nt35510_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap);
 static esp_err_t panel_nt35510_disp_on_off(esp_lcd_panel_t *panel, bool off);
+static esp_err_t panel_nt35510_sleep(esp_lcd_panel_t *panel, bool sleep);
 
 typedef struct {
     esp_lcd_panel_t base;
@@ -116,6 +117,7 @@ esp_lcd_new_panel_nt35510(const esp_lcd_panel_io_handle_t io, const esp_lcd_pane
     nt35510->base.mirror = panel_nt35510_mirror;
     nt35510->base.swap_xy = panel_nt35510_swap_xy;
     nt35510->base.disp_on_off = panel_nt35510_disp_on_off;
+    nt35510->base.disp_sleep = panel_nt35510_sleep;
     *ret_panel = &(nt35510->base);
     ESP_LOGD(TAG, "new nt35510 panel @%p", nt35510);
 
@@ -157,7 +159,7 @@ static esp_err_t panel_nt35510_reset(esp_lcd_panel_t *panel)
     } else {
         // perform software reset
         ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET << 8, NULL, 0), TAG,
-                            "io tx param LCD_CMD_SWRESET failed");
+                            "io tx param failed");
         vTaskDelay(pdMS_TO_TICKS(20)); // spec, wait at least 5m before sending new command
     }
 
@@ -170,14 +172,14 @@ static esp_err_t panel_nt35510_init(esp_lcd_panel_t *panel)
     esp_lcd_panel_io_handle_t io = nt35510->io;
     // LCD goes into sleep mode and display will be turned off after power on reset, exit sleep mode first
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SLPOUT << 8, NULL, 0), TAG,
-                        "io tx param LCD_CMD_SLPOUT failed");;
+                        "io tx param failed");;
     vTaskDelay(pdMS_TO_TICKS(100));
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL << 8, (uint16_t[]) {
         nt35510->madctl_val,
-    }, 2), TAG, "io tx param LCD_CMD_MADCTL failed");
+    }, 2), TAG, "io tx param failed");
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD << 8, (uint16_t[]) {
         nt35510->colmod_val,
-    }, 2), TAG, "io tx param LCD_CMD_COLMOD failed");
+    }, 2), TAG, "io tx param failed");
 
     return ESP_OK;
 }
@@ -197,28 +199,28 @@ static esp_err_t panel_nt35510_draw_bitmap(esp_lcd_panel_t *panel, int x_start,
     // define an area of frame memory where MCU can access
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, (LCD_CMD_CASET << 8) + 0, (uint16_t[]) {
         (x_start >> 8) & 0xFF,
-    }, 2), TAG, "io tx param LCD_CMD_CASET failed");
+    }, 2), TAG, "io tx param failed");
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, (LCD_CMD_CASET << 8) + 1, (uint16_t[]) {
         x_start & 0xFF,
-    }, 2), TAG, "io tx param LCD_CMD_CASET failed");
+    }, 2), TAG, "io tx param failed");
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, (LCD_CMD_CASET << 8) + 2, (uint16_t[]) {
         ((x_end - 1) >> 8) & 0xFF,
-    }, 2), TAG, "io tx param LCD_CMD_CASET failed");
+    }, 2), TAG, "io tx param failed");
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, (LCD_CMD_CASET << 8) + 3, (uint16_t[]) {
         (x_end - 1) & 0xFF,
-    }, 2), TAG, "io tx param LCD_CMD_CASET failed");
+    }, 2), TAG, "io tx param failed");
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, (LCD_CMD_RASET << 8) + 0, (uint16_t[]) {
         (y_start >> 8) & 0xFF,
-    }, 2), TAG, "io tx param LCD_CMD_RASET failed");
+    }, 2), TAG, "io tx param failed");
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, (LCD_CMD_RASET << 8) + 1, (uint16_t[]) {
         y_start & 0xFF,
-    }, 2), TAG, "io tx param LCD_CMD_RASET failed");
+    }, 2), TAG, "io tx param failed");
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, (LCD_CMD_RASET << 8) + 2, (uint16_t[]) {
         ((y_end - 1) >> 8) & 0xFF,
-    }, 2), TAG, "io tx param LCD_CMD_RASET failed");
+    }, 2), TAG, "io tx param failed");
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, (LCD_CMD_RASET << 8) + 3, (uint16_t[]) {
         (y_end - 1) & 0xFF,
-    }, 2), TAG, "io tx param LCD_CMD_RASET failed");
+    }, 2), TAG, "io tx param failed");
     // transfer frame buffer
     size_t len = (x_end - x_start) * (y_end - y_start) * nt35510->fb_bits_per_pixel / 8;
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_color(io, LCD_CMD_RAMWR << 8, color_data, len), TAG, "io tx color failed");
@@ -237,7 +239,7 @@ static esp_err_t panel_nt35510_invert_color(esp_lcd_panel_t *panel, bool invert_
         command = LCD_CMD_INVOFF;
     }
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command << 8, NULL, 0), TAG,
-                        "io tx param LCD_CMD_INVON/LCD_CMD_INVOFF failed");
+                        "io tx param failed");
     return ESP_OK;
 }
 
@@ -257,7 +259,7 @@ static esp_err_t panel_nt35510_mirror(esp_lcd_panel_t *panel, bool mirror_x, boo
     }
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL << 8, (uint16_t[]) {
         nt35510->madctl_val
-    }, 2), TAG, "io tx param LCD_CMD_MADCTL failed");
+    }, 2), TAG, "io tx param failed");
     return ESP_OK;
 }
 
@@ -272,7 +274,7 @@ static esp_err_t panel_nt35510_swap_xy(esp_lcd_panel_t *panel, bool swap_axes)
     }
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL << 8, (uint16_t[]) {
         nt35510->madctl_val
-    }, 2), TAG, "io tx param LCD_CMD_MADCTL failed");
+    }, 2), TAG, "io tx param failed");
     return ESP_OK;
 }
 
@@ -295,6 +297,23 @@ static esp_err_t panel_nt35510_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
         command = LCD_CMD_DISPOFF;
     }
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command << 8, NULL, 0), TAG,
-                        "io tx param LCD_CMD_DISPON/LCD_CMD_DISPOFF failed");
+                        "io tx param failed");
+    return ESP_OK;
+}
+
+static esp_err_t panel_nt35510_sleep(esp_lcd_panel_t *panel, bool sleep)
+{
+    nt35510_panel_t *nt35510 = __containerof(panel, nt35510_panel_t, base);
+    esp_lcd_panel_io_handle_t io = nt35510->io;
+    int command = 0;
+    if (sleep) {
+        command = LCD_CMD_SLPIN;
+    } else {
+        command = LCD_CMD_SLPOUT;
+    }
+    ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG,
+                        "io tx param failed");
+    vTaskDelay(pdMS_TO_TICKS(100));
+
     return ESP_OK;
 }

+ 8 - 1
components/esp_lcd/src/esp_lcd_panel_ops.c

@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -68,3 +68,10 @@ esp_err_t esp_lcd_panel_disp_off(esp_lcd_panel_handle_t panel, bool off)
 {
     return esp_lcd_panel_disp_on_off(panel, !off);
 }
+
+esp_err_t esp_lcd_panel_disp_sleep(esp_lcd_panel_handle_t panel, bool sleep)
+{
+    ESP_RETURN_ON_FALSE(panel, ESP_ERR_INVALID_ARG, TAG, "invalid panel handle");
+    ESP_RETURN_ON_FALSE(panel->disp_sleep, ESP_ERR_NOT_SUPPORTED, TAG, "sleep is not supported by this panel");
+    return panel->disp_sleep(panel, sleep);
+}

+ 30 - 11
components/esp_lcd/src/esp_lcd_panel_st7789.c

@@ -40,6 +40,7 @@ static esp_err_t panel_st7789_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool
 static esp_err_t panel_st7789_swap_xy(esp_lcd_panel_t *panel, bool swap_axes);
 static esp_err_t panel_st7789_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap);
 static esp_err_t panel_st7789_disp_on_off(esp_lcd_panel_t *panel, bool off);
+static esp_err_t panel_st7789_sleep(esp_lcd_panel_t *panel, bool sleep);
 
 typedef struct {
     esp_lcd_panel_t base;
@@ -124,6 +125,7 @@ esp_lcd_new_panel_st7789(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel
     st7789->base.mirror = panel_st7789_mirror;
     st7789->base.swap_xy = panel_st7789_swap_xy;
     st7789->base.disp_on_off = panel_st7789_disp_on_off;
+    st7789->base.disp_sleep = panel_st7789_sleep;
     *ret_panel = &(st7789->base);
     ESP_LOGD(TAG, "new st7789 panel @%p", st7789);
 
@@ -164,7 +166,7 @@ static esp_err_t panel_st7789_reset(esp_lcd_panel_t *panel)
         vTaskDelay(pdMS_TO_TICKS(10));
     } else { // perform software reset
         ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG,
-                            "io tx param LCD_CMD_SWRESET failed");
+                            "io tx param failed");
         vTaskDelay(pdMS_TO_TICKS(20)); // spec, wait at least 5m before sending new command
     }
 
@@ -177,17 +179,17 @@ static esp_err_t panel_st7789_init(esp_lcd_panel_t *panel)
     esp_lcd_panel_io_handle_t io = st7789->io;
     // LCD goes into sleep mode and display will be turned off after power on reset, exit sleep mode first
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SLPOUT, NULL, 0), TAG,
-                        "io tx param LCD_CMD_SLPOUT failed");
+                        "io tx param failed");
     vTaskDelay(pdMS_TO_TICKS(100));
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
         st7789->madctl_val,
-    }, 1), TAG, "io tx param LCD_CMD_MADCTL failed");
+    }, 1), TAG, "io tx param failed");
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]) {
         st7789->colmod_val,
-    }, 1), TAG, "io tx param LCD_CMD_COLMOD failed");
+    }, 1), TAG, "io tx param failed");
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7789_CMD_RAMCTRL, (uint8_t[]) {
         st7789->ramctl_val_1, st7789->ramctl_val_2
-    }, 2), TAG, "io tx param RAMCTRL failed");
+    }, 2), TAG, "io tx param failed");
 
     return ESP_OK;
 }
@@ -210,13 +212,13 @@ static esp_err_t panel_st7789_draw_bitmap(esp_lcd_panel_t *panel, int x_start, i
         x_start & 0xFF,
         ((x_end - 1) >> 8) & 0xFF,
         (x_end - 1) & 0xFF,
-    }, 4), TAG, "io tx param LCD_CMD_CASET failed");
+    }, 4), TAG, "io tx param failed");
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_RASET, (uint8_t[]) {
         (y_start >> 8) & 0xFF,
         y_start & 0xFF,
         ((y_end - 1) >> 8) & 0xFF,
         (y_end - 1) & 0xFF,
-    }, 4), TAG, "io tx param LCD_CMD_RASET failed");
+    }, 4), TAG, "io tx param failed");
     // transfer frame buffer
     size_t len = (x_end - x_start) * (y_end - y_start) * st7789->fb_bits_per_pixel / 8;
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_color(io, LCD_CMD_RAMWR, color_data, len), TAG, "io tx color failed");
@@ -235,7 +237,7 @@ static esp_err_t panel_st7789_invert_color(esp_lcd_panel_t *panel, bool invert_c
         command = LCD_CMD_INVOFF;
     }
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG,
-                        "io tx param LCD_CMD_INVON/LCD_CMD_INVOFF failed");
+                        "io tx param failed");
     return ESP_OK;
 }
 
@@ -255,7 +257,7 @@ static esp_err_t panel_st7789_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool
     }
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
         st7789->madctl_val
-    }, 1), TAG, "io tx param LCD_CMD_MADCTL failed");
+    }, 1), TAG, "io tx param failed");
     return ESP_OK;
 }
 
@@ -270,7 +272,7 @@ static esp_err_t panel_st7789_swap_xy(esp_lcd_panel_t *panel, bool swap_axes)
     }
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
         st7789->madctl_val
-    }, 1), TAG, "io tx param LCD_CMD_MADCTL failed");
+    }, 1), TAG, "io tx param failed");
     return ESP_OK;
 }
 
@@ -293,6 +295,23 @@ static esp_err_t panel_st7789_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
         command = LCD_CMD_DISPOFF;
     }
     ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG,
-                        "io tx param LCD_CMD_DISPON/LCD_CMD_DISPOFF failed");
+                        "io tx param failed");
+    return ESP_OK;
+}
+
+static esp_err_t panel_st7789_sleep(esp_lcd_panel_t *panel, bool sleep)
+{
+    st7789_panel_t *st7789 = __containerof(panel, st7789_panel_t, base);
+    esp_lcd_panel_io_handle_t io = st7789->io;
+    int command = 0;
+    if (sleep) {
+        command = LCD_CMD_SLPIN;
+    } else {
+        command = LCD_CMD_SLPOUT;
+    }
+    ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG,
+                        "io tx param failed");
+    vTaskDelay(pdMS_TO_TICKS(100));
+
     return ESP_OK;
 }

+ 20 - 3
components/esp_lcd/test_apps/spi_lcd/main/test_spi_lcd_panel.c

@@ -90,14 +90,29 @@ static void lcd_panel_test(esp_lcd_panel_io_handle_t io_handle, esp_lcd_panel_ha
     // turn on backlight
     gpio_set_level(TEST_LCD_BK_LIGHT_GPIO, 1);
 
-    for (int i = 0; i < 200; i++) {
+    for (int i = 0; i < 100; i++) {
         uint8_t color_byte = rand() & 0xFF;
         int x_start = rand() % (TEST_LCD_H_RES - 200);
         int y_start = rand() % (TEST_LCD_V_RES - 200);
         memset(img, color_byte, TEST_IMG_SIZE);
         esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_start + 200, y_start + 200, img);
     }
-    // turn off screen
+
+    printf("go into sleep mode\r\n");
+    esp_lcd_panel_disp_sleep(panel_handle, true);
+    vTaskDelay(pdMS_TO_TICKS(500));
+    printf("exit sleep mode\r\n");
+    esp_lcd_panel_disp_sleep(panel_handle, false);
+
+    for (int i = 0; i < 100; i++) {
+        uint8_t color_byte = rand() & 0xFF;
+        int x_start = rand() % (TEST_LCD_H_RES - 200);
+        int y_start = rand() % (TEST_LCD_V_RES - 200);
+        memset(img, color_byte, TEST_IMG_SIZE);
+        esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_start + 200, y_start + 200, img);
+    }
+
+    printf("turn off the panel\r\n");
     esp_lcd_panel_disp_on_off(panel_handle, false);
     TEST_ESP_OK(esp_lcd_panel_del(panel_handle));
     TEST_ESP_OK(esp_lcd_panel_io_del(io_handle));
@@ -247,12 +262,14 @@ TEST_CASE("spi_lcd_send_colors_to_fixed_region", "[lcd]")
         TEST_ESP_OK(esp_lcd_panel_io_tx_color(io_handle, -1, color_data + i * color_size_per_step, color_size_per_step));
     }
     vTaskDelay(pdMS_TO_TICKS(1000));
-    // change to another color
+
+    printf("change to another color\r\n");
     color_byte = rand() & 0xFF;
     memset(color_data, color_byte, color_size);
     for (int i = 0; i < steps; i++) {
         TEST_ESP_OK(esp_lcd_panel_io_tx_color(io_handle, -1, color_data + i * color_size_per_step, color_size_per_step));
     }
+    vTaskDelay(pdMS_TO_TICKS(1000));
 
     TEST_ESP_OK(esp_lcd_panel_del(panel_handle));
     TEST_ESP_OK(esp_lcd_panel_io_del(io_handle));

+ 2 - 1
docs/en/api-reference/peripherals/lcd.rst

@@ -454,7 +454,8 @@ LCD Panel IO Operations
 * :cpp:func:`esp_lcd_panel_reset` can reset the LCD panel.
 * :cpp:func:`esp_lcd_panel_init` performs a basic initialization of the panel. To perform more manufacture specific initialization, please go to :ref:`steps_add_manufacture_init`.
 * Through combined use of :cpp:func:`esp_lcd_panel_swap_xy` and :cpp:func:`esp_lcd_panel_mirror`, you can rotate the LCD screen.
-* :cpp:func:`esp_lcd_panel_disp_on_off` can turn on or off the LCD screen (different from LCD backlight).
+* :cpp:func:`esp_lcd_panel_disp_on_off` can turn on or off the LCD screen by cutting down the output path from the frame buffer to the LCD screen.
+* :cpp:func:`esp_lcd_panel_disp_sleep` can reduce the power consumption of the LCD screen by entering the sleep mode. The internal frame buffer is still retained.
 * :cpp:func:`esp_lcd_panel_draw_bitmap` is the most significant function, which does the magic to draw the user provided color buffer to the LCD screen, where the draw window is also configurable.
 
 .. _steps_add_manufacture_init: