소스 검색

Merge branch 'contrib/github_pr_8496' into 'master'

SPI  LCD support large color transfers (GitHub PR)

Closes IDFGH-6874

See merge request espressif/esp-idf!17391
morris 4 년 전
부모
커밋
98e19b3355

+ 52 - 14
components/esp_lcd/src/esp_lcd_panel_io_spi.c

@@ -15,12 +15,15 @@
 #endif
 #include "esp_lcd_panel_io_interface.h"
 #include "esp_lcd_panel_io.h"
+#include "hal/spi_ll.h"
 #include "driver/spi_master.h"
 #include "driver/gpio.h"
 #include "esp_log.h"
 #include "esp_check.h"
 #include "esp_lcd_common.h"
 
+#define LCD_SPI_MAX_DATA_SIZE (SPI_LL_DATA_MAX_BIT_LEN / 8)
+
 static const char *TAG = "lcd_panel.io.spi";
 
 static esp_err_t panel_io_spi_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, const void *param, size_t param_size);
@@ -33,7 +36,7 @@ typedef struct {
     spi_transaction_t base;
     struct {
         unsigned int dc_gpio_level: 1;
-        unsigned int trans_is_color: 1;
+        unsigned int en_trans_done_cb: 1;
     } flags;
 } lcd_spi_trans_descriptor_t;
 
@@ -250,18 +253,53 @@ static esp_err_t panel_io_spi_tx_color(esp_lcd_panel_io_t *io, int lcd_cmd, cons
     ret = spi_device_polling_transmit(spi_panel_io->spi_dev, &lcd_trans->base);
     ESP_GOTO_ON_ERROR(ret, err, TAG, "spi transmit (polling) command failed");
 
-    // sending LCD color data
-    lcd_trans->flags.trans_is_color = 1;
-    lcd_trans->flags.dc_gpio_level = spi_panel_io->flags.dc_data_level; // set D/C line to data mode
-    lcd_trans->base.length = color_size * 8; // transaction length is in bits
-    lcd_trans->base.tx_buffer = color;
-    if (spi_panel_io->flags.dc_as_cmd_phase) { // encoding DC value to SPI command phase when necessary
-        lcd_trans->base.cmd = spi_panel_io->flags.dc_data_level;
-    }
-    // color data is usually large, using queue+blocking mode
-    ret = spi_device_queue_trans(spi_panel_io->spi_dev, &lcd_trans->base, portMAX_DELAY);
-    ESP_GOTO_ON_ERROR(ret, err, TAG, "spi transmit (queue) color failed");
-    spi_panel_io->num_trans_inflight++;
+    // split to chunks if required:
+    // the SPI bus has a maximum transaction size determined by SPI_USR_MOSI_DBITLEN's bit width
+    do {
+        size_t chunk_size = color_size;
+
+        if (spi_panel_io->num_trans_inflight < spi_panel_io->queue_size) {
+            // get the next available transaction
+            lcd_trans = &spi_panel_io->trans_pool[spi_panel_io->num_trans_inflight];
+        } else {
+            // transaction pool has used up, recycle one transaction
+            ret = spi_device_get_trans_result(spi_panel_io->spi_dev, &spi_trans, portMAX_DELAY);
+            ESP_GOTO_ON_ERROR(ret, err, TAG, "recycle spi transactions failed");
+            lcd_trans = __containerof(spi_trans, lcd_spi_trans_descriptor_t, base);
+            spi_panel_io->num_trans_inflight--;
+        }
+        memset(lcd_trans, 0, sizeof(lcd_spi_trans_descriptor_t));
+
+        // SPI per-transfer size has its limitation, if the color buffer is too big, we need to split it into multiple trunks
+        if (chunk_size > LCD_SPI_MAX_DATA_SIZE) {
+            // cap the transfer size to the maximum supported by the bus
+            chunk_size = LCD_SPI_MAX_DATA_SIZE;
+        } else {
+            // mark en_trans_done_cb only at the last round to avoid premature completion callback
+            lcd_trans->flags.en_trans_done_cb = 1;
+        }
+
+        lcd_trans->base.user = spi_panel_io;
+        lcd_trans->flags.dc_gpio_level = spi_panel_io->flags.dc_data_level; // set D/C line to data mode
+        lcd_trans->base.length = chunk_size * 8; // transaction length is in bits
+        lcd_trans->base.tx_buffer = color;
+        if (spi_panel_io->flags.dc_as_cmd_phase) { // encoding DC value to SPI command phase when necessary
+            lcd_trans->base.cmd = spi_panel_io->flags.dc_data_level;
+        }
+        if (spi_panel_io->flags.octal_mode) {
+            // use 8 lines for transmitting command, address and data
+            lcd_trans->base.flags |= (SPI_TRANS_MULTILINE_CMD | SPI_TRANS_MULTILINE_ADDR | SPI_TRANS_MODE_OCT);
+        }
+
+        // color data is usually large, using queue+blocking mode
+        ret = spi_device_queue_trans(spi_panel_io->spi_dev, &lcd_trans->base, portMAX_DELAY);
+        ESP_GOTO_ON_ERROR(ret, err, TAG, "spi transmit (queue) color failed");
+        spi_panel_io->num_trans_inflight++;
+
+        // move on to the next chunk
+        color = (const uint8_t *)color + chunk_size;
+        color_size -= chunk_size;
+    } while (color_size > 0); // continue while we have remaining data to transmit
 
 err:
     return ret;
@@ -280,7 +318,7 @@ static void lcd_spi_post_trans_color_cb(spi_transaction_t *trans)
 {
     esp_lcd_panel_io_spi_t *spi_panel_io = trans->user;
     lcd_spi_trans_descriptor_t *lcd_trans = __containerof(trans, lcd_spi_trans_descriptor_t, base);
-    if (lcd_trans->flags.trans_is_color) {
+    if (lcd_trans->flags.en_trans_done_cb) {
         if (spi_panel_io->on_color_trans_done) {
             spi_panel_io->on_color_trans_done(&spi_panel_io->base, NULL, spi_panel_io->user_ctx);
         }

+ 5 - 5
components/esp_lcd/test_apps/spi_lcd/main/test_spi_lcd_panel.c

@@ -71,9 +71,10 @@ void test_spi_lcd_common_initialize(esp_lcd_panel_io_handle_t *io_handle, esp_lc
     TEST_ESP_OK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)TEST_SPI_HOST_ID, &io_config, io_handle));
 }
 
+#define TEST_IMG_SIZE (200 * 200 * sizeof(uint16_t))
+
 static void lcd_panel_test(esp_lcd_panel_io_handle_t io_handle, esp_lcd_panel_handle_t panel_handle)
 {
-#define TEST_IMG_SIZE (100 * 100 * sizeof(uint16_t))
     uint8_t *img = heap_caps_malloc(TEST_IMG_SIZE, MALLOC_CAP_DMA);
     TEST_ASSERT_NOT_NULL(img);
 
@@ -87,10 +88,10 @@ static void lcd_panel_test(esp_lcd_panel_io_handle_t io_handle, esp_lcd_panel_ha
 
     for (int i = 0; i < 200; i++) {
         uint8_t color_byte = esp_random() & 0xFF;
-        int x_start = esp_random() % (TEST_LCD_H_RES - 100);
-        int y_start = esp_random() % (TEST_LCD_V_RES - 100);
+        int x_start = esp_random() % (TEST_LCD_H_RES - 200);
+        int y_start = esp_random() % (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 + 100, y_start + 100, img);
+        esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_start + 200, y_start + 200, img);
     }
     // turn off screen
     esp_lcd_panel_disp_off(panel_handle, true);
@@ -99,7 +100,6 @@ static void lcd_panel_test(esp_lcd_panel_io_handle_t io_handle, esp_lcd_panel_ha
     TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST_ID));
     TEST_ESP_OK(gpio_reset_pin(TEST_LCD_BK_LIGHT_GPIO));
     free(img);
-#undef TEST_IMG_SIZE
 }
 
 TEST_CASE("lcd_panel_spi_io_test", "[lcd]")

+ 7 - 13
components/hal/esp32/include/hal/spi_ll.h

@@ -1,16 +1,8 @@
-// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+/*
+ * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
 
 /*******************************************************************************
  * NOTICE
@@ -49,6 +41,8 @@ extern "C" {
 #define SPI_LL_PERIPH_CLK_FREQ (80 * 1000000)
 #define SPI_LL_GET_HW(ID) ((ID)==0? &SPI1:((ID)==1? &SPI2 : &SPI3))
 
+#define SPI_LL_DATA_MAX_BIT_LEN (1 << 24)
+
 /**
  * The data structure holding calculated clock configuration. Since the
  * calculation needs long time, it should be calculated during initialization and

+ 2 - 0
components/hal/esp32c2/include/hal/spi_ll.h

@@ -40,6 +40,8 @@ extern "C" {
 #define SPI_LL_PERIPH_CLK_FREQ (80 * 1000000)
 #define SPI_LL_GET_HW(ID) ((ID)==0? ({abort();NULL;}):&GPSPI2)
 
+#define SPI_LL_DATA_MAX_BIT_LEN (1 << 18)
+
 /**
  * The data structure holding calculated clock configuration. Since the
  * calculation needs long time, it should be calculated during initialization and

+ 2 - 0
components/hal/esp32c3/include/hal/spi_ll.h

@@ -40,6 +40,8 @@ extern "C" {
 #define SPI_LL_PERIPH_CLK_FREQ (80 * 1000000)
 #define SPI_LL_GET_HW(ID) ((ID)==0? ({abort();NULL;}):&GPSPI2)
 
+#define SPI_LL_DATA_MAX_BIT_LEN (1 << 18)
+
 /**
  * The data structure holding calculated clock configuration. Since the
  * calculation needs long time, it should be calculated during initialization and

+ 7 - 13
components/hal/esp32h2/include/hal/spi_ll.h

@@ -1,16 +1,8 @@
-// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+/*
+ * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
 
 /*******************************************************************************
  * NOTICE
@@ -48,6 +40,8 @@ extern "C" {
 #define SPI_LL_PERIPH_CLK_FREQ (80 * 1000000)
 #define SPI_LL_GET_HW(ID) ((ID)==0? ({abort();NULL;}):&GPSPI2)
 
+#define SPI_LL_DATA_MAX_BIT_LEN (1 << 18)
+
 /**
  * The data structure holding calculated clock configuration. Since the
  * calculation needs long time, it should be calculated during initialization and

+ 2 - 0
components/hal/esp32s2/include/hal/spi_ll.h

@@ -43,6 +43,8 @@ extern "C" {
 #define SPI_LL_PERIPH_CLK_FREQ (80 * 1000000)
 #define SPI_LL_GET_HW(ID) ((ID)==0? ({abort();NULL;}):((ID)==1? &GPSPI2 : &GPSPI3))
 
+#define SPI_LL_DATA_MAX_BIT_LEN (1 << 23)
+
 /**
  * The data structure holding calculated clock configuration. Since the
  * calculation needs long time, it should be calculated during initialization and

+ 2 - 0
components/hal/esp32s3/include/hal/spi_ll.h

@@ -42,6 +42,8 @@ extern "C" {
 #define SPI_LL_PERIPH_CLK_FREQ (80 * 1000000)
 #define SPI_LL_GET_HW(ID) ((ID)==0? ({abort();NULL;}):((ID)==1? &GPSPI2 : &GPSPI3))
 
+#define SPI_LL_DATA_MAX_BIT_LEN (1 << 18)
+
 /**
  * The data structure holding calculated clock configuration. Since the
  * calculation needs long time, it should be calculated during initialization and

+ 0 - 2
tools/ci/check_copyright_ignore.txt

@@ -834,7 +834,6 @@ components/hal/esp32/include/hal/sigmadelta_ll.h
 components/hal/esp32/include/hal/soc_ll.h
 components/hal/esp32/include/hal/spi_flash_encrypted_ll.h
 components/hal/esp32/include/hal/spi_flash_ll.h
-components/hal/esp32/include/hal/spi_ll.h
 components/hal/esp32/include/hal/touch_sensor_hal.h
 components/hal/esp32/include/hal/touch_sensor_ll.h
 components/hal/esp32/include/hal/trace_ll.h
@@ -879,7 +878,6 @@ components/hal/esp32h2/include/hal/sigmadelta_ll.h
 components/hal/esp32h2/include/hal/soc_ll.h
 components/hal/esp32h2/include/hal/spi_flash_encrypted_ll.h
 components/hal/esp32h2/include/hal/spi_flash_ll.h
-components/hal/esp32h2/include/hal/spi_ll.h
 components/hal/esp32h2/include/hal/spimem_flash_ll.h
 components/hal/esp32h2/include/hal/twai_ll.h
 components/hal/esp32h2/include/hal/uhci_ll.h