Port to STM32F407 + ILI9325 not rendering properly on fast animations

Description

Everything is showing properly, but quick transitions fail to render and shows images dragged along the x axis. As an example, images of how the lv_demo_widgets looks are attached. You can see how the top tab bar, showing properly on the first run, fails to render after automatically sliding to next screen. Additionally, while the animation is scrolling the window up and down, I’m able to see some glitches on the body of the window that is scrolling, not being completely smooth.

What MCU/Processor/Board and compiler are you using?

STM32F407, STM32F4-DISCO, gcc, LVGL 7.1, and ILI9325 320x240 Display
Originally clock working at 168 MHz

What do you want to achieve?

Just displaying lv_demo_widgets on this MCU + Display

What have you tried so far?

-Slowing down clock speed
-Using different DMA configurations
-Increasing timings for FSCM operations
-Adding time delay after lv_disp_flush_ready
-Test different sizes for disp_drv.buffer between DISP_HOR_RES * 10 and DISP_HOR_RES * 120
-Using one and two buffers

No improvement was found during these changes. Even if refresh is strongly slowed down, the problem persists

Code to reproduce


/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define  DISP_HOR_RES 320
#define  DISP_VER_RES 240
#define  DISP_BUFFER_LINES 60
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */

static lv_disp_drv_t disp_drv;
static volatile uint16_t *my_fb = (volatile uint16_t *) 0x60020000;
static uint32_t x1_flush;
static uint32_t y1_flush;
static uint32_t x2_flush;
static uint32_t y2_fill;
static uint32_t y_fill_act;
static lv_color_t *buf_to_flush;
static lv_color_t disp_buf1[DISP_HOR_RES * DISP_BUFFER_LINES];
static lv_color_t disp_buf2[DISP_HOR_RES * DISP_BUFFER_LINES];
static lv_disp_buf_t buf;

int main(void) {
  /* USER CODE BEGIN 1 */
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */
  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */
  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_FSMC_Init();
  /* USER CODE BEGIN 2 */
  ili9325_Init();
  lv_init();

  lv_disp_buf_init(&buf, disp_buf1, disp_buf2, DISP_HOR_RES * DISP_BUFFER_LINES);
  lv_disp_drv_init(&disp_drv);

  disp_drv.buffer = &buf;
  disp_drv.hor_res = 320;
  disp_drv.ver_res = 240;
  disp_drv.flush_cb = my_flush_cb;
  lv_disp_drv_register(&disp_drv);

   lv_demo_widgets();

  /* USER CODE END 2 */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */

  while (1) {
    HAL_Delay(10);
    lv_task_handler();

    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

static void my_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) {

  /*Return if the area is out the screen*/
  if (area->x2 < 0) return;
  if (area->y2 < 0) return;
  if (area->x1 > DISP_HOR_RES - 1) return;
  if (area->y1 > DISP_VER_RES - 1) return;

  /*Truncate the area to the screen*/
  int32_t act_x1 = area->x1 < 0 ? 0 : area->x1;
  int32_t act_y1 = area->y1 < 0 ? 0 : area->y1;
  int32_t act_x2 = area->x2 > DISP_HOR_RES - 1 ? DISP_HOR_RES - 1 : area->x2;
  int32_t act_y2 = area->y2 > DISP_VER_RES - 1 ? DISP_VER_RES - 1 : area->y2;

  x1_flush = act_x1;
  y1_flush = act_y1;
  x2_flush = act_x2;
  y2_fill = act_y2;
  y_fill_act = act_y1;
  buf_to_flush = color_p;

  /* Set Cursor */
  ili9325_SetCursor(x1_flush, y1_flush);

  /* Prepare to write GRAM */
  LCD_IO_WriteReg(LCD_REG_34);

  /*##-7- Start the DMA transfer using the interrupt mode #*/
  /* Configure the source, destination and buffer size DMA fields and Start DMA Stream transfer */
  /* Enable All the DMA interrupts */
  HAL_StatusTypeDef err;

  err = HAL_DMA_Start_IT(&hdma_memtomem_dma2_stream0,
                         (uint32_t) buf_to_flush,
                         (uint32_t) my_fb,
                         (x2_flush - x1_flush + 1));

  if (err != HAL_OK) {
    while (1);    /*Halt on error*/
  }
}

void DMA_TransferComplete(DMA_HandleTypeDef *han) {

  y_fill_act++;

  if (y_fill_act > y2_fill) {
    lv_disp_flush_ready(&disp_drv);

  } else {

    buf_to_flush += x2_flush - x1_flush + 1;
    /*##-7- Start the DMA transfer using the interrupt mode ####################*/
    /* Configure the source, destination and buffer size DMA fields and Start DMA Stream transfer */
    /* Enable All the DMA interrupts */
    if (HAL_DMA_Start_IT(han, (uint32_t) buf_to_flush, (uint32_t) my_fb,
                         (x2_flush - x1_flush + 1)) != HAL_OK) {
      while (1);    /*Halt on error*/
    }
  }
}

Actual DMA configuration



      hdma_memtomem_dma2_stream0.Instance = DMA2_Stream0;
      hdma_memtomem_dma2_stream0.Init.Channel = DMA_CHANNEL_0;
      hdma_memtomem_dma2_stream0.Init.Direction = DMA_MEMORY_TO_MEMORY;
      hdma_memtomem_dma2_stream0.Init.PeriphInc = DMA_PINC_ENABLE;
      hdma_memtomem_dma2_stream0.Init.MemInc = DMA_MINC_ENABLE;
      hdma_memtomem_dma2_stream0.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
      hdma_memtomem_dma2_stream0.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
      hdma_memtomem_dma2_stream0.Init.Mode = DMA_NORMAL;
      hdma_memtomem_dma2_stream0.Init.Priority = DMA_PRIORITY_HIGH;
      hdma_memtomem_dma2_stream0.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
      hdma_memtomem_dma2_stream0.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
      hdma_memtomem_dma2_stream0.Init.MemBurst = DMA_MBURST_INC4;
      hdma_memtomem_dma2_stream0.Init.PeriphBurst = DMA_PBURST_INC4;
      if (HAL_DMA_Init(&hdma_memtomem_dma2_stream0) != HAL_OK)
      {
        Error_Handler();
      }

Actual FSCM timing configuration

     /* Timing for FSCM */
      Timing.AddressSetupTime = 2;
      Timing.AddressHoldTime = 7;
      Timing.DataSetupTime = 20;
      Timing.BusTurnAroundDuration = 0;
      Timing.CLKDivision = 16;
      Timing.DataLatency = 17;
      Timing.AccessMode = FSMC_ACCESS_MODE_A;
      /* ExtTiming */

Screenshot and/or video

First run

After first transition, see the tab bar (the rest of the image is blurry because its automatically scrolling up & down)

After second transition, see the tab bar (the rest of the image is blurry because its automatically scrolling up & down)

Hi,

I have 2 question:

  1. Where do you call lv_disp_refr_ready?
  2. Can you test it without DMA?

Hi @kisvegabor,

  1. Where do you call lv_disp_refr_ready ?

Did you mean lv_disp_flush_ready ? if so, it is called in the DMA_TransferComplete function.

  1. Can you test it without DMA?

Yes, without DMA it’s working. Slower, but it’s working. The flush_cb I’m using without DMA is this one:

    static void my_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) {
      int32_t x, y;
        for (y = area->y1; y <= area->y2; y++) {
          for (x = area->x1; x <= area->x2; x++) {
            ili9325_WritePixel(x, y, color_p->full);
            color_p++;
          }
        }
        lv_disp_flush_ready(disp_drv);
      return;
    }

Thanks

By the way, I’ve tried with the lv_demo_benchmark. Without DMA is working fine, but with DMA, this is how it looks:



Sorry, yes, I meant that. :slight_smile:

Maybe I’m missing something but where do you set the width of the “window” to write in `li9325?

I see that you use ili9325_SetCursor but where do you set width and height?

Maybe I’m missing something but where do you set the width of the “window” to write in `li9325?

After you pointing this out, you helped me realize that only refresh areas whose width is smaller than screen width fail to render. DMA is transferring image buffer line by line, but not shifting the x and y position accordingly in the GRAM. Thus, when an image 320 pixel wide is written, a 320 pixel line is sent to GRAM, and consequently following lines will fall in x = 0 of the next line, and the image will look right. But if image is shorter than the screen width, when next line is sent it will fall at the end of the previous line, and not below, and that’s what I’m seeing here.

Adding

   /* Set Cursor */
    ili9325_SetCursor(x1_flush, y_fill_act);
    /* Prepare to write GRAM */
    LCD_IO_WriteReg(LCD_REG_34);

in DMA_TransferComplete function fixed everything… Sorry for the newbie issue and thank you!

The whole fixed function is below:

void DMA_TransferComplete(DMA_HandleTypeDef *han) {

  y_fill_act++;

  if (y_fill_act > y2_fill) {
    lv_disp_flush_ready(&disp_drv);

  } else {
   /* Set Cursor */
    ili9325_SetCursor(x1_flush, y_fill_act);

    /* Prepare to write GRAM */
    LCD_IO_WriteReg(LCD_REG_34);

    buf_to_flush += x2_flush - x1_flush + 1;
    /*##-7- Start the DMA transfer using the interrupt mode ####################*/
    /* Configure the source, destination and buffer size DMA fields and Start DMA Stream transfer */
    /* Enable All the DMA interrupts */
    if (HAL_DMA_Start_IT(han, (uint32_t) buf_to_flush, (uint32_t) my_fb,
                         (x2_flush - x1_flush + 1)) != HAL_OK) {
      while (1);    /*Halt on error*/
    }
  }
}

Oh, I see.

Glad to hear the you finally made it work. :slight_smile: