Open greenaddress opened 11 months ago
Code sample to reproduce
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "sdkconfig.h"
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include "esp_timer.h"
#include <driver/gpio.h>
#include <driver/spi_master.h>
#include <esp_lcd_panel_io.h>
#include <esp_lcd_panel_ops.h>
#include <esp_lcd_panel_vendor.h>
#include <esp_random.h>
#include <freertos/semphr.h>
#define DISP_PIN_NUM_MISO 2
#define DISP_PIN_NUM_MOSI 19
#define DISP_PIN_NUM_CLK 18
#define DISP_PIN_NUM_CS 5
#define DISP_PIN_NUM_DC 16
#define DISP_PIN_NUM_TCS -1
#define DISP_PIN_NUM_RST 23
#define DISP_PIN_NUM_BCKL 4
#define DISP_X_GAP 40
#define DISP_Y_GAP 53
#define DISP_WIDTH 240
#define DISP_HEIGHT 135
#define LCD_PIXEL_CLOCK_HZ 10000000
#define PIXEL_SIZE uint16_t
static esp_lcd_panel_handle_t panel_handle = NULL;
static SemaphoreHandle_t esp_lcd_mutex = NULL;
static DMA_ATTR PIXEL_SIZE display_buffer[DISP_WIDTH];
void prepare_display(void)
{
spi_bus_config_t buscfg = {
.sclk_io_num = DISP_PIN_NUM_CLK,
.mosi_io_num = DISP_PIN_NUM_MOSI,
.miso_io_num = DISP_PIN_NUM_MISO,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = DISP_WIDTH * sizeof(PIXEL_SIZE),
};
ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO));
esp_lcd_panel_io_handle_t io_handle = NULL;
esp_lcd_panel_io_spi_config_t io_config = {
.dc_gpio_num = DISP_PIN_NUM_DC,
.cs_gpio_num = DISP_PIN_NUM_CS,
.pclk_hz = LCD_PIXEL_CLOCK_HZ,
.lcd_cmd_bits = 8,
.lcd_param_bits = 8,
.spi_mode = 0,
.trans_queue_depth = 10,
};
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)SPI2_HOST, &io_config, &io_handle));
esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = DISP_PIN_NUM_RST,
.rgb_endian = LCD_RGB_ENDIAN_RGB,
.bits_per_pixel = sizeof(PIXEL_SIZE) * 8,
};
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle));
gpio_config_t bk_gpio_config = { .mode = GPIO_MODE_OUTPUT, .pin_bit_mask = 1ULL << DISP_PIN_NUM_BCKL };
ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
ESP_ERROR_CHECK(gpio_set_level(DISP_PIN_NUM_BCKL, 0));
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));
ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, true));
ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, true, false));
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, true));
ESP_ERROR_CHECK(esp_lcd_panel_set_gap(panel_handle, DISP_X_GAP, DISP_Y_GAP));
ESP_ERROR_CHECK(gpio_set_level(DISP_PIN_NUM_BCKL, 1));
}
static void fill_rect(int x, int y, int w, int h, PIXEL_SIZE color)
{
if (xSemaphoreTake(esp_lcd_mutex, 500 / portTICK_PERIOD_MS) == pdTRUE) {
for (int i = 0; i < w; ++i) {
display_buffer[i] = color;
}
const int startx = x - DISP_X_GAP;
const int endx = startx + w;
for (int i = 0; i < h; ++i) {
const int starty = (y - DISP_Y_GAP) + i;
const int endy = starty + 1;
ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(panel_handle, startx, starty, endx, endy, display_buffer));
}
xSemaphoreGive(esp_lcd_mutex);
}
// FIXME: without this delay you get artifacts on the display
vTaskDelay(10 / portTICK_PERIOD_MS);
}
void app_main(void)
{
/* mutex can be removed if using the display from just one task */
esp_lcd_mutex = xSemaphoreCreateMutex();
prepare_display();
fill_rect(40, 53, 240, 135, 0);
fill_rect(40, 73, 240, 111, 0);
fill_rect(40, 97, 240, 44, 19460);
fill_rect(64, 97, 48, 44, 19460);
fill_rect(112, 97, 144, 44, 19460);
fill_rect(40, 161, 21, 26, 0);
fill_rect(61, 161, 105, 26, 0);
fill_rect(166, 161, 112, 26, 0);
fill_rect(40, 53, 240, 24, 0);
fill_rect(40, 53, 160, 24, 0);
vTaskSuspend(NULL);
}
I had a similar problem. You overwrite the buffer with new data while it is still not finished drawing (DMA).
@kriegste thanks, indeed.
For what is worth this happens regardless of weather the buffer I pass in esp_lcd_panel_draw_bitmap is DMA allocated memory.
Internally the code, if you don't provide a DMA buffer but have setup the display with SPI_DMA_CH_AUTO, seems to copy the data over a DMA buffer anyway and then send that off to the display.
In any case there is some bug somewhere in the DMA handling in esp_lcd or spi master.
Try using a second buffer and alternate between them:
fill buffer 0 draw buffer 0 fill buffer 1 draw buffer 1 fill buffer 0 ...
@kriegste Unfortunately it still happens even if I alternate buffers with a flag or even if I allocate a new one for each drawing (both caps DMA and not) and free it after.
You overwrite the buffer with new data while it is still not finished drawing (DMA).
This explanation is correct. If you want to reuse the same draw buffer, please make sure it's old data has been send out by the DMA. You can register a "color trans done" callback by esp_lcd_panel_io_register_event_callbacks
.
Answers checklist.
IDF version.
v5.1.1
Espressif SoC revision.
ESP32-D0WDQ6-V3 (revision v3.0)
Operating System used.
Linux
How did you build your project?
Command line with idf.py
If you are using Windows, please specify command line type.
None
Development Kit.
ESP32 T-Display
Power Supply used.
USB
What is the expected behavior?
Expected behavior is the display shows is black with a green filled rectangle in the middle.
What is the actual behavior?
Actual behavior is that the display gets artifacts: in the last line drawn of the rectangle one or more pixels have not been painted - at the same those green pixels appear on the last row of the display where they were not intended to be.
If we add a
vTaskDelay(10 / portTICK_PERIOD_MS);
every so often of esp_lcd_panel_draw_bitmap then the artifact is not visible.Notice that only one task is running and that this occurs with and without mutex. Notable it occurs even if running in single core mode.
Steps to reproduce.
Debug Logs.
No response
More Information.
No response