mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-03 12:44:33 +02:00
spi_lcd: support large color transfer
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@@ -11,12 +11,15 @@
|
|||||||
#include <sys/cdefs.h>
|
#include <sys/cdefs.h>
|
||||||
#include "esp_lcd_panel_io_interface.h"
|
#include "esp_lcd_panel_io_interface.h"
|
||||||
#include "esp_lcd_panel_io.h"
|
#include "esp_lcd_panel_io.h"
|
||||||
|
#include "hal/spi_ll.h"
|
||||||
#include "driver/spi_master.h"
|
#include "driver/spi_master.h"
|
||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_check.h"
|
#include "esp_check.h"
|
||||||
#include "esp_lcd_common.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 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);
|
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);
|
||||||
@@ -29,7 +32,7 @@ typedef struct {
|
|||||||
spi_transaction_t base;
|
spi_transaction_t base;
|
||||||
struct {
|
struct {
|
||||||
unsigned int dc_gpio_level: 1;
|
unsigned int dc_gpio_level: 1;
|
||||||
unsigned int trans_is_color: 1;
|
unsigned int en_trans_done_cb: 1;
|
||||||
} flags;
|
} flags;
|
||||||
} lcd_spi_trans_descriptor_t;
|
} lcd_spi_trans_descriptor_t;
|
||||||
|
|
||||||
@@ -239,19 +242,54 @@ 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);
|
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");
|
ESP_GOTO_ON_ERROR(ret, err, TAG, "spi transmit (polling) command failed");
|
||||||
|
|
||||||
// sending LCD color data
|
// split to chunks if required:
|
||||||
lcd_trans->flags.trans_is_color = 1;
|
// 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->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.length = chunk_size * 8; // transaction length is in bits
|
||||||
lcd_trans->base.tx_buffer = color;
|
lcd_trans->base.tx_buffer = color;
|
||||||
if (spi_panel_io->flags.dc_as_cmd_phase) { // encoding DC value to SPI command phase when necessary
|
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;
|
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
|
// color data is usually large, using queue+blocking mode
|
||||||
ret = spi_device_queue_trans(spi_panel_io->spi_dev, &lcd_trans->base, portMAX_DELAY);
|
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");
|
ESP_GOTO_ON_ERROR(ret, err, TAG, "spi transmit (queue) color failed");
|
||||||
spi_panel_io->num_trans_inflight++;
|
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:
|
err:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -269,7 +307,7 @@ static void lcd_spi_post_trans_color_cb(spi_transaction_t *trans)
|
|||||||
{
|
{
|
||||||
esp_lcd_panel_io_spi_t *spi_panel_io = trans->user;
|
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);
|
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) {
|
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);
|
spi_panel_io->on_color_trans_done(&spi_panel_io->base, NULL, spi_panel_io->user_ctx);
|
||||||
}
|
}
|
||||||
|
@@ -61,9 +61,10 @@ static void lcd_initialize_spi(esp_lcd_panel_io_handle_t *io_handle, esp_lcd_pan
|
|||||||
TEST_ESP_OK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)TEST_SPI_HOST_ID, &io_config, io_handle));
|
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)
|
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);
|
uint8_t *img = heap_caps_malloc(TEST_IMG_SIZE, MALLOC_CAP_DMA);
|
||||||
TEST_ASSERT_NOT_NULL(img);
|
TEST_ASSERT_NOT_NULL(img);
|
||||||
|
|
||||||
@@ -79,10 +80,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++) {
|
for (int i = 0; i < 200; i++) {
|
||||||
uint8_t color_byte = esp_random() & 0xFF;
|
uint8_t color_byte = esp_random() & 0xFF;
|
||||||
int x_start = esp_random() % (TEST_LCD_H_RES - 100);
|
int x_start = esp_random() % (TEST_LCD_H_RES - 200);
|
||||||
int y_start = esp_random() % (TEST_LCD_V_RES - 100);
|
int y_start = esp_random() % (TEST_LCD_V_RES - 200);
|
||||||
memset(img, color_byte, TEST_IMG_SIZE);
|
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
|
// turn off screen
|
||||||
esp_lcd_panel_disp_off(panel_handle, true);
|
esp_lcd_panel_disp_off(panel_handle, true);
|
||||||
@@ -91,7 +92,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(spi_bus_free(TEST_SPI_HOST_ID));
|
||||||
TEST_ESP_OK(gpio_reset_pin(TEST_LCD_BK_LIGHT_GPIO));
|
TEST_ESP_OK(gpio_reset_pin(TEST_LCD_BK_LIGHT_GPIO));
|
||||||
free(img);
|
free(img);
|
||||||
#undef TEST_IMG_SIZE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("lcd panel spi io test", "[lcd]")
|
TEST_CASE("lcd panel spi io test", "[lcd]")
|
||||||
|
@@ -1,16 +1,8 @@
|
|||||||
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
|
/*
|
||||||
//
|
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
*
|
||||||
// you may not use this file except in compliance with the License.
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* NOTICE
|
* NOTICE
|
||||||
@@ -49,6 +41,8 @@ extern "C" {
|
|||||||
#define SPI_LL_PERIPH_CLK_FREQ (80 * 1000000)
|
#define SPI_LL_PERIPH_CLK_FREQ (80 * 1000000)
|
||||||
#define SPI_LL_GET_HW(ID) ((ID)==0? &SPI1:((ID)==1? &SPI2 : &SPI3))
|
#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
|
* The data structure holding calculated clock configuration. Since the
|
||||||
* calculation needs long time, it should be calculated during initialization and
|
* calculation needs long time, it should be calculated during initialization and
|
||||||
|
@@ -40,6 +40,8 @@ extern "C" {
|
|||||||
#define SPI_LL_PERIPH_CLK_FREQ (80 * 1000000)
|
#define SPI_LL_PERIPH_CLK_FREQ (80 * 1000000)
|
||||||
#define SPI_LL_GET_HW(ID) ((ID)==0? ({abort();NULL;}):&GPSPI2)
|
#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
|
* The data structure holding calculated clock configuration. Since the
|
||||||
* calculation needs long time, it should be calculated during initialization and
|
* calculation needs long time, it should be calculated during initialization and
|
||||||
|
@@ -1,16 +1,8 @@
|
|||||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
/*
|
||||||
//
|
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
*
|
||||||
// you may not use this file except in compliance with the License.
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* NOTICE
|
* NOTICE
|
||||||
@@ -48,6 +40,8 @@ extern "C" {
|
|||||||
#define SPI_LL_PERIPH_CLK_FREQ (80 * 1000000)
|
#define SPI_LL_PERIPH_CLK_FREQ (80 * 1000000)
|
||||||
#define SPI_LL_GET_HW(ID) ((ID)==0? ({abort();NULL;}):&GPSPI2)
|
#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
|
* The data structure holding calculated clock configuration. Since the
|
||||||
* calculation needs long time, it should be calculated during initialization and
|
* calculation needs long time, it should be calculated during initialization and
|
||||||
|
@@ -43,6 +43,8 @@ extern "C" {
|
|||||||
#define SPI_LL_PERIPH_CLK_FREQ (80 * 1000000)
|
#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_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
|
* The data structure holding calculated clock configuration. Since the
|
||||||
* calculation needs long time, it should be calculated during initialization and
|
* calculation needs long time, it should be calculated during initialization and
|
||||||
|
@@ -42,6 +42,8 @@ extern "C" {
|
|||||||
#define SPI_LL_PERIPH_CLK_FREQ (80 * 1000000)
|
#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_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
|
* The data structure holding calculated clock configuration. Since the
|
||||||
* calculation needs long time, it should be calculated during initialization and
|
* calculation needs long time, it should be calculated during initialization and
|
||||||
|
Reference in New Issue
Block a user